mirror of
https://github.com/mlua-rs/mlua
synced 2026-06-08 16:05:43 +00:00
Compare commits
93 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 31efd0cf72 | |||
| 6b79e4cd76 | |||
| ee2da72685 | |||
| 6180a528a7 | |||
| 69ef08d4d2 | |||
| f7f04d5180 | |||
| e10623d658 | |||
| c3ab89ba66 | |||
| fa500639f0 | |||
| 3d2574a855 | |||
| 003935db47 | |||
| fbed171f90 | |||
| 298d48f708 | |||
| 4ff677f5fc | |||
| 5b920321dd | |||
| 6bb99783e9 | |||
| 547f82acc1 | |||
| 9ea98e3f1e | |||
| 0ad03bee98 | |||
| ba7c331984 | |||
| 53dea3166a | |||
| 129dec43ed | |||
| a7f58e57ec | |||
| e6871525c4 | |||
| c4956dbf49 | |||
| d0ea428e23 | |||
| 3e87d3ea9b | |||
| 0f8bde4b4e | |||
| b9c9dfbd03 | |||
| 71824297f6 | |||
| c0a8064228 | |||
| 84efb8bf95 | |||
| cf687a6ac6 | |||
| ea85edb242 | |||
| 788175e0d6 | |||
| 90ef25a6ee | |||
| ac72f6acae | |||
| 4444ac4ea3 | |||
| 5f950e30ec | |||
| 311aa5f6e1 | |||
| 739d5f5262 | |||
| 375028e13f | |||
| 46e949c184 | |||
| 62b53e218c | |||
| 0393406b9f | |||
| 272cfdb89b | |||
| 8e244a25ea | |||
| ef8b8e11ec | |||
| 2543414726 | |||
| ee112568d6 | |||
| 69d3ddec29 | |||
| 5b0d811c5a | |||
| 19536db976 | |||
| fc69551f87 | |||
| a68b0b6905 | |||
| 1ec1cc9922 | |||
| fd68b033f0 | |||
| c3817409a8 | |||
| 863d8092d6 | |||
| 24b6ff3c20 | |||
| bc36261f5c | |||
| bcb9a4d220 | |||
| 9caf3542d9 | |||
| cb45db05fa | |||
| d1cb2a9a96 | |||
| aa3f6ba46c | |||
| cc57bed4c8 | |||
| b5d38ab2e3 | |||
| cd4091f64d | |||
| 91e069a77e | |||
| cacd3dc70f | |||
| 6f6cda0099 | |||
| d51ce86142 | |||
| aa061bce6f | |||
| 1c6b6ad801 | |||
| 031854fa2a | |||
| 5fd96c7908 | |||
| 7a3f19b857 | |||
| 55a5d7ef10 | |||
| af31dbd180 | |||
| 9ae3cb0a7c | |||
| fc1c80c142 | |||
| ee7ced6334 | |||
| d8307d0e4c | |||
| bf9fcc5aca | |||
| 7ce6b97da9 | |||
| 4891a6ac10 | |||
| 4ef0d583fc | |||
| 30b0122f5d | |||
| c31c72076f | |||
| 3bfaee4ecc | |||
| 89b68e2a24 | |||
| cbf805f492 |
@@ -129,8 +129,8 @@ jobs:
|
||||
- name: Run compile tests (macos lua54)
|
||||
if: ${{ matrix.os == 'macos-latest' && matrix.lua == 'lua54' }}
|
||||
run: |
|
||||
TRYBUILD=overwrite cargo test --features "${{ matrix.lua }},vendored" -- --ignored
|
||||
TRYBUILD=overwrite cargo test --features "${{ matrix.lua }},vendored,async,send,serialize,macros" -- --ignored
|
||||
TRYBUILD=overwrite cargo test --features "${{ matrix.lua }},vendored" --tests -- --ignored
|
||||
TRYBUILD=overwrite cargo test --features "${{ matrix.lua }},vendored,async,send,serialize,macros" --tests -- --ignored
|
||||
shell: bash
|
||||
|
||||
test_with_sanitizer:
|
||||
|
||||
+45
-4
@@ -1,3 +1,44 @@
|
||||
## v0.10.5 (May 24th, 2025)
|
||||
|
||||
- mlua-sys is back to 0.6.x (Luau 0.663)
|
||||
- Reverted: Trigger abort when Luau userdata destructors are panic (requires new mlua-sys)
|
||||
- Reverted: Added large (52bit) integers support for Luau (breaking change)
|
||||
|
||||
## v0.10.4 (May 5th, 2025)
|
||||
|
||||
_yanked_ because of semver-breaking changes
|
||||
|
||||
- Luau updated to 0.672
|
||||
- New serde option `encode_empty_tables_as_array` to serialize empty tables as arrays
|
||||
- Added `WeakLua` and `Lua::weak()` to create weak references to Lua state
|
||||
- Trigger abort when Luau userdata destructors are panic (Luau GC does not support it)
|
||||
- Added `AnyUserData::type_id()` method to get the type id of the userdata
|
||||
- Added `Chunk::name()`, `Chunk::environment()` and `Chunk::mode()` functions
|
||||
- Support borrowing underlying wrapped types for `UserDataRef` and `UserDataRefMut` (under `userdata-wrappers` feature)
|
||||
- Added large (52bit) integers support for Luau
|
||||
- Enable `serde` for `bstr` if `serialize` feature flag is enabled
|
||||
- Recursive warnings (Lua 5.4) are no longer allowed
|
||||
- Implemented `IntoLua`/`FromLua` for `BorrowedString` and `BorrowedBytes`
|
||||
- Implemented `IntoLua`/`FromLua` for `char`
|
||||
- Enable `Thread::reset()` for all Lua versions (limited support for 5.1-5.3)
|
||||
- Bugfixes and improvements
|
||||
|
||||
## v0.10.3 (Jan 27th, 2025)
|
||||
|
||||
- Set `Default` for `Value` to be `Nil`
|
||||
- Allow exhaustive match on `Value` (#502)
|
||||
- Add `Table::set_safeenv` method (Luau)
|
||||
|
||||
## v0.10.2 (Dec 1st, 2024)
|
||||
|
||||
- Switch proc-macro-error to proc-macro-error2 (#493)
|
||||
- Do not allow Lua to run GC finalizers on ref thread (#491)
|
||||
- Fix chunks loading in Luau when memory limit is enforced (#488)
|
||||
- Added `String::wrap` method to wrap arbitrary `AsRef<[u8]>` into `impl IntoLua`
|
||||
- Better FreeBSD/OpenBSD support (thanks to cos)
|
||||
- Delay "any" userdata metatable creation until first instance is created (#482)
|
||||
- Reduce amount of generated code for `UserData` (less generics)
|
||||
|
||||
## v0.10.1 (Nov 9th, 2024)
|
||||
|
||||
- Minimal Luau updated to 0.650
|
||||
@@ -288,7 +329,7 @@ Other:
|
||||
|
||||
## v0.8.0
|
||||
Changes since 0.7.4
|
||||
- Roblox Luau support
|
||||
- Luau support
|
||||
- Removed C glue
|
||||
- Added async support to `__index` and `__newindex` metamethods
|
||||
- Added `Function::info()` to get information about functions (#149).
|
||||
@@ -338,7 +379,7 @@ Breaking changes:
|
||||
|
||||
## v0.8.0-beta.1
|
||||
|
||||
- Roblox Luau support
|
||||
- Luau support
|
||||
- Refactored ffi module. C glue is no longer required
|
||||
- Added async support to `__index` and `__newindex` metamethods
|
||||
|
||||
@@ -451,7 +492,7 @@ Breaking changes:
|
||||
- [**Breaking**] Removed `AnyUserData::has_metamethod()`
|
||||
- Added `Thread::reset()` for luajit/lua54 to recycle threads.
|
||||
It's possible to attach a new function to a thread (coroutine).
|
||||
- Added `chunk!` macro support to load chunks of Lua code using the Rust tokenizer and optinally capturing Rust variables.
|
||||
- Added `chunk!` macro support to load chunks of Lua code using the Rust tokenizer and optionally capturing Rust variables.
|
||||
- Improved error reporting (`Error`'s `__tostring` method formats full stacktraces). This is useful in the module mode.
|
||||
|
||||
## v0.6.0-beta.1
|
||||
@@ -507,7 +548,7 @@ Breaking changes:
|
||||
|
||||
- Lua 5.4 support with `MetaMethod::Close`.
|
||||
- `lua53` feature is disabled by default. Now preferred Lua version have to be chosen explicitly.
|
||||
- Provide safety guaraness for Lua state, which means that potenially unsafe operations, like loading C modules (using `require` or `package.loadlib`) are disabled. Equalient for the previous `Lua::new()` function is `Lua::unsafe_new()`.
|
||||
- Provide safety guarantees for Lua state, which means that potentially unsafe operations, like loading C modules (using `require` or `package.loadlib`) are disabled. Equivalent to the previous `Lua::new()` function is `Lua::unsafe_new()`.
|
||||
- New `send` feature to require `Send`.
|
||||
- New `module` feature, that disables linking to Lua Core Libraries. Required for modules.
|
||||
- Don't allow `'callback` outlive `'lua` in `Lua::create_function()` to fix [the unsoundness](tests/compile/static_callback_args.rs).
|
||||
|
||||
+9
-8
@@ -1,17 +1,17 @@
|
||||
[package]
|
||||
name = "mlua"
|
||||
version = "0.10.1" # remember to update mlua_derive
|
||||
version = "0.10.5" # remember to update mlua_derive
|
||||
authors = ["Aleksandr Orlenko <zxteam@pm.me>", "kyren <catherine@kyju.org>"]
|
||||
rust-version = "1.79.0"
|
||||
edition = "2021"
|
||||
repository = "https://github.com/khvzak/mlua"
|
||||
repository = "https://github.com/mlua-rs/mlua"
|
||||
documentation = "https://docs.rs/mlua"
|
||||
readme = "README.md"
|
||||
keywords = ["lua", "luajit", "luau", "async", "scripting"]
|
||||
categories = ["api-bindings", "asynchronous"]
|
||||
license = "MIT"
|
||||
description = """
|
||||
High level bindings to Lua 5.4/5.3/5.2/5.1 (including LuaJIT) and Roblox Luau
|
||||
High level bindings to Lua 5.4/5.3/5.2/5.1 (including LuaJIT) and Luau
|
||||
with async/await features and support of writing native Lua modules in Rust.
|
||||
"""
|
||||
|
||||
@@ -36,17 +36,17 @@ luau = ["ffi/luau", "dep:libloading"]
|
||||
luau-jit = ["luau", "ffi/luau-codegen"]
|
||||
luau-vector4 = ["luau", "ffi/luau-vector4"]
|
||||
vendored = ["ffi/vendored"]
|
||||
module = ["dep:mlua_derive", "ffi/module"]
|
||||
module = ["mlua_derive", "ffi/module"]
|
||||
async = ["dep:futures-util"]
|
||||
send = ["parking_lot/send_guard", "error-send"]
|
||||
error-send = []
|
||||
serialize = ["dep:serde", "dep:erased-serde", "dep:serde-value"]
|
||||
serialize = ["dep:serde", "dep:erased-serde", "dep:serde-value", "bstr/serde"]
|
||||
macros = ["mlua_derive/macros"]
|
||||
anyhow = ["dep:anyhow", "error-send"]
|
||||
userdata-wrappers = []
|
||||
|
||||
[dependencies]
|
||||
mlua_derive = { version = "=0.10.0", optional = true, path = "mlua_derive" }
|
||||
mlua_derive = { version = "=0.10.1", optional = true, path = "mlua_derive" }
|
||||
bstr = { version = "1.0", features = ["std"], default-features = false }
|
||||
either = "1.0"
|
||||
num-traits = { version = "0.2.14" }
|
||||
@@ -57,8 +57,9 @@ erased-serde = { version = "0.4", optional = true }
|
||||
serde-value = { version = "0.7", optional = true }
|
||||
parking_lot = { version = "0.12", features = ["arc_lock"] }
|
||||
anyhow = { version = "1.0", optional = true }
|
||||
rustversion = "1.0"
|
||||
|
||||
ffi = { package = "mlua-sys", version = "0.6.5", path = "mlua-sys" }
|
||||
ffi = { package = "mlua-sys", version = "0.6.8", path = "mlua-sys" }
|
||||
|
||||
[target.'cfg(unix)'.dependencies]
|
||||
libloading = { version = "0.8", optional = true }
|
||||
@@ -78,7 +79,7 @@ static_assertions = "1.0"
|
||||
|
||||
[target.'cfg(not(target_arch = "wasm32"))'.dev-dependencies]
|
||||
criterion = { version = "0.5", features = ["async_tokio"] }
|
||||
rustyline = "14.0"
|
||||
rustyline = "15.0"
|
||||
tokio = { version = "1.0", features = ["full"] }
|
||||
|
||||
[lints.rust]
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
# mlua
|
||||
[![Build Status]][github-actions] [![Latest Version]][crates.io] [![API Documentation]][docs.rs] [![Coverage Status]][codecov.io] ![MSRV]
|
||||
|
||||
[Build Status]: https://github.com/khvzak/mlua/workflows/CI/badge.svg
|
||||
[github-actions]: https://github.com/khvzak/mlua/actions
|
||||
[Build Status]: https://github.com/mlua-rs/mlua/workflows/CI/badge.svg
|
||||
[github-actions]: https://github.com/mlua-rs/mlua/actions
|
||||
[Latest Version]: https://img.shields.io/crates/v/mlua.svg
|
||||
[crates.io]: https://crates.io/crates/mlua
|
||||
[API Documentation]: https://docs.rs/mlua/badge.svg
|
||||
@@ -19,19 +19,19 @@
|
||||
|
||||
> **Note**
|
||||
>
|
||||
> See v0.10 [release notes](https://github.com/khvzak/mlua/blob/main/docs/release_notes/v0.10.md).
|
||||
> See v0.10 [release notes](https://github.com/mlua-rs/mlua/blob/main/docs/release_notes/v0.10.md).
|
||||
|
||||
`mlua` is bindings to [Lua](https://www.lua.org) programming language for Rust with a goal to provide
|
||||
_safe_ (as far as it's possible), high level, easy to use, practical and flexible API.
|
||||
|
||||
Started as `rlua` fork, `mlua` supports Lua 5.4, 5.3, 5.2, 5.1 (including LuaJIT) and [Roblox Luau] and allows to write native Lua modules in Rust as well as use Lua in a standalone mode.
|
||||
Started as `rlua` fork, `mlua` supports Lua 5.4, 5.3, 5.2, 5.1 (including LuaJIT) and [Luau] and allows to write native Lua modules in Rust as well as use Lua in a standalone mode.
|
||||
|
||||
`mlua` tested on Windows/macOS/Linux including module mode in [GitHub Actions] on `x86_64` platform and cross-compilation to `aarch64` (other targets are also supported).
|
||||
|
||||
WebAssembly (WASM) is supported through `wasm32-unknown-emscripten` target for all Lua versions excluding JIT.
|
||||
WebAssembly (WASM) is supported through `wasm32-unknown-emscripten` target for all Lua/Luau versions excluding JIT.
|
||||
|
||||
[GitHub Actions]: https://github.com/khvzak/mlua/actions
|
||||
[Roblox Luau]: https://luau.org
|
||||
[GitHub Actions]: https://github.com/mlua-rs/mlua/actions
|
||||
[Luau]: https://luau.org
|
||||
|
||||
## Usage
|
||||
|
||||
@@ -64,9 +64,9 @@ Below is a list of the available feature flags. By default `mlua` does not enabl
|
||||
[5.2]: https://www.lua.org/manual/5.2/manual.html
|
||||
[5.1]: https://www.lua.org/manual/5.1/manual.html
|
||||
[LuaJIT]: https://luajit.org/
|
||||
[Luau]: https://github.com/Roblox/luau
|
||||
[lua-src]: https://github.com/khvzak/lua-src-rs
|
||||
[luajit-src]: https://github.com/khvzak/luajit-src-rs
|
||||
[Luau]: https://github.com/luau-lang/luau
|
||||
[lua-src]: https://github.com/mlua-rs/lua-src-rs
|
||||
[luajit-src]: https://github.com/mlua-rs/luajit-src-rs
|
||||
[tokio]: https://github.com/tokio-rs/tokio
|
||||
[async-std]: https://github.com/async-rs/async-std
|
||||
[`Send`]: https://doc.rust-lang.org/std/marker/trait.Send.html
|
||||
@@ -133,7 +133,7 @@ Add to `Cargo.toml` :
|
||||
|
||||
``` toml
|
||||
[dependencies]
|
||||
mlua = { version = "0.10.1", features = ["lua54", "vendored"] }
|
||||
mlua = { version = "0.10", features = ["lua54", "vendored"] }
|
||||
```
|
||||
|
||||
`main.rs`
|
||||
@@ -168,7 +168,7 @@ Add to `Cargo.toml` :
|
||||
crate-type = ["cdylib"]
|
||||
|
||||
[dependencies]
|
||||
mlua = { version = "0.10.1", features = ["lua54", "module"] }
|
||||
mlua = { version = "0.10", features = ["lua54", "module"] }
|
||||
```
|
||||
|
||||
`lib.rs` :
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
The v0.10 version of mlua has goal to improve the user experience while keeping the same performance and safety guarantees.
|
||||
This document highlights the most notable features. For a full list of changes, see the [CHANGELOG].
|
||||
|
||||
[CHANGELOG]: https://github.com/khvzak/mlua/blob/main/CHANGELOG.md
|
||||
[CHANGELOG]: https://github.com/mlua-rs/mlua/blob/main/CHANGELOG.md
|
||||
|
||||
### New features
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
The v0.9 version of mlua is a major release that includes a number of API changes and improvements. This release is a stepping stone towards the v1.0.
|
||||
This document highlights the most important changes. For a full list of changes, see the [CHANGELOG].
|
||||
|
||||
[CHANGELOG]: https://github.com/khvzak/mlua/blob/main/CHANGELOG.md
|
||||
[CHANGELOG]: https://github.com/mlua-rs/mlua/blob/main/CHANGELOG.md
|
||||
|
||||
### New features
|
||||
|
||||
@@ -304,7 +304,7 @@ assert_eq!(f.call::<_, mlua::String>(())?, "hello");
|
||||
|
||||
The new mlua version has a number of performance improvements. Please check the [benchmarks results] to see how mlua compares to rlua and rhai.
|
||||
|
||||
[benchmarks results]: https://github.com/khvzak/script-bench-rs
|
||||
[benchmarks results]: https://github.com/mlua-rs/script-bench-rs
|
||||
|
||||
### Changes in `module` mode
|
||||
|
||||
|
||||
+4
-4
@@ -1,10 +1,10 @@
|
||||
[package]
|
||||
name = "mlua-sys"
|
||||
version = "0.6.5"
|
||||
version = "0.6.8"
|
||||
authors = ["Aleksandr Orlenko <zxteam@pm.me>"]
|
||||
rust-version = "1.71"
|
||||
edition = "2021"
|
||||
repository = "https://github.com/khvzak/mlua"
|
||||
repository = "https://github.com/mlua-rs/mlua"
|
||||
documentation = "https://docs.rs/mlua-sys"
|
||||
readme = "README.md"
|
||||
categories = ["external-ffi-bindings"]
|
||||
@@ -12,7 +12,7 @@ license = "MIT"
|
||||
links = "lua"
|
||||
build = "build/main.rs"
|
||||
description = """
|
||||
Low level (FFI) bindings to Lua 5.4/5.3/5.2/5.1 (including LuaJIT) and Roblox Luau
|
||||
Low level (FFI) bindings to Lua 5.4/5.3/5.2/5.1 (including LuaJIT) and Luau
|
||||
"""
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
@@ -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.1", optional = true }
|
||||
luau0-src = { version = "0.12.0", optional = true }
|
||||
|
||||
[lints.rust]
|
||||
unexpected_cfgs = { level = "allow", check-cfg = ['cfg(raw_dylib)'] }
|
||||
|
||||
+2
-2
@@ -1,8 +1,8 @@
|
||||
# mlua-sys
|
||||
|
||||
Low level (FFI) bindings to Lua 5.4/5.3/5.2/5.1 (including LuaJIT) and Roblox [Luau].
|
||||
Low level (FFI) bindings to Lua 5.4/5.3/5.2/5.1 (including LuaJIT) and [Luau].
|
||||
|
||||
Intended to be consumed by the [mlua] crate.
|
||||
|
||||
[Luau]: https://github.com/Roblox/luau
|
||||
[Luau]: https://github.com/luau-lang/luau
|
||||
[mlua]: https://crates.io/crates/mlua
|
||||
|
||||
@@ -32,15 +32,19 @@ pub fn probe_lua() {
|
||||
// Find using `pkg-config`
|
||||
|
||||
#[cfg(feature = "lua54")]
|
||||
let (incl_bound, excl_bound, alt_probe, ver) = ("5.4", "5.5", Some("lua5.4"), "5.4");
|
||||
let (incl_bound, excl_bound, alt_probe, ver) =
|
||||
("5.4", "5.5", ["lua5.4", "lua-5.4", "lua54"], "5.4");
|
||||
#[cfg(feature = "lua53")]
|
||||
let (incl_bound, excl_bound, alt_probe, ver) = ("5.3", "5.4", Some("lua5.3"), "5.3");
|
||||
let (incl_bound, excl_bound, alt_probe, ver) =
|
||||
("5.3", "5.4", ["lua5.3", "lua-5.3", "lua53"], "5.3");
|
||||
#[cfg(feature = "lua52")]
|
||||
let (incl_bound, excl_bound, alt_probe, ver) = ("5.2", "5.3", Some("lua5.2"), "5.2");
|
||||
let (incl_bound, excl_bound, alt_probe, ver) =
|
||||
("5.2", "5.3", ["lua5.2", "lua-5.2", "lua52"], "5.2");
|
||||
#[cfg(feature = "lua51")]
|
||||
let (incl_bound, excl_bound, alt_probe, ver) = ("5.1", "5.2", Some("lua5.1"), "5.1");
|
||||
let (incl_bound, excl_bound, alt_probe, ver) =
|
||||
("5.1", "5.2", ["lua5.1", "lua-5.1", "lua51"], "5.1");
|
||||
#[cfg(feature = "luajit")]
|
||||
let (incl_bound, excl_bound, alt_probe, ver) = ("2.0.4", "2.2", None, "JIT");
|
||||
let (incl_bound, excl_bound, alt_probe, ver) = ("2.0.4", "2.2", [], "JIT");
|
||||
|
||||
#[rustfmt::skip]
|
||||
let mut lua = pkg_config::Config::new()
|
||||
@@ -48,10 +52,16 @@ pub fn probe_lua() {
|
||||
.cargo_metadata(true)
|
||||
.probe(if cfg!(feature = "luajit") { "luajit" } else { "lua" });
|
||||
|
||||
if lua.is_err() && alt_probe.is_some() {
|
||||
lua = pkg_config::Config::new()
|
||||
.cargo_metadata(true)
|
||||
.probe(alt_probe.unwrap());
|
||||
if lua.is_err() {
|
||||
for pkg in alt_probe {
|
||||
lua = pkg_config::Config::new()
|
||||
.cargo_metadata(true)
|
||||
.probe(pkg);
|
||||
|
||||
if lua.is_ok() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
lua.unwrap_or_else(|err| panic!("cannot find Lua{ver} using `pkg-config`: {err}"));
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
//! Low level bindings to Lua 5.4/5.3/5.2/5.1 (including LuaJIT) and Roblox Luau.
|
||||
//! Low level bindings to Lua 5.4/5.3/5.2/5.1 (including LuaJIT) and Luau.
|
||||
|
||||
#![allow(non_camel_case_types, non_snake_case, dead_code)]
|
||||
#![allow(clippy::missing_safety_doc)]
|
||||
|
||||
@@ -548,7 +548,7 @@ pub unsafe fn luaL_getsubtable(L: *mut lua_State, idx: c_int, fname: *const c_ch
|
||||
|
||||
pub unsafe fn luaL_requiref(L: *mut lua_State, modname: *const c_char, openf: lua_CFunction, glb: c_int) {
|
||||
luaL_checkstack(L, 3, cstr!("not enough stack slots available"));
|
||||
luaL_getsubtable(L, LUA_REGISTRYINDEX, cstr!("_LOADED"));
|
||||
luaL_getsubtable(L, LUA_REGISTRYINDEX, LUA_LOADED_TABLE);
|
||||
if lua_getfield(L, -1, modname) == LUA_TNIL {
|
||||
lua_pop(L, 1);
|
||||
lua_pushcfunction(L, openf);
|
||||
|
||||
@@ -8,6 +8,9 @@ use super::lua::{self, lua_CFunction, lua_Integer, lua_Number, lua_State};
|
||||
// Extra error code for 'luaL_load'
|
||||
pub const LUA_ERRFILE: c_int = lua::LUA_ERRERR + 1;
|
||||
|
||||
// Key, in the registry, for table of loaded modules
|
||||
pub const LUA_LOADED_TABLE: *const c_char = cstr!("_LOADED");
|
||||
|
||||
#[repr(C)]
|
||||
pub struct luaL_Reg {
|
||||
pub name: *const c_char,
|
||||
|
||||
@@ -232,7 +232,7 @@ pub unsafe fn luaL_tolstring(L: *mut lua_State, mut idx: c_int, len: *mut usize)
|
||||
|
||||
pub unsafe fn luaL_requiref(L: *mut lua_State, modname: *const c_char, openf: lua_CFunction, glb: c_int) {
|
||||
luaL_checkstack(L, 3, cstr!("not enough stack slots available"));
|
||||
luaL_getsubtable(L, LUA_REGISTRYINDEX, cstr!("_LOADED"));
|
||||
luaL_getsubtable(L, LUA_REGISTRYINDEX, LUA_LOADED_TABLE);
|
||||
if lua_getfield(L, -1, modname) == LUA_TNIL {
|
||||
lua_pop(L, 1);
|
||||
lua_pushcfunction(L, openf);
|
||||
|
||||
@@ -8,6 +8,12 @@ use super::lua::{self, lua_CFunction, lua_Integer, lua_Number, lua_State, lua_Un
|
||||
// Extra error code for 'luaL_load'
|
||||
pub const LUA_ERRFILE: c_int = lua::LUA_ERRERR + 1;
|
||||
|
||||
// Key, in the registry, for table of loaded modules
|
||||
pub const LUA_LOADED_TABLE: *const c_char = cstr!("_LOADED");
|
||||
|
||||
// Key, in the registry, for table of preloaded loaders
|
||||
pub const LUA_PRELOAD_TABLE: *const c_char = cstr!("_PRELOAD");
|
||||
|
||||
#[repr(C)]
|
||||
pub struct luaL_Reg {
|
||||
pub name: *const c_char,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//! MLua compatibility layer for Roblox Luau.
|
||||
//! MLua compatibility layer for Luau.
|
||||
//!
|
||||
//! Based on github.com/keplerproject/lua-compat-5.3
|
||||
|
||||
@@ -326,12 +326,16 @@ pub unsafe fn luaL_loadbufferenv(
|
||||
mut size: usize,
|
||||
name: *const c_char,
|
||||
mode: *const c_char,
|
||||
env: c_int,
|
||||
mut env: c_int,
|
||||
) -> c_int {
|
||||
extern "C" {
|
||||
fn free(p: *mut c_void);
|
||||
}
|
||||
|
||||
unsafe extern "C-unwind" fn data_dtor(data: *mut c_void) {
|
||||
free(*(data as *mut *mut c_char) as *mut c_void);
|
||||
}
|
||||
|
||||
let chunk_is_text = size == 0 || (*data as u8) >= b'\t';
|
||||
if !mode.is_null() {
|
||||
let modeb = CStr::from_ptr(mode).to_bytes();
|
||||
@@ -345,9 +349,16 @@ pub unsafe fn luaL_loadbufferenv(
|
||||
}
|
||||
|
||||
if chunk_is_text {
|
||||
if env < 0 {
|
||||
env -= 1;
|
||||
}
|
||||
let data_ud = lua_newuserdatadtor(L, mem::size_of::<*mut c_char>(), data_dtor) as *mut *mut c_char;
|
||||
let data = luau_compile_(data, size, ptr::null_mut(), &mut size);
|
||||
ptr::write(data_ud, data);
|
||||
// By deferring the `free(data)` to the userdata destructor, we ensure that
|
||||
// even if `luau_load` throws an error, the `data` is still released.
|
||||
let ok = luau_load(L, name, data, size, env) == 0;
|
||||
free(data as *mut c_void);
|
||||
lua_replace(L, -2); // replace data with the result
|
||||
if !ok {
|
||||
return LUA_ERRSYNTAX;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
//! Contains definitions from `luacode.h`.
|
||||
|
||||
use std::marker::{PhantomData, PhantomPinned};
|
||||
use std::os::raw::{c_char, c_int, c_void};
|
||||
use std::{ptr, slice};
|
||||
|
||||
@@ -15,6 +16,10 @@ pub struct lua_CompileOptions {
|
||||
pub vectorType: *const c_char,
|
||||
pub mutableGlobals: *const *const c_char,
|
||||
pub userdataTypes: *const *const c_char,
|
||||
pub librariesWithKnownMembers: *const *const c_char,
|
||||
pub libraryMemberTypeCallback: Option<lua_LibraryMemberTypeCallback>,
|
||||
pub libraryMemberConstantCallback: Option<lua_LibraryMemberConstantCallback>,
|
||||
pub disabledBuiltins: *const *const c_char,
|
||||
}
|
||||
|
||||
impl Default for lua_CompileOptions {
|
||||
@@ -29,10 +34,56 @@ impl Default for lua_CompileOptions {
|
||||
vectorType: ptr::null(),
|
||||
mutableGlobals: ptr::null(),
|
||||
userdataTypes: ptr::null(),
|
||||
librariesWithKnownMembers: ptr::null(),
|
||||
libraryMemberTypeCallback: None,
|
||||
libraryMemberConstantCallback: None,
|
||||
disabledBuiltins: ptr::null(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct lua_CompileConstant {
|
||||
_data: [u8; 0],
|
||||
_marker: PhantomData<(*mut u8, PhantomPinned)>,
|
||||
}
|
||||
|
||||
/// Type table tags
|
||||
#[doc(hidden)]
|
||||
#[repr(i32)]
|
||||
#[non_exhaustive]
|
||||
pub enum luau_BytecodeType {
|
||||
Nil = 0,
|
||||
Boolean,
|
||||
Number,
|
||||
String,
|
||||
Table,
|
||||
Function,
|
||||
Thread,
|
||||
UserData,
|
||||
Vector,
|
||||
Buffer,
|
||||
|
||||
Any = 15,
|
||||
}
|
||||
|
||||
pub type lua_LibraryMemberTypeCallback =
|
||||
unsafe extern "C-unwind" fn(library: *const c_char, member: *const c_char) -> c_int;
|
||||
|
||||
pub type lua_LibraryMemberConstantCallback = unsafe extern "C-unwind" fn(
|
||||
library: *const c_char,
|
||||
member: *const c_char,
|
||||
constant: *mut lua_CompileConstant,
|
||||
);
|
||||
|
||||
extern "C" {
|
||||
pub fn luau_set_compile_constant_nil(cons: *mut lua_CompileConstant);
|
||||
pub fn luau_set_compile_constant_boolean(cons: *mut lua_CompileConstant, b: c_int);
|
||||
pub fn luau_set_compile_constant_number(cons: *mut lua_CompileConstant, n: f64);
|
||||
pub fn luau_set_compile_constant_vector(cons: *mut lua_CompileConstant, x: f32, y: f32, z: f32, w: f32);
|
||||
pub fn luau_set_compile_constant_string(cons: *mut lua_CompileConstant, s: *const c_char, l: usize);
|
||||
}
|
||||
|
||||
extern "C-unwind" {
|
||||
#[link_name = "luau_compile"]
|
||||
pub fn luau_compile_(
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
[package]
|
||||
name = "mlua_derive"
|
||||
version = "0.10.0"
|
||||
version = "0.10.1"
|
||||
authors = ["Aleksandr Orlenko <zxteam@pm.me>"]
|
||||
edition = "2021"
|
||||
description = "Procedural macros for the mlua crate."
|
||||
repository = "https://github.com/khvzak/mlua"
|
||||
repository = "https://github.com/mlua-rs/mlua"
|
||||
keywords = ["lua", "mlua"]
|
||||
license = "MIT"
|
||||
|
||||
@@ -12,13 +12,13 @@ license = "MIT"
|
||||
proc-macro = true
|
||||
|
||||
[features]
|
||||
macros = ["proc-macro-error", "itertools", "regex", "once_cell"]
|
||||
macros = ["proc-macro-error2", "itertools", "regex", "once_cell"]
|
||||
|
||||
[dependencies]
|
||||
quote = "1.0"
|
||||
proc-macro2 = { version = "1.0", features = ["span-locations"] }
|
||||
proc-macro-error = { version = "1.0", optional = true }
|
||||
proc-macro-error2 = { version = "2.0.1", optional = true }
|
||||
syn = { version = "2.0", features = ["full"] }
|
||||
itertools = { version = "0.13", optional = true }
|
||||
itertools = { version = "0.14", optional = true }
|
||||
regex = { version = "1.4", optional = true }
|
||||
once_cell = { version = "1.0", optional = true }
|
||||
|
||||
@@ -7,7 +7,7 @@ use syn::{parse_macro_input, ItemFn, LitStr, Result};
|
||||
#[cfg(feature = "macros")]
|
||||
use {
|
||||
crate::chunk::Chunk, proc_macro::TokenTree, proc_macro2::TokenStream as TokenStream2,
|
||||
proc_macro_error::proc_macro_error,
|
||||
proc_macro_error2::proc_macro_error,
|
||||
};
|
||||
|
||||
#[derive(Default)]
|
||||
|
||||
@@ -74,7 +74,7 @@ fn parse_pos(span: &Span) -> Option<(usize, usize)> {
|
||||
fn fallback_span_pos(span: &Span) -> (Pos, Pos) {
|
||||
let (start, end) = match parse_pos(span) {
|
||||
Some(v) => v,
|
||||
None => proc_macro_error::abort_call_site!("Cannot retrieve span information; please use nightly"),
|
||||
None => proc_macro_error2::abort_call_site!("Cannot retrieve span information; please use nightly"),
|
||||
};
|
||||
(Pos::new(1, start), Pos::new(1, end))
|
||||
}
|
||||
|
||||
+56
-1
@@ -2,6 +2,8 @@ use std::borrow::Cow;
|
||||
use std::collections::HashMap;
|
||||
use std::ffi::CString;
|
||||
use std::io::Result as IoResult;
|
||||
use std::marker::PhantomData;
|
||||
use std::panic::Location;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::string::String as StdString;
|
||||
|
||||
@@ -9,13 +11,16 @@ use crate::error::{Error, Result};
|
||||
use crate::function::Function;
|
||||
use crate::state::{Lua, WeakLua};
|
||||
use crate::table::Table;
|
||||
use crate::traits::{FromLuaMulti, IntoLuaMulti};
|
||||
use crate::traits::{FromLuaMulti, IntoLua, IntoLuaMulti};
|
||||
use crate::value::Value;
|
||||
|
||||
/// Trait for types [loadable by Lua] and convertible to a [`Chunk`]
|
||||
///
|
||||
/// [loadable by Lua]: https://www.lua.org/manual/5.4/manual.html#3.3.2
|
||||
pub trait AsChunk<'a> {
|
||||
/// Returns optional chunk name
|
||||
///
|
||||
/// See [`Chunk::set_name`] for possible name prefixes.
|
||||
fn name(&self) -> Option<StdString> {
|
||||
None
|
||||
}
|
||||
@@ -302,12 +307,27 @@ impl Compiler {
|
||||
}
|
||||
|
||||
impl Chunk<'_> {
|
||||
/// Returns the name of this chunk.
|
||||
pub fn name(&self) -> &str {
|
||||
&self.name
|
||||
}
|
||||
|
||||
/// Sets the name of this chunk, which results in more informative error traces.
|
||||
///
|
||||
/// Possible name prefixes:
|
||||
/// - `@` - file path (when truncation is needed, the end of the file path is kept, as this is
|
||||
/// more useful for identifying the file)
|
||||
/// - `=` - custom chunk name (when truncation is needed, the beginning of the name is kept)
|
||||
pub fn set_name(mut self, name: impl Into<String>) -> Self {
|
||||
self.name = name.into();
|
||||
self
|
||||
}
|
||||
|
||||
/// Returns the environment of this chunk.
|
||||
pub fn environment(&self) -> Option<&Table> {
|
||||
self.env.as_ref().ok()?.as_ref()
|
||||
}
|
||||
|
||||
/// Sets the environment of the loaded chunk to the given value.
|
||||
///
|
||||
/// In Lua >=5.2 main chunks always have exactly one upvalue, and this upvalue is used as the
|
||||
@@ -324,6 +344,11 @@ impl Chunk<'_> {
|
||||
self
|
||||
}
|
||||
|
||||
/// Returns the mode (auto-detected by default) of this chunk.
|
||||
pub fn mode(&self) -> ChunkMode {
|
||||
self.detect_mode()
|
||||
}
|
||||
|
||||
/// Sets whether the chunk is text or binary (autodetected by default).
|
||||
///
|
||||
/// Be aware, Lua does not check the consistency of the code inside binary chunks.
|
||||
@@ -561,3 +586,33 @@ impl Chunk<'_> {
|
||||
buf
|
||||
}
|
||||
}
|
||||
|
||||
struct WrappedChunk<'a, T: AsChunk<'a>> {
|
||||
chunk: T,
|
||||
caller: &'static Location<'static>,
|
||||
_marker: PhantomData<&'a T>,
|
||||
}
|
||||
|
||||
impl<'a> Chunk<'a> {
|
||||
/// Wraps a chunk of Lua code, returning an opaque type that implements [`IntoLua`] trait.
|
||||
///
|
||||
/// The resulted `IntoLua` implementation will convert the chunk into a Lua function without
|
||||
/// executing it.
|
||||
#[doc(hidden)]
|
||||
#[track_caller]
|
||||
pub fn wrap(chunk: impl AsChunk<'a> + 'a) -> impl IntoLua + 'a {
|
||||
WrappedChunk {
|
||||
chunk,
|
||||
caller: Location::caller(),
|
||||
_marker: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: AsChunk<'a>> IntoLua for WrappedChunk<'a, T> {
|
||||
fn into_lua(self, lua: &Lua) -> Result<Value> {
|
||||
lua.load_with_location(self.chunk, self.caller)
|
||||
.into_function()
|
||||
.map(Value::Function)
|
||||
}
|
||||
}
|
||||
|
||||
+135
-2
@@ -5,7 +5,7 @@ use std::hash::{BuildHasher, Hash};
|
||||
use std::os::raw::c_int;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::string::String as StdString;
|
||||
use std::{slice, str};
|
||||
use std::{mem, slice, str};
|
||||
|
||||
use bstr::{BStr, BString, ByteSlice, ByteVec};
|
||||
use num_traits::cast;
|
||||
@@ -13,7 +13,7 @@ use num_traits::cast;
|
||||
use crate::error::{Error, Result};
|
||||
use crate::function::Function;
|
||||
use crate::state::{Lua, RawLua};
|
||||
use crate::string::String;
|
||||
use crate::string::{BorrowedBytes, BorrowedStr, String};
|
||||
use crate::table::Table;
|
||||
use crate::thread::Thread;
|
||||
use crate::traits::{FromLua, IntoLua, ShortTypeName as _};
|
||||
@@ -91,6 +91,94 @@ impl FromLua for String {
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoLua for BorrowedStr<'_> {
|
||||
#[inline]
|
||||
fn into_lua(self, _: &Lua) -> Result<Value> {
|
||||
Ok(Value::String(self.borrow.into_owned()))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn push_into_stack(self, lua: &RawLua) -> Result<()> {
|
||||
lua.push_ref(&self.borrow.0);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoLua for &BorrowedStr<'_> {
|
||||
#[inline]
|
||||
fn into_lua(self, _: &Lua) -> Result<Value> {
|
||||
Ok(Value::String(self.borrow.clone().into_owned()))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn push_into_stack(self, lua: &RawLua) -> Result<()> {
|
||||
lua.push_ref(&self.borrow.0);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl FromLua for BorrowedStr<'_> {
|
||||
fn from_lua(value: Value, lua: &Lua) -> Result<Self> {
|
||||
let s = String::from_lua(value, lua)?;
|
||||
let BorrowedStr { buf, _lua, .. } = BorrowedStr::try_from(&s)?;
|
||||
let buf = unsafe { mem::transmute::<&str, &'static str>(buf) };
|
||||
let borrow = Cow::Owned(s);
|
||||
Ok(Self { buf, borrow, _lua })
|
||||
}
|
||||
|
||||
unsafe fn from_stack(idx: c_int, lua: &RawLua) -> Result<Self> {
|
||||
let s = String::from_stack(idx, lua)?;
|
||||
let BorrowedStr { buf, _lua, .. } = BorrowedStr::try_from(&s)?;
|
||||
let buf = unsafe { mem::transmute::<&str, &'static str>(buf) };
|
||||
let borrow = Cow::Owned(s);
|
||||
Ok(Self { buf, borrow, _lua })
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoLua for BorrowedBytes<'_> {
|
||||
#[inline]
|
||||
fn into_lua(self, _: &Lua) -> Result<Value> {
|
||||
Ok(Value::String(self.borrow.into_owned()))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn push_into_stack(self, lua: &RawLua) -> Result<()> {
|
||||
lua.push_ref(&self.borrow.0);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoLua for &BorrowedBytes<'_> {
|
||||
#[inline]
|
||||
fn into_lua(self, _: &Lua) -> Result<Value> {
|
||||
Ok(Value::String(self.borrow.clone().into_owned()))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn push_into_stack(self, lua: &RawLua) -> Result<()> {
|
||||
lua.push_ref(&self.borrow.0);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl FromLua for BorrowedBytes<'_> {
|
||||
fn from_lua(value: Value, lua: &Lua) -> Result<Self> {
|
||||
let s = String::from_lua(value, lua)?;
|
||||
let BorrowedBytes { buf, _lua, .. } = BorrowedBytes::from(&s);
|
||||
let buf = unsafe { mem::transmute::<&[u8], &'static [u8]>(buf) };
|
||||
let borrow = Cow::Owned(s);
|
||||
Ok(Self { buf, borrow, _lua })
|
||||
}
|
||||
|
||||
unsafe fn from_stack(idx: c_int, lua: &RawLua) -> Result<Self> {
|
||||
let s = String::from_stack(idx, lua)?;
|
||||
let BorrowedBytes { buf, _lua, .. } = BorrowedBytes::from(&s);
|
||||
let buf = unsafe { mem::transmute::<&[u8], &'static [u8]>(buf) };
|
||||
let borrow = Cow::Owned(s);
|
||||
Ok(Self { buf, borrow, _lua })
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoLua for Table {
|
||||
#[inline]
|
||||
fn into_lua(self, _: &Lua) -> Result<Value> {
|
||||
@@ -655,6 +743,51 @@ impl IntoLua for &Path {
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoLua for char {
|
||||
#[inline]
|
||||
fn into_lua(self, lua: &Lua) -> Result<Value> {
|
||||
let mut char_bytes = [0; 4];
|
||||
self.encode_utf8(&mut char_bytes);
|
||||
Ok(Value::String(lua.create_string(&char_bytes[..self.len_utf8()])?))
|
||||
}
|
||||
}
|
||||
|
||||
impl FromLua for char {
|
||||
fn from_lua(value: Value, _lua: &Lua) -> Result<Self> {
|
||||
let ty = value.type_name();
|
||||
match value {
|
||||
Value::Integer(i) => {
|
||||
cast(i)
|
||||
.and_then(char::from_u32)
|
||||
.ok_or_else(|| Error::FromLuaConversionError {
|
||||
from: ty,
|
||||
to: "char".to_string(),
|
||||
message: Some("integer out of range when converting to char".to_string()),
|
||||
})
|
||||
}
|
||||
Value::String(s) => {
|
||||
let str = s.to_str()?;
|
||||
let mut str_iter = str.chars();
|
||||
match (str_iter.next(), str_iter.next()) {
|
||||
(Some(char), None) => Ok(char),
|
||||
_ => Err(Error::FromLuaConversionError {
|
||||
from: ty,
|
||||
to: "char".to_string(),
|
||||
message: Some(
|
||||
"expected string to have exactly one char when converting to char".to_string(),
|
||||
),
|
||||
}),
|
||||
}
|
||||
}
|
||||
_ => Err(Error::FromLuaConversionError {
|
||||
from: ty,
|
||||
to: Self::type_name(),
|
||||
message: Some("expected string or integer".to_string()),
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn push_bytes_into_stack<T>(this: T, lua: &RawLua) -> Result<()>
|
||||
where
|
||||
|
||||
+3
-3
@@ -146,7 +146,7 @@ impl Function {
|
||||
/// Ok(())
|
||||
/// })?;
|
||||
///
|
||||
/// sleep.call_async(10).await?;
|
||||
/// sleep.call_async::<()>(10).await?;
|
||||
///
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
@@ -509,10 +509,10 @@ impl Function {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct WrappedFunction(pub(crate) Callback);
|
||||
struct WrappedFunction(pub(crate) Callback);
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
pub(crate) struct WrappedAsyncFunction(pub(crate) AsyncCallback);
|
||||
struct WrappedAsyncFunction(pub(crate) AsyncCallback);
|
||||
|
||||
impl Function {
|
||||
/// Wraps a Rust function or closure, returning an opaque type that implements [`IntoLua`]
|
||||
|
||||
+4
-3
@@ -66,6 +66,7 @@
|
||||
// warnings at all.
|
||||
#![cfg_attr(docsrs, feature(doc_cfg))]
|
||||
#![cfg_attr(not(send), allow(clippy::arc_with_non_send_sync))]
|
||||
#![allow(clippy::ptr_eq)]
|
||||
|
||||
#[macro_use]
|
||||
mod macros;
|
||||
@@ -104,7 +105,7 @@ pub use crate::function::{Function, FunctionInfo};
|
||||
pub use crate::hook::{Debug, DebugEvent, DebugNames, DebugSource, DebugStack};
|
||||
pub use crate::multi::{MultiValue, Variadic};
|
||||
pub use crate::scope::Scope;
|
||||
pub use crate::state::{GCMode, Lua, LuaOptions};
|
||||
pub use crate::state::{GCMode, Lua, LuaOptions, WeakLua};
|
||||
pub use crate::stdlib::StdLib;
|
||||
pub use crate::string::{BorrowedBytes, BorrowedStr, String};
|
||||
pub use crate::table::{Table, TablePairs, TableSequence};
|
||||
@@ -209,7 +210,7 @@ pub use mlua_derive::FromLua;
|
||||
///
|
||||
/// You can register multiple entrypoints as required.
|
||||
///
|
||||
/// ```
|
||||
/// ```ignore
|
||||
/// use mlua::{Lua, Result, Table};
|
||||
///
|
||||
/// #[mlua::lua_module]
|
||||
@@ -246,7 +247,7 @@ pub use mlua_derive::FromLua;
|
||||
/// ...
|
||||
/// }
|
||||
/// ```
|
||||
#[cfg(any(feature = "module", docsrs))]
|
||||
#[cfg(all(feature = "mlua_derive", any(feature = "module", doc)))]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "module")))]
|
||||
pub use mlua_derive::lua_module;
|
||||
|
||||
|
||||
+3
-3
@@ -20,7 +20,7 @@ use {libloading::Library, rustc_hash::FxHashMap};
|
||||
//
|
||||
|
||||
#[cfg(unix)]
|
||||
const TARGET_MLUA_LUAU_ABI_VERSION: u32 = 2;
|
||||
const TARGET_MLUA_LUAU_ABI_VERSION: u32 = 3;
|
||||
|
||||
#[cfg(all(unix, feature = "module"))]
|
||||
#[no_mangle]
|
||||
@@ -130,10 +130,10 @@ unsafe extern "C-unwind" fn lua_require(state: *mut ffi::lua_State) -> c_int {
|
||||
for i in 1.. {
|
||||
if ffi::lua_rawgeti(state, -1, i) == ffi::LUA_TNIL {
|
||||
// no more loaders?
|
||||
if (*err_buf).is_empty() {
|
||||
if (&*err_buf).is_empty() {
|
||||
ffi::luaL_error(state, cstr!("module '%s' not found"), name);
|
||||
} else {
|
||||
let bytes = (*err_buf).as_bytes();
|
||||
let bytes = (&*err_buf).as_bytes();
|
||||
let extra = ffi::lua_pushlstring(state, bytes.as_ptr() as *const _, bytes.len());
|
||||
ffi::luaL_error(state, cstr!("module '%s' not found:%s"), name, extra);
|
||||
}
|
||||
|
||||
+22
-5
@@ -18,15 +18,32 @@ pub(crate) struct MemoryState {
|
||||
}
|
||||
|
||||
impl MemoryState {
|
||||
#[cfg(feature = "luau")]
|
||||
#[inline]
|
||||
pub(crate) unsafe fn get(state: *mut ffi::lua_State) -> *mut Self {
|
||||
let mut mem_state = ptr::null_mut();
|
||||
#[cfg(feature = "luau")]
|
||||
{
|
||||
ffi::lua_getallocf(state, &mut mem_state);
|
||||
mlua_assert!(!mem_state.is_null(), "Luau state has no allocator userdata");
|
||||
ffi::lua_getallocf(state, &mut mem_state);
|
||||
mlua_assert!(!mem_state.is_null(), "Luau state has no allocator userdata");
|
||||
mem_state as *mut MemoryState
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "luau"))]
|
||||
#[rustversion::since(1.85)]
|
||||
#[inline]
|
||||
#[allow(clippy::incompatible_msrv)]
|
||||
pub(crate) unsafe fn get(state: *mut ffi::lua_State) -> *mut Self {
|
||||
let mut mem_state = ptr::null_mut();
|
||||
if !ptr::fn_addr_eq(ffi::lua_getallocf(state, &mut mem_state), ALLOCATOR) {
|
||||
mem_state = ptr::null_mut();
|
||||
}
|
||||
#[cfg(not(feature = "luau"))]
|
||||
mem_state as *mut MemoryState
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "luau"))]
|
||||
#[rustversion::before(1.85)]
|
||||
#[inline]
|
||||
pub(crate) unsafe fn get(state: *mut ffi::lua_State) -> *mut Self {
|
||||
let mut mem_state = ptr::null_mut();
|
||||
if ffi::lua_getallocf(state, &mut mem_state) != ALLOCATOR {
|
||||
mem_state = ptr::null_mut();
|
||||
}
|
||||
|
||||
+3
-3
@@ -126,8 +126,7 @@ impl MultiValue {
|
||||
|
||||
/// 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.
|
||||
/// This method works in *O*(1) time and does not allocate any additional memory.
|
||||
#[inline]
|
||||
pub fn from_vec(vec: Vec<Value>) -> MultiValue {
|
||||
vec.into()
|
||||
@@ -135,7 +134,8 @@ impl MultiValue {
|
||||
|
||||
/// Consumes the `MultiValue` and returns a vector of values.
|
||||
///
|
||||
/// This methods works in *O*(1) time and does not allocate any additional memory.
|
||||
/// This method needs *O*(*n*) data movement if the circular buffer doesn't happen to be at the
|
||||
/// beginning of the allocation.
|
||||
#[inline]
|
||||
pub fn into_vec(self) -> Vec<Value> {
|
||||
self.into()
|
||||
|
||||
+1
-1
@@ -13,7 +13,7 @@ pub use crate::{
|
||||
UserData as LuaUserData, UserDataFields as LuaUserDataFields, UserDataMetatable as LuaUserDataMetatable,
|
||||
UserDataMethods as LuaUserDataMethods, UserDataRef as LuaUserDataRef,
|
||||
UserDataRefMut as LuaUserDataRefMut, UserDataRegistry as LuaUserDataRegistry, Value as LuaValue,
|
||||
VmState as LuaVmState,
|
||||
Variadic as LuaVariadic, VmState as LuaVmState, WeakLua,
|
||||
};
|
||||
|
||||
#[cfg(not(feature = "luau"))]
|
||||
|
||||
+6
-6
@@ -168,15 +168,15 @@ impl<'scope, 'env: 'scope> Scope<'scope, 'env> {
|
||||
#[cfg(feature = "luau")]
|
||||
let ud_ptr = {
|
||||
let data = UserDataStorage::new_scoped(data);
|
||||
util::push_userdata::<UserDataStorage<T>>(state, data, protect)?
|
||||
util::push_userdata(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 _);
|
||||
let mut registry = UserDataRegistry::new_unique(self.lua.lua(), ud_ptr as *mut _);
|
||||
T::register(&mut registry);
|
||||
self.lua.push_userdata_metatable(registry)?;
|
||||
self.lua.push_userdata_metatable(registry.into_raw())?;
|
||||
let mt_ptr = ffi::lua_topointer(state, -1);
|
||||
self.lua.register_userdata_metatable(mt_ptr, None);
|
||||
|
||||
@@ -216,15 +216,15 @@ impl<'scope, 'env: 'scope> Scope<'scope, 'env> {
|
||||
#[cfg(feature = "luau")]
|
||||
let ud_ptr = {
|
||||
let data = UserDataStorage::new_scoped(data);
|
||||
util::push_userdata::<UserDataStorage<T>>(state, data, protect)?
|
||||
util::push_userdata(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 _);
|
||||
let mut registry = UserDataRegistry::new_unique(self.lua.lua(), ud_ptr as *mut _);
|
||||
register(&mut registry);
|
||||
self.lua.push_userdata_metatable(registry)?;
|
||||
self.lua.push_userdata_metatable(registry.into_raw())?;
|
||||
let mt_ptr = ffi::lua_topointer(state, -1);
|
||||
self.lua.register_userdata_metatable(mt_ptr, None);
|
||||
|
||||
|
||||
@@ -49,6 +49,11 @@ pub struct Options {
|
||||
///
|
||||
/// Default: **false**
|
||||
pub sort_keys: bool,
|
||||
|
||||
/// If true, empty Lua tables will be encoded as array, instead of map.
|
||||
///
|
||||
/// Default: **false**
|
||||
pub encode_empty_tables_as_array: bool,
|
||||
}
|
||||
|
||||
impl Default for Options {
|
||||
@@ -64,6 +69,7 @@ impl Options {
|
||||
deny_unsupported_types: true,
|
||||
deny_recursive_tables: true,
|
||||
sort_keys: false,
|
||||
encode_empty_tables_as_array: false,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -93,6 +99,15 @@ impl Options {
|
||||
self.sort_keys = enabled;
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets [`encode_empty_tables_as_array`] option.
|
||||
///
|
||||
/// [`encode_empty_tables_as_array`]: #structfield.encode_empty_tables_as_array
|
||||
#[must_use]
|
||||
pub const fn encode_empty_tables_as_array(mut self, enabled: bool) -> Self {
|
||||
self.encode_empty_tables_as_array = enabled;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl Deserializer {
|
||||
@@ -141,6 +156,9 @@ impl<'de> serde::Deserializer<'de> for Deserializer {
|
||||
Err(_) => visitor.visit_bytes(&s.as_bytes()),
|
||||
},
|
||||
Value::Table(ref t) if t.raw_len() > 0 || t.is_array() => self.deserialize_seq(visitor),
|
||||
Value::Table(ref t) if self.options.encode_empty_tables_as_array && t.is_empty() => {
|
||||
self.deserialize_seq(visitor)
|
||||
}
|
||||
Value::Table(_) => self.deserialize_map(visitor),
|
||||
Value::LightUserData(ud) if ud.0.is_null() => visitor.visit_none(),
|
||||
Value::UserData(ud) if ud.is_serializable() => {
|
||||
|
||||
+68
-31
@@ -46,18 +46,20 @@ use serde::Serialize;
|
||||
|
||||
pub(crate) use extra::ExtraData;
|
||||
pub use raw::RawLua;
|
||||
use util::{callback_error_ext, StateGuard};
|
||||
use util::callback_error_ext;
|
||||
|
||||
/// Top level Lua struct which represents an instance of Lua VM.
|
||||
#[derive(Clone)]
|
||||
pub struct Lua {
|
||||
pub(self) raw: XRc<ReentrantMutex<RawLua>>,
|
||||
// Controls whether garbage collection should be run on drop
|
||||
pub(self) collect_garbage: bool,
|
||||
}
|
||||
|
||||
/// Weak reference to Lua instance.
|
||||
///
|
||||
/// This can used to prevent circular references between Lua and Rust objects.
|
||||
#[derive(Clone)]
|
||||
pub(crate) struct WeakLua(XWeak<ReentrantMutex<RawLua>>);
|
||||
pub struct WeakLua(XWeak<ReentrantMutex<RawLua>>);
|
||||
|
||||
pub(crate) struct LuaGuard(ArcReentrantMutexGuard<RawLua>);
|
||||
|
||||
@@ -98,9 +100,6 @@ pub struct LuaOptions {
|
||||
|
||||
/// Max size of thread (coroutine) object pool used to execute asynchronous functions.
|
||||
///
|
||||
/// It works on Lua 5.4 and Luau, where [`lua_resetthread`] function
|
||||
/// is available and allows to reuse old coroutines after resetting their state.
|
||||
///
|
||||
/// Default: **0** (disabled)
|
||||
///
|
||||
/// [`lua_resetthread`]: https://www.lua.org/manual/5.4/manual.html#lua_resetthread
|
||||
@@ -154,6 +153,16 @@ impl Drop for Lua {
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for Lua {
|
||||
#[inline]
|
||||
fn clone(&self) -> Self {
|
||||
Lua {
|
||||
raw: XRc::clone(&self.raw),
|
||||
collect_garbage: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Lua {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "Lua({:p})", self.lock().state())
|
||||
@@ -421,7 +430,6 @@ impl Lua {
|
||||
|
||||
callback_error_ext(state, ptr::null_mut(), move |extra, nargs| {
|
||||
let rawlua = (*extra).raw_lua();
|
||||
let _guard = StateGuard::new(rawlua, state);
|
||||
let args = A::from_stack_args(nargs, 1, None, rawlua)?;
|
||||
func(rawlua.lua(), args)?.push_into_stack(rawlua)?;
|
||||
Ok(1)
|
||||
@@ -612,7 +620,7 @@ impl Lua {
|
||||
/// .into_function()?,
|
||||
/// )?;
|
||||
/// while co.status() == ThreadStatus::Resumable {
|
||||
/// co.resume(())?;
|
||||
/// co.resume::<()>(())?;
|
||||
/// }
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
@@ -639,7 +647,6 @@ impl Lua {
|
||||
if Rc::strong_count(&interrupt_cb) > 2 {
|
||||
return Ok(VmState::Continue); // Don't allow recursion
|
||||
}
|
||||
let _guard = StateGuard::new((*extra).raw_lua(), state);
|
||||
interrupt_cb((*extra).lua())
|
||||
});
|
||||
match result {
|
||||
@@ -687,18 +694,19 @@ impl Lua {
|
||||
unsafe extern "C-unwind" fn warn_proc(ud: *mut c_void, msg: *const c_char, tocont: c_int) {
|
||||
let extra = ud as *mut ExtraData;
|
||||
callback_error_ext((*extra).raw_lua().state(), extra, |extra, _| {
|
||||
let cb = mlua_expect!(
|
||||
(*extra).warn_callback.as_ref(),
|
||||
"no warning callback set in warn_proc"
|
||||
);
|
||||
let warn_callback = (*extra).warn_callback.clone();
|
||||
let warn_callback = mlua_expect!(warn_callback, "no warning callback set in warn_proc");
|
||||
if XRc::strong_count(&warn_callback) > 2 {
|
||||
return Ok(());
|
||||
}
|
||||
let msg = StdString::from_utf8_lossy(CStr::from_ptr(msg).to_bytes());
|
||||
cb((*extra).lua(), &msg, tocont != 0)
|
||||
warn_callback((*extra).lua(), &msg, tocont != 0)
|
||||
});
|
||||
}
|
||||
|
||||
let lua = self.lock();
|
||||
unsafe {
|
||||
(*lua.extra.get()).warn_callback = Some(Box::new(callback));
|
||||
(*lua.extra.get()).warn_callback = Some(XRc::new(callback));
|
||||
ffi::lua_setwarnf(lua.state(), Some(warn_proc), lua.extra.get() as *mut c_void);
|
||||
}
|
||||
}
|
||||
@@ -1003,10 +1011,17 @@ impl Lua {
|
||||
/// [`Chunk::exec`]: crate::Chunk::exec
|
||||
#[track_caller]
|
||||
pub fn load<'a>(&self, chunk: impl AsChunk<'a>) -> Chunk<'a> {
|
||||
let caller = Location::caller();
|
||||
self.load_with_location(chunk, Location::caller())
|
||||
}
|
||||
|
||||
pub(crate) fn load_with_location<'a>(
|
||||
&self,
|
||||
chunk: impl AsChunk<'a>,
|
||||
location: &'static Location<'static>,
|
||||
) -> Chunk<'a> {
|
||||
Chunk {
|
||||
lua: self.weak(),
|
||||
name: chunk.name().unwrap_or_else(|| caller.to_string()),
|
||||
name: chunk.name().unwrap_or_else(|| location.to_string()),
|
||||
env: chunk.environment(self),
|
||||
mode: chunk.mode(),
|
||||
source: chunk.source(),
|
||||
@@ -1036,8 +1051,8 @@ impl Lua {
|
||||
let state = lua.state();
|
||||
unsafe {
|
||||
if lua.unlikely_memory_error() {
|
||||
crate::util::push_buffer(lua.ref_thread(), buf.as_ref(), false)?;
|
||||
return Ok(Buffer(lua.pop_ref_thread()));
|
||||
crate::util::push_buffer(state, buf.as_ref(), false)?;
|
||||
return Ok(Buffer(lua.pop_ref()));
|
||||
}
|
||||
|
||||
let _sg = StackGuard::new(state);
|
||||
@@ -1306,7 +1321,7 @@ impl Lua {
|
||||
/// This methods provides a way to add fields or methods to userdata objects of a type `T`.
|
||||
pub fn register_userdata_type<T: 'static>(&self, f: impl FnOnce(&mut UserDataRegistry<T>)) -> Result<()> {
|
||||
let type_id = TypeId::of::<T>();
|
||||
let mut registry = UserDataRegistry::new(type_id);
|
||||
let mut registry = UserDataRegistry::new(self);
|
||||
f(&mut registry);
|
||||
|
||||
let lua = self.lock();
|
||||
@@ -1316,8 +1331,8 @@ impl Lua {
|
||||
ffi::luaL_unref(lua.state(), ffi::LUA_REGISTRYINDEX, table_id);
|
||||
}
|
||||
|
||||
// Register the type
|
||||
lua.create_userdata_metatable(registry)?;
|
||||
// Add to "pending" registration map
|
||||
((*lua.extra.get()).pending_userdata_reg).insert(type_id, registry.into_raw());
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@@ -1785,8 +1800,8 @@ impl Lua {
|
||||
let state = lua.state();
|
||||
unsafe {
|
||||
let mut unref_list = (*lua.extra.get()).registry_unref_list.lock();
|
||||
let unref_list = mem::replace(&mut *unref_list, Some(Vec::new()));
|
||||
for id in mlua_expect!(unref_list, "unref list not set") {
|
||||
let unref_list = unref_list.replace(Vec::new());
|
||||
for id in mlua_expect!(unref_list, "unref list is not set") {
|
||||
ffi::luaL_unref(state, ffi::LUA_REGISTRYINDEX, id);
|
||||
}
|
||||
}
|
||||
@@ -1816,7 +1831,7 @@ impl Lua {
|
||||
/// fn main() -> Result<()> {
|
||||
/// let lua = Lua::new();
|
||||
/// lua.set_app_data("hello");
|
||||
/// lua.create_function(hello)?.call(())?;
|
||||
/// lua.create_function(hello)?.call::<()>(())?;
|
||||
/// let s = lua.app_data_ref::<&str>().unwrap();
|
||||
/// assert_eq!(*s, "world");
|
||||
/// Ok(())
|
||||
@@ -1900,6 +1915,8 @@ impl Lua {
|
||||
}
|
||||
|
||||
/// Returns an internal `Poll::Pending` constant used for executing async callbacks.
|
||||
///
|
||||
/// Every time when [`Future`] is Pending, Lua corotine is suspended with this constant.
|
||||
#[cfg(feature = "async")]
|
||||
#[doc(hidden)]
|
||||
#[inline(always)]
|
||||
@@ -1908,6 +1925,15 @@ impl Lua {
|
||||
LightUserData(&ASYNC_POLL_PENDING as *const u8 as *mut std::os::raw::c_void)
|
||||
}
|
||||
|
||||
/// Returns a weak reference to the Lua instance.
|
||||
///
|
||||
/// This is useful for creating a reference to the Lua instance that does not prevent it from
|
||||
/// being deallocated.
|
||||
#[inline(always)]
|
||||
pub fn weak(&self) -> WeakLua {
|
||||
WeakLua(XRc::downgrade(&self.raw))
|
||||
}
|
||||
|
||||
// Luau version located in `luau/mod.rs`
|
||||
#[cfg(not(feature = "luau"))]
|
||||
fn disable_c_modules(&self) -> Result<()> {
|
||||
@@ -1946,11 +1972,6 @@ impl Lua {
|
||||
LuaGuard(self.raw.lock_arc())
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub(crate) fn weak(&self) -> WeakLua {
|
||||
WeakLua(XRc::downgrade(&self.raw))
|
||||
}
|
||||
|
||||
/// Returns a handle to the unprotected Lua state without any synchronization.
|
||||
///
|
||||
/// This is useful where we know that the lock is already held by the caller.
|
||||
@@ -1973,14 +1994,30 @@ impl WeakLua {
|
||||
Some(LuaGuard::new(self.0.upgrade()?))
|
||||
}
|
||||
|
||||
/// Upgrades the weak Lua reference to a strong reference.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if the Lua instance is destroyed.
|
||||
#[track_caller]
|
||||
#[inline(always)]
|
||||
pub(crate) fn upgrade(&self) -> Lua {
|
||||
pub fn upgrade(&self) -> Lua {
|
||||
Lua {
|
||||
raw: self.0.upgrade().expect("Lua instance is destroyed"),
|
||||
collect_garbage: false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Tries to upgrade the weak Lua reference to a strong reference.
|
||||
///
|
||||
/// Returns `None` if the Lua instance is destroyed.
|
||||
#[inline(always)]
|
||||
pub fn try_upgrade(&self) -> Option<Lua> {
|
||||
Some(Lua {
|
||||
raw: self.0.upgrade()?,
|
||||
collect_garbage: false,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for WeakLua {
|
||||
|
||||
+8
-3
@@ -13,6 +13,7 @@ use crate::error::Result;
|
||||
use crate::state::RawLua;
|
||||
use crate::stdlib::StdLib;
|
||||
use crate::types::{AppData, ReentrantMutex, XRc};
|
||||
use crate::userdata::RawUserDataRegistry;
|
||||
use crate::util::{get_internal_metatable, push_internal_userdata, TypeKey, WrappedFailure};
|
||||
|
||||
#[cfg(any(feature = "luau", doc))]
|
||||
@@ -26,8 +27,8 @@ use super::{Lua, WeakLua};
|
||||
// Unique key to store `ExtraData` in the registry
|
||||
static EXTRA_REGISTRY_KEY: u8 = 0;
|
||||
|
||||
const WRAPPED_FAILURE_POOL_SIZE: usize = 64;
|
||||
const REF_STACK_RESERVE: c_int = 1;
|
||||
const WRAPPED_FAILURE_POOL_DEFAULT_CAPACITY: usize = 64;
|
||||
const REF_STACK_RESERVE: c_int = 2;
|
||||
|
||||
/// Data associated with the Lua state.
|
||||
pub(crate) struct ExtraData {
|
||||
@@ -35,6 +36,7 @@ pub(crate) struct ExtraData {
|
||||
pub(super) weak: MaybeUninit<WeakLua>,
|
||||
pub(super) owned: bool,
|
||||
|
||||
pub(super) pending_userdata_reg: FxHashMap<TypeId, RawUserDataRegistry>,
|
||||
pub(super) registered_userdata_t: FxHashMap<TypeId, c_int>,
|
||||
pub(super) registered_userdata_mt: FxHashMap<*const c_void, Option<TypeId>>,
|
||||
pub(super) last_checked_userdata_mt: (*const c_void, Option<TypeId>),
|
||||
@@ -58,6 +60,7 @@ pub(crate) struct ExtraData {
|
||||
|
||||
// Pool of `WrappedFailure` enums in the ref thread (as userdata)
|
||||
pub(super) wrapped_failure_pool: Vec<c_int>,
|
||||
pub(super) wrapped_failure_top: usize,
|
||||
// Pool of `Thread`s (coroutines) for async execution
|
||||
#[cfg(feature = "async")]
|
||||
pub(super) thread_pool: Vec<c_int>,
|
||||
@@ -144,6 +147,7 @@ impl ExtraData {
|
||||
lua: MaybeUninit::uninit(),
|
||||
weak: MaybeUninit::uninit(),
|
||||
owned,
|
||||
pending_userdata_reg: FxHashMap::default(),
|
||||
registered_userdata_t: FxHashMap::default(),
|
||||
registered_userdata_mt: FxHashMap::default(),
|
||||
last_checked_userdata_mt: (ptr::null(), None),
|
||||
@@ -157,7 +161,8 @@ impl ExtraData {
|
||||
ref_stack_size: ffi::LUA_MINSTACK - REF_STACK_RESERVE,
|
||||
ref_stack_top: ffi::lua_gettop(ref_thread),
|
||||
ref_free: Vec::new(),
|
||||
wrapped_failure_pool: Vec::with_capacity(WRAPPED_FAILURE_POOL_SIZE),
|
||||
wrapped_failure_pool: Vec::with_capacity(WRAPPED_FAILURE_POOL_DEFAULT_CAPACITY),
|
||||
wrapped_failure_top: 0,
|
||||
#[cfg(feature = "async")]
|
||||
thread_pool: Vec::new(),
|
||||
wrapped_failure_mt_ptr,
|
||||
|
||||
+127
-94
@@ -12,7 +12,7 @@ use crate::chunk::ChunkMode;
|
||||
use crate::error::{Error, Result};
|
||||
use crate::function::Function;
|
||||
use crate::memory::{MemoryState, ALLOCATOR};
|
||||
use crate::state::util::{callback_error_ext, ref_stack_pop, StateGuard};
|
||||
use crate::state::util::{callback_error_ext, ref_stack_pop};
|
||||
use crate::stdlib::StdLib;
|
||||
use crate::string::String;
|
||||
use crate::table::Table;
|
||||
@@ -22,12 +22,15 @@ use crate::types::{
|
||||
AppDataRef, AppDataRefMut, Callback, CallbackUpvalue, DestructedUserdata, Integer, LightUserData,
|
||||
MaybeSend, ReentrantMutex, RegistryKey, ValueRef, XRc,
|
||||
};
|
||||
use crate::userdata::{AnyUserData, MetaMethod, UserData, UserDataRegistry, UserDataStorage};
|
||||
use crate::userdata::{
|
||||
init_userdata_metatable, AnyUserData, MetaMethod, RawUserDataRegistry, UserData, UserDataRegistry,
|
||||
UserDataStorage,
|
||||
};
|
||||
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, take_userdata, StackGuard, WrappedFailure,
|
||||
get_metatable_ptr, get_userdata, init_error_registry, init_internal_metatable, pop_error,
|
||||
push_internal_userdata, push_string, push_table, rawset_field, safe_pcall, safe_xpcall, short_type_name,
|
||||
StackGuard, WrappedFailure,
|
||||
};
|
||||
use crate::value::{Nil, Value};
|
||||
|
||||
@@ -317,41 +320,60 @@ impl RawLua {
|
||||
let state = self.state();
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(state);
|
||||
check_stack(state, 2)?;
|
||||
check_stack(state, 3)?;
|
||||
|
||||
let mode_str = match mode {
|
||||
let name = name.map(CStr::as_ptr).unwrap_or(ptr::null());
|
||||
let mode = match mode {
|
||||
Some(ChunkMode::Binary) => cstr!("b"),
|
||||
Some(ChunkMode::Text) => cstr!("t"),
|
||||
None => cstr!("bt"),
|
||||
};
|
||||
|
||||
match ffi::luaL_loadbufferenv(
|
||||
state,
|
||||
source.as_ptr() as *const c_char,
|
||||
source.len(),
|
||||
name.map(|n| n.as_ptr()).unwrap_or_else(ptr::null),
|
||||
mode_str,
|
||||
match env {
|
||||
Some(env) => {
|
||||
self.push_ref(&env.0);
|
||||
-1
|
||||
}
|
||||
_ => 0,
|
||||
},
|
||||
) {
|
||||
ffi::LUA_OK => {
|
||||
#[cfg(feature = "luau-jit")]
|
||||
if (*self.extra.get()).enable_jit && ffi::luau_codegen_supported() != 0 {
|
||||
ffi::luau_codegen_compile(state, -1);
|
||||
}
|
||||
|
||||
Ok(Function(self.pop_ref()))
|
||||
}
|
||||
let status = if self.unlikely_memory_error() {
|
||||
self.load_chunk_inner(state, name, env, mode, source)
|
||||
} else {
|
||||
// Luau and Lua 5.2 can trigger an exception during chunk loading
|
||||
protect_lua!(state, 0, 1, |state| {
|
||||
self.load_chunk_inner(state, name, env, mode, source)
|
||||
})?
|
||||
};
|
||||
match status {
|
||||
ffi::LUA_OK => Ok(Function(self.pop_ref())),
|
||||
err => Err(pop_error(state, err)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn load_chunk_inner(
|
||||
&self,
|
||||
state: *mut ffi::lua_State,
|
||||
name: *const c_char,
|
||||
env: Option<&Table>,
|
||||
mode: *const c_char,
|
||||
source: &[u8],
|
||||
) -> c_int {
|
||||
let status = ffi::luaL_loadbufferenv(
|
||||
state,
|
||||
source.as_ptr() as *const c_char,
|
||||
source.len(),
|
||||
name,
|
||||
mode,
|
||||
match env {
|
||||
Some(env) => {
|
||||
self.push_ref(&env.0);
|
||||
-1
|
||||
}
|
||||
_ => 0,
|
||||
},
|
||||
);
|
||||
#[cfg(feature = "luau-jit")]
|
||||
if status == ffi::LUA_OK {
|
||||
if (*self.extra.get()).enable_jit && ffi::luau_codegen_supported() != 0 {
|
||||
ffi::luau_codegen_compile(state, -1);
|
||||
}
|
||||
}
|
||||
status
|
||||
}
|
||||
|
||||
/// Sets a 'hook' function for a thread (coroutine).
|
||||
#[cfg(not(feature = "luau"))]
|
||||
pub(crate) unsafe fn set_thread_hook<F>(
|
||||
@@ -379,7 +401,6 @@ impl RawLua {
|
||||
return Ok(VmState::Continue); // Don't allow recursion
|
||||
}
|
||||
let rawlua = (*extra).raw_lua();
|
||||
let _guard = StateGuard::new(rawlua, state);
|
||||
let debug = Debug::new(rawlua, ar);
|
||||
hook_cb((*extra).lua(), debug)
|
||||
});
|
||||
@@ -411,8 +432,8 @@ impl RawLua {
|
||||
pub(crate) unsafe fn create_string(&self, s: impl AsRef<[u8]>) -> Result<String> {
|
||||
let state = self.state();
|
||||
if self.unlikely_memory_error() {
|
||||
push_string(self.ref_thread(), s.as_ref(), false)?;
|
||||
return Ok(String(self.pop_ref_thread()));
|
||||
push_string(state, s.as_ref(), false)?;
|
||||
return Ok(String(self.pop_ref()));
|
||||
}
|
||||
|
||||
let _sg = StackGuard::new(state);
|
||||
@@ -423,12 +444,12 @@ impl RawLua {
|
||||
|
||||
/// See [`Lua::create_table_with_capacity`]
|
||||
pub(crate) unsafe fn create_table_with_capacity(&self, narr: usize, nrec: usize) -> Result<Table> {
|
||||
let state = self.state();
|
||||
if self.unlikely_memory_error() {
|
||||
push_table(self.ref_thread(), narr, nrec, false)?;
|
||||
return Ok(Table(self.pop_ref_thread()));
|
||||
push_table(state, narr, nrec, false)?;
|
||||
return Ok(Table(self.pop_ref()));
|
||||
}
|
||||
|
||||
let state = self.state();
|
||||
let _sg = StackGuard::new(state);
|
||||
check_stack(state, 3)?;
|
||||
push_table(state, narr, nrec, true)?;
|
||||
@@ -484,7 +505,6 @@ impl RawLua {
|
||||
/// Wraps a Lua function into a new or recycled thread (coroutine).
|
||||
#[cfg(feature = "async")]
|
||||
pub(crate) unsafe fn create_recycled_thread(&self, func: &Function) -> Result<Thread> {
|
||||
#[cfg(any(feature = "lua54", feature = "luau"))]
|
||||
if let Some(index) = (*self.extra.get()).thread_pool.pop() {
|
||||
let thread_state = ffi::lua_tothread(self.ref_thread(), index);
|
||||
ffi::lua_xpush(self.ref_thread(), thread_state, func.0.index);
|
||||
@@ -504,27 +524,47 @@ impl RawLua {
|
||||
|
||||
/// Resets thread (coroutine) and returns it to the pool for later use.
|
||||
#[cfg(feature = "async")]
|
||||
#[cfg(any(feature = "lua54", feature = "luau"))]
|
||||
pub(crate) unsafe fn recycle_thread(&self, thread: &mut Thread) -> bool {
|
||||
pub(crate) unsafe fn recycle_thread(&self, thread: &mut Thread) {
|
||||
let thread_state = thread.1;
|
||||
let extra = &mut *self.extra.get();
|
||||
if extra.thread_pool.len() < extra.thread_pool.capacity() {
|
||||
let thread_state = ffi::lua_tothread(extra.ref_thread, thread.0.index);
|
||||
#[cfg(all(feature = "lua54", not(feature = "vendored")))]
|
||||
let status = ffi::lua_resetthread(thread_state);
|
||||
#[cfg(all(feature = "lua54", feature = "vendored"))]
|
||||
let status = ffi::lua_closethread(thread_state, self.state());
|
||||
if extra.thread_pool.len() == extra.thread_pool.capacity() {
|
||||
#[cfg(feature = "lua54")]
|
||||
if status != ffi::LUA_OK {
|
||||
// Error object is on top, drop it
|
||||
if ffi::lua_status(thread_state) != ffi::LUA_OK {
|
||||
// Close all to-be-closed variables without returning thread to the pool
|
||||
#[cfg(not(feature = "vendored"))]
|
||||
ffi::lua_resetthread(thread_state);
|
||||
#[cfg(feature = "vendored")]
|
||||
ffi::lua_closethread(thread_state, self.state());
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
let mut reset_ok = false;
|
||||
if ffi::lua_status(thread_state) == ffi::LUA_OK {
|
||||
if ffi::lua_gettop(thread_state) > 0 {
|
||||
ffi::lua_settop(thread_state, 0);
|
||||
}
|
||||
#[cfg(feature = "luau")]
|
||||
reset_ok = true;
|
||||
}
|
||||
|
||||
#[cfg(feature = "lua54")]
|
||||
if !reset_ok {
|
||||
#[cfg(not(feature = "vendored"))]
|
||||
let status = ffi::lua_resetthread(thread_state);
|
||||
#[cfg(feature = "vendored")]
|
||||
let status = ffi::lua_closethread(thread_state, self.state());
|
||||
reset_ok = status == ffi::LUA_OK;
|
||||
}
|
||||
#[cfg(feature = "luau")]
|
||||
if !reset_ok {
|
||||
ffi::lua_resetthread(thread_state);
|
||||
reset_ok = true;
|
||||
}
|
||||
|
||||
if reset_ok {
|
||||
extra.thread_pool.push(thread.0.index);
|
||||
thread.0.drop = false; // Prevent thread from being garbage collected
|
||||
return true;
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
/// Pushes a value that implements `IntoLua` onto the Lua stack.
|
||||
@@ -713,6 +753,10 @@ impl RawLua {
|
||||
|
||||
pub(crate) unsafe fn drop_ref(&self, vref: &ValueRef) {
|
||||
let ref_thread = self.ref_thread();
|
||||
mlua_debug_assert!(
|
||||
ffi::lua_gettop(ref_thread) >= vref.index,
|
||||
"GC finalizer is not allowed in ref_thread"
|
||||
);
|
||||
ffi::lua_pushnil(ref_thread);
|
||||
ffi::lua_replace(ref_thread, vref.index);
|
||||
(*self.extra.get()).ref_free.push(vref.index);
|
||||
@@ -754,10 +798,10 @@ impl RawLua {
|
||||
}
|
||||
|
||||
// Create a new metatable from `UserData` definition
|
||||
let mut registry = UserDataRegistry::new(type_id);
|
||||
let mut registry = UserDataRegistry::new(self.lua());
|
||||
T::register(&mut registry);
|
||||
|
||||
self.create_userdata_metatable(registry)
|
||||
self.create_userdata_metatable(registry.into_raw())
|
||||
})
|
||||
}
|
||||
|
||||
@@ -772,8 +816,11 @@ impl RawLua {
|
||||
return Ok(table_id as Integer);
|
||||
}
|
||||
|
||||
// Create an empty metatable
|
||||
let registry = UserDataRegistry::<T>::new(type_id);
|
||||
// Check if metatable creation is pending or create an empty metatable otherwise
|
||||
let registry = match (*self.extra.get()).pending_userdata_reg.remove(&type_id) {
|
||||
Some(registry) => registry,
|
||||
None => UserDataRegistry::<T>::new(self.lua()).into_raw(),
|
||||
};
|
||||
self.create_userdata_metatable(registry)
|
||||
})
|
||||
}
|
||||
@@ -787,12 +834,11 @@ impl RawLua {
|
||||
let _sg = StackGuard::new(state);
|
||||
check_stack(state, 3)?;
|
||||
|
||||
// We push metatable first to ensure having correct metatable with `__gc` method
|
||||
ffi::lua_pushnil(state);
|
||||
ffi::lua_rawgeti(state, ffi::LUA_REGISTRYINDEX, get_metatable_id()?);
|
||||
// We generate metatable first to make sure it *always* available when userdata pushed
|
||||
let mt_id = get_metatable_id()?;
|
||||
let protect = !self.unlikely_memory_error();
|
||||
crate::util::push_userdata(state, data, protect)?;
|
||||
ffi::lua_replace(state, -3);
|
||||
ffi::lua_rawgeti(state, ffi::LUA_REGISTRYINDEX, mt_id);
|
||||
ffi::lua_setmetatable(state, -2);
|
||||
|
||||
// Set empty environment for Lua 5.1
|
||||
@@ -810,12 +856,9 @@ impl RawLua {
|
||||
Ok(AnyUserData(self.pop_ref()))
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn create_userdata_metatable<T>(
|
||||
&self,
|
||||
registry: UserDataRegistry<T>,
|
||||
) -> Result<Integer> {
|
||||
pub(crate) unsafe fn create_userdata_metatable(&self, registry: RawUserDataRegistry) -> Result<Integer> {
|
||||
let state = self.state();
|
||||
let type_id = registry.type_id();
|
||||
let type_id = registry.type_id;
|
||||
|
||||
self.push_userdata_metatable(registry)?;
|
||||
|
||||
@@ -832,7 +875,7 @@ impl RawLua {
|
||||
Ok(id as Integer)
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn push_userdata_metatable<T>(&self, mut registry: UserDataRegistry<T>) -> Result<()> {
|
||||
pub(crate) unsafe fn push_userdata_metatable(&self, mut registry: RawUserDataRegistry) -> Result<()> {
|
||||
let state = self.state();
|
||||
let mut stack_guard = StackGuard::new(state);
|
||||
check_stack(state, 13)?;
|
||||
@@ -852,14 +895,14 @@ impl RawLua {
|
||||
rawset_field(state, -2, MetaMethod::validate(&k)?)?;
|
||||
}
|
||||
let mut has_name = false;
|
||||
for (k, push_field) in registry.meta_fields {
|
||||
for (k, v) in registry.meta_fields {
|
||||
has_name = has_name || k == MetaMethod::Type;
|
||||
push_field(self)?;
|
||||
v?.push_into_stack(self)?;
|
||||
rawset_field(state, -2, MetaMethod::validate(&k)?)?;
|
||||
}
|
||||
// Set `__name/__type` if not provided
|
||||
if !has_name {
|
||||
let type_name = short_type_name::<T>();
|
||||
let type_name = registry.type_name;
|
||||
push_string(state, type_name.as_bytes(), !self.unlikely_memory_error())?;
|
||||
rawset_field(state, -2, MetaMethod::Type.name())?;
|
||||
}
|
||||
@@ -876,8 +919,8 @@ impl RawLua {
|
||||
ffi::lua_pop(state, 1);
|
||||
push_table(state, 0, fields_nrec, true)?;
|
||||
}
|
||||
for (k, push_field) in mem::take(&mut registry.fields) {
|
||||
push_field(self)?;
|
||||
for (k, v) in mem::take(&mut registry.fields) {
|
||||
v?.push_into_stack(self)?;
|
||||
rawset_field(state, -2, &k)?;
|
||||
}
|
||||
rawset_field(state, metatable_index, "__index")?;
|
||||
@@ -897,12 +940,12 @@ impl RawLua {
|
||||
self.push(self.create_callback(m)?)?;
|
||||
rawset_field(state, -2, &k)?;
|
||||
}
|
||||
for (k, push_field) in registry.fields {
|
||||
for (k, v) in registry.fields {
|
||||
unsafe extern "C-unwind" fn return_field(state: *mut ffi::lua_State) -> c_int {
|
||||
ffi::lua_pushvalue(state, ffi::lua_upvalueindex(1));
|
||||
1
|
||||
}
|
||||
push_field(self)?;
|
||||
v?.push_into_stack(self)?;
|
||||
protect_lua!(state, 1, 1, fn(state) {
|
||||
ffi::lua_pushcclosure(state, return_field, 1);
|
||||
})?;
|
||||
@@ -960,18 +1003,7 @@ impl RawLua {
|
||||
}
|
||||
}
|
||||
|
||||
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>);
|
||||
ffi::lua_pushcfunction(state, registry.destructor);
|
||||
rawset_field(state, metatable_index, "__gc")?;
|
||||
|
||||
init_userdata_metatable(
|
||||
@@ -1004,17 +1036,22 @@ impl RawLua {
|
||||
// Returns `TypeId` for the userdata ref, checking that it's registered and not destructed.
|
||||
//
|
||||
// Returns `None` if the userdata is registered but non-static.
|
||||
pub(crate) unsafe fn get_userdata_ref_type_id(&self, vref: &ValueRef) -> Result<Option<TypeId>> {
|
||||
self.get_userdata_type_id_inner(self.ref_thread(), vref.index)
|
||||
#[inline(always)]
|
||||
pub(crate) fn get_userdata_ref_type_id(&self, vref: &ValueRef) -> Result<Option<TypeId>> {
|
||||
unsafe { self.get_userdata_type_id_inner(self.ref_thread(), vref.index) }
|
||||
}
|
||||
|
||||
// Same as `get_userdata_ref_type_id` but assumes the userdata is already on the stack.
|
||||
pub(crate) unsafe fn get_userdata_type_id<T>(&self, idx: c_int) -> Result<Option<TypeId>> {
|
||||
match self.get_userdata_type_id_inner(self.state(), idx) {
|
||||
pub(crate) unsafe fn get_userdata_type_id<T>(
|
||||
&self,
|
||||
state: *mut ffi::lua_State,
|
||||
idx: c_int,
|
||||
) -> Result<Option<TypeId>> {
|
||||
match self.get_userdata_type_id_inner(state, idx) {
|
||||
Ok(type_id) => Ok(type_id),
|
||||
Err(Error::UserDataTypeMismatch) if ffi::lua_type(self.state(), idx) != ffi::LUA_TUSERDATA => {
|
||||
Err(Error::UserDataTypeMismatch) if ffi::lua_type(state, idx) != ffi::LUA_TUSERDATA => {
|
||||
// Report `FromLuaConversionError` instead
|
||||
let idx_type_name = CStr::from_ptr(ffi::luaL_typename(self.state(), idx));
|
||||
let idx_type_name = CStr::from_ptr(ffi::luaL_typename(state, idx));
|
||||
let idx_type_name = idx_type_name.to_str().unwrap();
|
||||
let message = format!("expected userdata of type '{}'", short_type_name::<T>());
|
||||
Err(Error::from_lua_conversion(idx_type_name, "userdata", message))
|
||||
@@ -1067,7 +1104,6 @@ impl RawLua {
|
||||
// Lua ensures that `LUA_MINSTACK` stack spaces are available (after pushing arguments)
|
||||
// The lock must be already held as the callback is executed
|
||||
let rawlua = (*extra).raw_lua();
|
||||
let _guard = StateGuard::new(rawlua, state);
|
||||
match (*upvalue).data {
|
||||
Some(ref func) => func(rawlua, nargs),
|
||||
None => Err(Error::CallbackDestructed),
|
||||
@@ -1111,12 +1147,10 @@ impl RawLua {
|
||||
// Async functions cannot be scoped and therefore destroyed,
|
||||
// so the first upvalue is always valid
|
||||
let upvalue = get_userdata::<AsyncCallbackUpvalue>(state, ffi::lua_upvalueindex(1));
|
||||
let extra = (*upvalue).extra.get();
|
||||
callback_error_ext(state, extra, |extra, nargs| {
|
||||
callback_error_ext(state, (*upvalue).extra.get(), |extra, nargs| {
|
||||
// Lua ensures that `LUA_MINSTACK` stack spaces are available (after pushing arguments)
|
||||
// The lock must be already held as the callback is executed
|
||||
let rawlua = (*extra).raw_lua();
|
||||
let _guard = StateGuard::new(rawlua, state);
|
||||
|
||||
let func = &*(*upvalue).data;
|
||||
let fut = func(rawlua, nargs);
|
||||
@@ -1141,7 +1175,6 @@ impl RawLua {
|
||||
// Lua ensures that `LUA_MINSTACK` stack spaces are available (after pushing arguments)
|
||||
// The lock must be already held as the future is polled
|
||||
let rawlua = (*extra).raw_lua();
|
||||
let _guard = StateGuard::new(rawlua, state);
|
||||
|
||||
let fut = &mut (*upvalue).data;
|
||||
let mut ctx = Context::from_waker(rawlua.waker());
|
||||
|
||||
+31
-39
@@ -7,12 +7,10 @@ use crate::error::{Error, Result};
|
||||
use crate::state::{ExtraData, RawLua};
|
||||
use crate::util::{self, get_internal_metatable, WrappedFailure};
|
||||
|
||||
const WRAPPED_FAILURE_POOL_SIZE: usize = 64;
|
||||
|
||||
pub(super) struct StateGuard<'a>(&'a RawLua, *mut ffi::lua_State);
|
||||
struct StateGuard<'a>(&'a RawLua, *mut ffi::lua_State);
|
||||
|
||||
impl<'a> StateGuard<'a> {
|
||||
pub(super) fn new(inner: &'a RawLua, mut state: *mut ffi::lua_State) -> Self {
|
||||
fn new(inner: &'a RawLua, mut state: *mut ffi::lua_State) -> Self {
|
||||
state = inner.state.replace(state);
|
||||
Self(inner, state)
|
||||
}
|
||||
@@ -25,7 +23,7 @@ impl Drop for StateGuard<'_> {
|
||||
}
|
||||
|
||||
// An optimized version of `callback_error` that does not allocate `WrappedFailure` userdata
|
||||
// and instead reuses unsed values from previous calls (or allocates new).
|
||||
// and instead reuses unused values from previous calls (or allocates new).
|
||||
pub(super) unsafe fn callback_error_ext<F, R>(
|
||||
state: *mut ffi::lua_State,
|
||||
mut extra: *mut ExtraData,
|
||||
@@ -42,26 +40,27 @@ where
|
||||
|
||||
enum PreallocatedFailure {
|
||||
New(*mut WrappedFailure),
|
||||
Existing(i32),
|
||||
Reserved,
|
||||
}
|
||||
|
||||
impl PreallocatedFailure {
|
||||
unsafe fn reserve(state: *mut ffi::lua_State, extra: *mut ExtraData) -> Self {
|
||||
match (*extra).wrapped_failure_pool.pop() {
|
||||
Some(index) => PreallocatedFailure::Existing(index),
|
||||
None => {
|
||||
// We need to check stack for Luau in case when callback is called from interrupt
|
||||
// See https://github.com/Roblox/luau/issues/446 and mlua #142 and #153
|
||||
#[cfg(feature = "luau")]
|
||||
ffi::lua_rawcheckstack(state, 2);
|
||||
// Place it to the beginning of the stack
|
||||
let ud = WrappedFailure::new_userdata(state);
|
||||
ffi::lua_insert(state, 1);
|
||||
PreallocatedFailure::New(ud)
|
||||
}
|
||||
if (*extra).wrapped_failure_top > 0 {
|
||||
(*extra).wrapped_failure_top -= 1;
|
||||
return PreallocatedFailure::Reserved;
|
||||
}
|
||||
|
||||
// We need to check stack for Luau in case when callback is called from interrupt
|
||||
// See https://github.com/luau-lang/luau/issues/446 and mlua #142 and #153
|
||||
#[cfg(feature = "luau")]
|
||||
ffi::lua_rawcheckstack(state, 2);
|
||||
// Place it to the beginning of the stack
|
||||
let ud = WrappedFailure::new_userdata(state);
|
||||
ffi::lua_insert(state, 1);
|
||||
PreallocatedFailure::New(ud)
|
||||
}
|
||||
|
||||
#[cold]
|
||||
unsafe fn r#use(&self, state: *mut ffi::lua_State, extra: *mut ExtraData) -> *mut WrappedFailure {
|
||||
let ref_thread = (*extra).ref_thread;
|
||||
match *self {
|
||||
@@ -69,12 +68,12 @@ where
|
||||
ffi::lua_settop(state, 1);
|
||||
ud
|
||||
}
|
||||
PreallocatedFailure::Existing(index) => {
|
||||
PreallocatedFailure::Reserved => {
|
||||
let index = (*extra).wrapped_failure_pool.pop().unwrap();
|
||||
ffi::lua_settop(state, 0);
|
||||
#[cfg(feature = "luau")]
|
||||
ffi::lua_rawcheckstack(state, 2);
|
||||
ffi::lua_pushvalue(ref_thread, index);
|
||||
ffi::lua_xmove(ref_thread, state, 1);
|
||||
ffi::lua_xpush(ref_thread, state, index);
|
||||
ffi::lua_pushnil(ref_thread);
|
||||
ffi::lua_replace(ref_thread, index);
|
||||
(*extra).ref_free.push(index);
|
||||
@@ -87,24 +86,13 @@ where
|
||||
let ref_thread = (*extra).ref_thread;
|
||||
match self {
|
||||
PreallocatedFailure::New(_) => {
|
||||
if (*extra).wrapped_failure_pool.len() < WRAPPED_FAILURE_POOL_SIZE {
|
||||
ffi::lua_rotate(state, 1, -1);
|
||||
ffi::lua_xmove(state, ref_thread, 1);
|
||||
let index = ref_stack_pop(extra);
|
||||
(*extra).wrapped_failure_pool.push(index);
|
||||
} else {
|
||||
ffi::lua_remove(state, 1);
|
||||
}
|
||||
}
|
||||
PreallocatedFailure::Existing(index) => {
|
||||
if (*extra).wrapped_failure_pool.len() < WRAPPED_FAILURE_POOL_SIZE {
|
||||
(*extra).wrapped_failure_pool.push(index);
|
||||
} else {
|
||||
ffi::lua_pushnil(ref_thread);
|
||||
ffi::lua_replace(ref_thread, index);
|
||||
(*extra).ref_free.push(index);
|
||||
}
|
||||
ffi::lua_rotate(state, 1, -1);
|
||||
ffi::lua_xmove(state, ref_thread, 1);
|
||||
let index = ref_stack_pop(extra);
|
||||
(*extra).wrapped_failure_pool.push(index);
|
||||
(*extra).wrapped_failure_top += 1;
|
||||
}
|
||||
PreallocatedFailure::Reserved => (*extra).wrapped_failure_top += 1,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -113,7 +101,11 @@ where
|
||||
// to store a wrapped failure (error or panic) *before* we proceed.
|
||||
let prealloc_failure = PreallocatedFailure::reserve(state, extra);
|
||||
|
||||
match catch_unwind(AssertUnwindSafe(|| f(extra, nargs))) {
|
||||
match catch_unwind(AssertUnwindSafe(|| {
|
||||
let rawlua = (*extra).raw_lua();
|
||||
let _guard = StateGuard::new(rawlua, state);
|
||||
f(extra, nargs)
|
||||
})) {
|
||||
Ok(Ok(r)) => {
|
||||
// Return unused `WrappedFailure` to the pool
|
||||
prealloc_failure.release(state, extra);
|
||||
|
||||
+82
-33
@@ -1,4 +1,4 @@
|
||||
use std::borrow::Borrow;
|
||||
use std::borrow::{Borrow, Cow};
|
||||
use std::hash::{Hash, Hasher};
|
||||
use std::ops::Deref;
|
||||
use std::os::raw::{c_int, c_void};
|
||||
@@ -7,7 +7,9 @@ use std::{cmp, fmt, slice, str};
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
use crate::state::Lua;
|
||||
use crate::traits::IntoLua;
|
||||
use crate::types::{LuaType, ValueRef};
|
||||
use crate::value::Value;
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
use {
|
||||
@@ -42,13 +44,7 @@ impl String {
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn to_str(&self) -> Result<BorrowedStr> {
|
||||
let BorrowedBytes(bytes, guard) = self.as_bytes();
|
||||
let s = str::from_utf8(bytes).map_err(|e| Error::FromLuaConversionError {
|
||||
from: "string",
|
||||
to: "&str".to_string(),
|
||||
message: Some(e.to_string()),
|
||||
})?;
|
||||
Ok(BorrowedStr(s, guard))
|
||||
BorrowedStr::try_from(self)
|
||||
}
|
||||
|
||||
/// Converts this string to a [`StdString`].
|
||||
@@ -107,19 +103,21 @@ impl String {
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn as_bytes(&self) -> BorrowedBytes {
|
||||
let (bytes, guard) = unsafe { self.to_slice() };
|
||||
BorrowedBytes(&bytes[..bytes.len() - 1], guard)
|
||||
BorrowedBytes::from(self)
|
||||
}
|
||||
|
||||
/// Get the bytes that make up this string, including the trailing nul byte.
|
||||
pub fn as_bytes_with_nul(&self) -> BorrowedBytes {
|
||||
let (bytes, guard) = unsafe { self.to_slice() };
|
||||
BorrowedBytes(bytes, guard)
|
||||
let BorrowedBytes { buf, borrow, _lua } = BorrowedBytes::from(self);
|
||||
// Include the trailing nul byte (it's always present but excluded by default)
|
||||
let buf = unsafe { slice::from_raw_parts((*buf).as_ptr(), (*buf).len() + 1) };
|
||||
BorrowedBytes { buf, borrow, _lua }
|
||||
}
|
||||
|
||||
// Does not return the terminating nul byte
|
||||
unsafe fn to_slice(&self) -> (&[u8], Lua) {
|
||||
let lua = self.0.lua.upgrade();
|
||||
let slice = unsafe {
|
||||
let slice = {
|
||||
let rawlua = lua.lock();
|
||||
let ref_thread = rawlua.ref_thread();
|
||||
|
||||
@@ -132,7 +130,7 @@ impl String {
|
||||
// string type
|
||||
let mut size = 0;
|
||||
let data = ffi::lua_tolstring(ref_thread, self.0.index, &mut size);
|
||||
slice::from_raw_parts(data as *const u8, size + 1)
|
||||
slice::from_raw_parts(data as *const u8, size)
|
||||
};
|
||||
(slice, lua)
|
||||
}
|
||||
@@ -236,40 +234,45 @@ impl fmt::Display for Display<'_> {
|
||||
}
|
||||
|
||||
/// A borrowed string (`&str`) that holds a strong reference to the Lua state.
|
||||
pub struct BorrowedStr<'a>(&'a str, #[allow(unused)] Lua);
|
||||
pub struct BorrowedStr<'a> {
|
||||
// `buf` points to a readonly memory managed by Lua
|
||||
pub(crate) buf: &'a str,
|
||||
pub(crate) borrow: Cow<'a, String>,
|
||||
pub(crate) _lua: Lua,
|
||||
}
|
||||
|
||||
impl Deref for BorrowedStr<'_> {
|
||||
type Target = str;
|
||||
|
||||
#[inline(always)]
|
||||
fn deref(&self) -> &str {
|
||||
self.0
|
||||
self.buf
|
||||
}
|
||||
}
|
||||
|
||||
impl Borrow<str> for BorrowedStr<'_> {
|
||||
#[inline(always)]
|
||||
fn borrow(&self) -> &str {
|
||||
self.0
|
||||
self.buf
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<str> for BorrowedStr<'_> {
|
||||
#[inline(always)]
|
||||
fn as_ref(&self) -> &str {
|
||||
self.0
|
||||
self.buf
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for BorrowedStr<'_> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
self.0.fmt(f)
|
||||
self.buf.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for BorrowedStr<'_> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
self.0.fmt(f)
|
||||
self.buf.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -278,7 +281,7 @@ where
|
||||
T: AsRef<str>,
|
||||
{
|
||||
fn eq(&self, other: &T) -> bool {
|
||||
self.0 == other.as_ref()
|
||||
self.buf == other.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -289,45 +292,65 @@ where
|
||||
T: AsRef<str>,
|
||||
{
|
||||
fn partial_cmp(&self, other: &T) -> Option<cmp::Ordering> {
|
||||
self.0.partial_cmp(other.as_ref())
|
||||
self.buf.partial_cmp(other.as_ref())
|
||||
}
|
||||
}
|
||||
|
||||
impl Ord for BorrowedStr<'_> {
|
||||
fn cmp(&self, other: &Self) -> cmp::Ordering {
|
||||
self.0.cmp(other.0)
|
||||
self.buf.cmp(other.buf)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> TryFrom<&'a String> for BorrowedStr<'a> {
|
||||
type Error = Error;
|
||||
|
||||
#[inline]
|
||||
fn try_from(value: &'a String) -> Result<Self> {
|
||||
let BorrowedBytes { buf, borrow, _lua } = BorrowedBytes::from(value);
|
||||
let buf = str::from_utf8(buf).map_err(|e| Error::FromLuaConversionError {
|
||||
from: "string",
|
||||
to: "&str".to_string(),
|
||||
message: Some(e.to_string()),
|
||||
})?;
|
||||
Ok(Self { buf, borrow, _lua })
|
||||
}
|
||||
}
|
||||
|
||||
/// A borrowed byte slice (`&[u8]`) that holds a strong reference to the Lua state.
|
||||
pub struct BorrowedBytes<'a>(&'a [u8], #[allow(unused)] Lua);
|
||||
pub struct BorrowedBytes<'a> {
|
||||
// `buf` points to a readonly memory managed by Lua
|
||||
pub(crate) buf: &'a [u8],
|
||||
pub(crate) borrow: Cow<'a, String>,
|
||||
pub(crate) _lua: Lua,
|
||||
}
|
||||
|
||||
impl Deref for BorrowedBytes<'_> {
|
||||
type Target = [u8];
|
||||
|
||||
#[inline(always)]
|
||||
fn deref(&self) -> &[u8] {
|
||||
self.0
|
||||
self.buf
|
||||
}
|
||||
}
|
||||
|
||||
impl Borrow<[u8]> for BorrowedBytes<'_> {
|
||||
#[inline(always)]
|
||||
fn borrow(&self) -> &[u8] {
|
||||
self.0
|
||||
self.buf
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<[u8]> for BorrowedBytes<'_> {
|
||||
#[inline(always)]
|
||||
fn as_ref(&self) -> &[u8] {
|
||||
self.0
|
||||
self.buf
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for BorrowedBytes<'_> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
self.0.fmt(f)
|
||||
self.buf.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -336,7 +359,7 @@ where
|
||||
T: AsRef<[u8]>,
|
||||
{
|
||||
fn eq(&self, other: &T) -> bool {
|
||||
self.0 == other.as_ref()
|
||||
self.buf == other.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -347,22 +370,48 @@ where
|
||||
T: AsRef<[u8]>,
|
||||
{
|
||||
fn partial_cmp(&self, other: &T) -> Option<cmp::Ordering> {
|
||||
self.0.partial_cmp(other.as_ref())
|
||||
self.buf.partial_cmp(other.as_ref())
|
||||
}
|
||||
}
|
||||
|
||||
impl Ord for BorrowedBytes<'_> {
|
||||
fn cmp(&self, other: &Self) -> cmp::Ordering {
|
||||
self.0.cmp(other.0)
|
||||
self.buf.cmp(other.buf)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> IntoIterator for BorrowedBytes<'a> {
|
||||
impl<'a> IntoIterator for &'a BorrowedBytes<'_> {
|
||||
type Item = &'a u8;
|
||||
type IntoIter = slice::Iter<'a, u8>;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.0.iter()
|
||||
self.iter()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a String> for BorrowedBytes<'a> {
|
||||
#[inline]
|
||||
fn from(value: &'a String) -> Self {
|
||||
let (buf, _lua) = unsafe { value.to_slice() };
|
||||
let borrow = Cow::Borrowed(value);
|
||||
Self { buf, borrow, _lua }
|
||||
}
|
||||
}
|
||||
|
||||
struct WrappedString<T: AsRef<[u8]>>(T);
|
||||
|
||||
impl String {
|
||||
/// Wraps bytes, returning an opaque type that implements [`IntoLua`] trait.
|
||||
///
|
||||
/// This function uses [`Lua::create_string`] under the hood.
|
||||
pub fn wrap(data: impl AsRef<[u8]>) -> impl IntoLua {
|
||||
WrappedString(data)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: AsRef<[u8]>> IntoLua for WrappedString<T> {
|
||||
fn into_lua(self, lua: &Lua) -> Result<Value> {
|
||||
lua.create_string(self.0).map(Value::String)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+28
-17
@@ -468,26 +468,16 @@ impl Table {
|
||||
///
|
||||
/// It checks both the array part and the hash part.
|
||||
pub fn is_empty(&self) -> bool {
|
||||
// Check array part
|
||||
if self.raw_len() != 0 {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check hash part
|
||||
let lua = self.0.lua.lock();
|
||||
let state = lua.state();
|
||||
let ref_thread = lua.ref_thread();
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(state);
|
||||
assert_stack(state, 4);
|
||||
|
||||
lua.push_ref(&self.0);
|
||||
ffi::lua_pushnil(state);
|
||||
if ffi::lua_next(state, -2) != 0 {
|
||||
return false;
|
||||
ffi::lua_pushnil(ref_thread);
|
||||
if ffi::lua_next(ref_thread, self.0.index) == 0 {
|
||||
return true;
|
||||
}
|
||||
ffi::lua_pop(ref_thread, 2);
|
||||
}
|
||||
|
||||
true
|
||||
false
|
||||
}
|
||||
|
||||
/// Returns a reference to the metatable of this table, or `None` if no metatable is set.
|
||||
@@ -581,6 +571,24 @@ impl Table {
|
||||
unsafe { ffi::lua_getreadonly(ref_thread, self.0.index) != 0 }
|
||||
}
|
||||
|
||||
/// Controls `safeenv` attribute on the table.
|
||||
///
|
||||
/// This a special flag that activates some performance optimizations for environment tables.
|
||||
/// In particular, it controls:
|
||||
/// - Optimization of import resolution (cache values of constant keys).
|
||||
/// - Fast-path for built-in iteration with pairs/ipairs.
|
||||
/// - Fast-path for some built-in functions (fastcall).
|
||||
///
|
||||
/// For `safeenv` environments, monkey patching or modifying values may not work as expected.
|
||||
///
|
||||
/// Requires `feature = "luau"`
|
||||
#[cfg(any(feature = "luau", doc))]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
|
||||
pub fn set_safeenv(&self, enabled: bool) {
|
||||
let lua = self.0.lua.lock();
|
||||
unsafe { ffi::lua_setsafeenv(lua.ref_thread(), self.0.index, enabled as _) };
|
||||
}
|
||||
|
||||
/// Converts this table to a generic C pointer.
|
||||
///
|
||||
/// Different tables will give different pointers.
|
||||
@@ -1012,7 +1020,10 @@ impl Serialize for SerializableTable<'_> {
|
||||
|
||||
// Array
|
||||
let len = self.table.raw_len();
|
||||
if len > 0 || self.table.is_array() {
|
||||
if len > 0
|
||||
|| self.table.is_array()
|
||||
|| (self.options.encode_empty_tables_as_array && self.table.is_empty())
|
||||
{
|
||||
let mut seq = serializer.serialize_seq(Some(len))?;
|
||||
let mut serialize_err = None;
|
||||
let res = self.table.for_each_value::<Value>(|value| {
|
||||
|
||||
+67
-50
@@ -2,8 +2,7 @@ use std::fmt;
|
||||
use std::os::raw::{c_int, c_void};
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
#[allow(unused)]
|
||||
use crate::state::Lua;
|
||||
use crate::function::Function;
|
||||
use crate::state::RawLua;
|
||||
use crate::traits::{FromLuaMulti, IntoLuaMulti};
|
||||
use crate::types::{LuaType, ValueRef};
|
||||
@@ -42,6 +41,26 @@ pub enum ThreadStatus {
|
||||
Error,
|
||||
}
|
||||
|
||||
/// Internal representation of a Lua thread status.
|
||||
///
|
||||
/// The number in `New` and `Yielded` variants is the number of arguments pushed
|
||||
/// to the thread stack.
|
||||
#[derive(Clone, Copy)]
|
||||
enum ThreadStatusInner {
|
||||
New,
|
||||
Running,
|
||||
Yielded,
|
||||
Finished,
|
||||
Error,
|
||||
}
|
||||
|
||||
impl ThreadStatusInner {
|
||||
#[inline(always)]
|
||||
fn is_resumable(self) -> bool {
|
||||
matches!(self, ThreadStatusInner::New | ThreadStatusInner::Yielded)
|
||||
}
|
||||
}
|
||||
|
||||
/// Handle to an internal Lua thread (coroutine).
|
||||
#[derive(Clone)]
|
||||
pub struct Thread(pub(crate) ValueRef, pub(crate) *mut ffi::lua_State);
|
||||
@@ -122,7 +141,7 @@ impl Thread {
|
||||
R: FromLuaMulti,
|
||||
{
|
||||
let lua = self.0.lua.lock();
|
||||
if self.status_inner(&lua) != ThreadStatus::Resumable {
|
||||
if !self.status_inner(&lua).is_resumable() {
|
||||
return Err(Error::CoroutineUnresumable);
|
||||
}
|
||||
|
||||
@@ -170,23 +189,27 @@ impl Thread {
|
||||
|
||||
/// Gets the status of the thread.
|
||||
pub fn status(&self) -> ThreadStatus {
|
||||
self.status_inner(&self.0.lua.lock())
|
||||
match self.status_inner(&self.0.lua.lock()) {
|
||||
ThreadStatusInner::New | ThreadStatusInner::Yielded => ThreadStatus::Resumable,
|
||||
ThreadStatusInner::Running => ThreadStatus::Running,
|
||||
ThreadStatusInner::Finished => ThreadStatus::Finished,
|
||||
ThreadStatusInner::Error => ThreadStatus::Error,
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets the status of the thread (internal implementation).
|
||||
pub(crate) fn status_inner(&self, lua: &RawLua) -> ThreadStatus {
|
||||
fn status_inner(&self, lua: &RawLua) -> ThreadStatusInner {
|
||||
let thread_state = self.state();
|
||||
if thread_state == lua.state() {
|
||||
// The thread is currently running
|
||||
return ThreadStatus::Running;
|
||||
return ThreadStatusInner::Running;
|
||||
}
|
||||
let status = unsafe { ffi::lua_status(thread_state) };
|
||||
if status != ffi::LUA_OK && status != ffi::LUA_YIELD {
|
||||
ThreadStatus::Error
|
||||
} else if status == ffi::LUA_YIELD || unsafe { ffi::lua_gettop(thread_state) > 0 } {
|
||||
ThreadStatus::Resumable
|
||||
} else {
|
||||
ThreadStatus::Finished
|
||||
match status {
|
||||
ffi::LUA_YIELD => ThreadStatusInner::Yielded,
|
||||
ffi::LUA_OK if unsafe { ffi::lua_gettop(thread_state) } > 0 => ThreadStatusInner::New,
|
||||
ffi::LUA_OK => ThreadStatusInner::Finished,
|
||||
_ => ThreadStatusInner::Error,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -198,7 +221,7 @@ impl Thread {
|
||||
#[cfg_attr(docsrs, doc(cfg(not(feature = "luau"))))]
|
||||
pub fn set_hook<F>(&self, triggers: HookTriggers, callback: F)
|
||||
where
|
||||
F: Fn(&Lua, Debug) -> Result<crate::VmState> + MaybeSend + 'static,
|
||||
F: Fn(&crate::Lua, Debug) -> Result<crate::VmState> + MaybeSend + 'static,
|
||||
{
|
||||
let lua = self.0.lua.lock();
|
||||
unsafe {
|
||||
@@ -215,32 +238,37 @@ impl Thread {
|
||||
/// In Luau: resets to the initial state of a newly created Lua thread.
|
||||
/// Lua threads in arbitrary states (like yielded or errored) can be reset properly.
|
||||
///
|
||||
/// Other Lua versions can reset only new or finished threads.
|
||||
///
|
||||
/// Sets a Lua function for the thread afterwards.
|
||||
///
|
||||
/// Requires `feature = "lua54"` OR `feature = "luau"`.
|
||||
///
|
||||
/// [Lua 5.4]: https://www.lua.org/manual/5.4/manual.html#lua_closethread
|
||||
#[cfg(any(feature = "lua54", feature = "luau"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(any(feature = "lua54", feature = "luau"))))]
|
||||
pub fn reset(&self, func: crate::function::Function) -> Result<()> {
|
||||
pub fn reset(&self, func: Function) -> Result<()> {
|
||||
let lua = self.0.lua.lock();
|
||||
if self.status_inner(&lua) == ThreadStatus::Running {
|
||||
return Err(Error::runtime("cannot reset a running thread"));
|
||||
let thread_state = self.state();
|
||||
match self.status_inner(&lua) {
|
||||
ThreadStatusInner::Running => return Err(Error::runtime("cannot reset a running thread")),
|
||||
// Any Lua can reuse new or finished thread
|
||||
ThreadStatusInner::New => unsafe { ffi::lua_settop(thread_state, 0) },
|
||||
ThreadStatusInner::Finished => {}
|
||||
#[cfg(not(any(feature = "lua54", feature = "luau")))]
|
||||
_ => return Err(Error::runtime("cannot reset non-finished thread")),
|
||||
#[cfg(any(feature = "lua54", feature = "luau"))]
|
||||
_ => unsafe {
|
||||
#[cfg(all(feature = "lua54", not(feature = "vendored")))]
|
||||
let status = ffi::lua_resetthread(thread_state);
|
||||
#[cfg(all(feature = "lua54", feature = "vendored"))]
|
||||
let status = ffi::lua_closethread(thread_state, lua.state());
|
||||
#[cfg(feature = "lua54")]
|
||||
if status != ffi::LUA_OK {
|
||||
return Err(pop_error(thread_state, status));
|
||||
}
|
||||
#[cfg(feature = "luau")]
|
||||
ffi::lua_resetthread(thread_state);
|
||||
},
|
||||
}
|
||||
|
||||
let thread_state = self.state();
|
||||
unsafe {
|
||||
#[cfg(all(feature = "lua54", not(feature = "vendored")))]
|
||||
let status = ffi::lua_resetthread(thread_state);
|
||||
#[cfg(all(feature = "lua54", feature = "vendored"))]
|
||||
let status = ffi::lua_closethread(thread_state, lua.state());
|
||||
#[cfg(feature = "lua54")]
|
||||
if status != ffi::LUA_OK {
|
||||
return Err(pop_error(thread_state, status));
|
||||
}
|
||||
#[cfg(feature = "luau")]
|
||||
ffi::lua_resetthread(thread_state);
|
||||
|
||||
// Push function to the top of the thread stack
|
||||
ffi::lua_xpush(lua.ref_thread(), thread_state, func.0.index);
|
||||
|
||||
@@ -338,7 +366,7 @@ impl Thread {
|
||||
/// Ok(())
|
||||
/// })?)?;
|
||||
/// thread.sandbox()?;
|
||||
/// thread.resume(())?;
|
||||
/// thread.resume::<()>(())?;
|
||||
///
|
||||
/// // The global environment should be unchanged
|
||||
/// assert_eq!(lua.globals().get::<Option<u32>>("var")?, None);
|
||||
@@ -393,30 +421,19 @@ impl LuaType for Thread {
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
impl<A, R> AsyncThread<A, R> {
|
||||
#[inline]
|
||||
#[inline(always)]
|
||||
pub(crate) fn set_recyclable(&mut self, recyclable: bool) {
|
||||
self.recycle = recyclable;
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
#[cfg(any(feature = "lua54", feature = "luau"))]
|
||||
impl<A, R> Drop for AsyncThread<A, R> {
|
||||
fn drop(&mut self) {
|
||||
if self.recycle {
|
||||
if let Some(lua) = self.thread.0.lua.try_lock() {
|
||||
unsafe {
|
||||
// For Lua 5.4 this also closes all pending to-be-closed variables
|
||||
if !lua.recycle_thread(&mut self.thread) {
|
||||
#[cfg(feature = "lua54")]
|
||||
if self.thread.status_inner(&lua) == ThreadStatus::Error {
|
||||
#[cfg(not(feature = "vendored"))]
|
||||
ffi::lua_resetthread(self.thread.state());
|
||||
#[cfg(feature = "vendored")]
|
||||
ffi::lua_closethread(self.thread.state(), lua.state());
|
||||
}
|
||||
}
|
||||
}
|
||||
// For Lua 5.4 this also closes all pending to-be-closed variables
|
||||
unsafe { lua.recycle_thread(&mut self.thread) };
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -428,7 +445,7 @@ impl<A: IntoLuaMulti, R: FromLuaMulti> Stream for AsyncThread<A, R> {
|
||||
|
||||
fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
|
||||
let lua = self.thread.0.lua.lock();
|
||||
if self.thread.status_inner(&lua) != ThreadStatus::Resumable {
|
||||
if !self.thread.status_inner(&lua).is_resumable() {
|
||||
return Poll::Ready(None);
|
||||
}
|
||||
|
||||
@@ -466,7 +483,7 @@ impl<A: IntoLuaMulti, R: FromLuaMulti> Future for AsyncThread<A, R> {
|
||||
|
||||
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
let lua = self.thread.0.lua.lock();
|
||||
if self.thread.status_inner(&lua) != ThreadStatus::Resumable {
|
||||
if !self.thread.status_inner(&lua).is_resumable() {
|
||||
return Poll::Ready(Err(Error::CoroutineUnresumable));
|
||||
}
|
||||
|
||||
@@ -506,7 +523,7 @@ impl<A: IntoLuaMulti, R: FromLuaMulti> Future for AsyncThread<A, R> {
|
||||
#[cfg(feature = "async")]
|
||||
#[inline(always)]
|
||||
unsafe fn is_poll_pending(state: *mut ffi::lua_State) -> bool {
|
||||
ffi::lua_tolightuserdata(state, -1) == Lua::poll_pending().0
|
||||
ffi::lua_tolightuserdata(state, -1) == crate::Lua::poll_pending().0
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
|
||||
+2
-2
@@ -86,10 +86,10 @@ pub(crate) type InterruptCallback = Rc<dyn Fn(&Lua) -> Result<VmState> + Send>;
|
||||
pub(crate) type InterruptCallback = Rc<dyn Fn(&Lua) -> Result<VmState>>;
|
||||
|
||||
#[cfg(all(feature = "send", feature = "lua54"))]
|
||||
pub(crate) type WarnCallback = Box<dyn Fn(&Lua, &str, bool) -> Result<()> + Send>;
|
||||
pub(crate) type WarnCallback = XRc<dyn Fn(&Lua, &str, bool) -> Result<()> + Send>;
|
||||
|
||||
#[cfg(all(not(feature = "send"), feature = "lua54"))]
|
||||
pub(crate) type WarnCallback = Box<dyn Fn(&Lua, &str, bool) -> Result<()>>;
|
||||
pub(crate) type WarnCallback = XRc<dyn Fn(&Lua, &str, bool) -> Result<()>>;
|
||||
|
||||
/// A trait that adds `Send` requirement if `send` feature is enabled.
|
||||
#[cfg(feature = "send")]
|
||||
|
||||
@@ -4,7 +4,7 @@ use std::os::raw::{c_int, c_void};
|
||||
use crate::state::{RawLua, WeakLua};
|
||||
|
||||
/// A reference to a Lua (complex) value stored in the Lua auxiliary thread.
|
||||
pub(crate) struct ValueRef {
|
||||
pub struct ValueRef {
|
||||
pub(crate) lua: WeakLua,
|
||||
pub(crate) index: c_int,
|
||||
pub(crate) drop: bool,
|
||||
|
||||
+37
-29
@@ -26,9 +26,13 @@ use {
|
||||
|
||||
// Re-export for convenience
|
||||
pub(crate) use cell::UserDataStorage;
|
||||
pub use cell::{UserDataRef, UserDataRefMut};
|
||||
pub(crate) use registry::UserDataProxy;
|
||||
pub use r#ref::{UserDataRef, UserDataRefMut};
|
||||
pub use registry::UserDataRegistry;
|
||||
pub(crate) use registry::{RawUserDataRegistry, UserDataProxy};
|
||||
pub(crate) use util::{
|
||||
borrow_userdata_scoped, borrow_userdata_scoped_mut, collect_userdata, init_userdata_metatable,
|
||||
TypeIdHints,
|
||||
};
|
||||
|
||||
/// Kinds of metamethods that can be overridden.
|
||||
///
|
||||
@@ -622,7 +626,9 @@ impl AnyUserData {
|
||||
/// Checks whether the type of this userdata is `T`.
|
||||
#[inline]
|
||||
pub fn is<T: 'static>(&self) -> bool {
|
||||
self.inspect::<T, _, _>(|_| Ok(())).is_ok()
|
||||
let type_id = self.type_id();
|
||||
// We do not use wrapped types here, rather prefer to check the "real" type of the userdata
|
||||
matches!(type_id, Some(type_id) if type_id == TypeId::of::<T>())
|
||||
}
|
||||
|
||||
/// Borrow this userdata immutably if it is of type `T`.
|
||||
@@ -637,7 +643,8 @@ impl AnyUserData {
|
||||
/// [`DataTypeMismatch`]: crate::Error::UserDataTypeMismatch
|
||||
#[inline]
|
||||
pub fn borrow<T: 'static>(&self) -> Result<UserDataRef<T>> {
|
||||
self.inspect(|ud| ud.try_borrow_owned())
|
||||
let lua = self.0.lua.lock();
|
||||
unsafe { UserDataRef::borrow_from_stack(&lua, lua.ref_thread(), self.0.index) }
|
||||
}
|
||||
|
||||
/// Borrow this userdata immutably if it is of type `T`, passing the borrowed value
|
||||
@@ -645,7 +652,10 @@ impl AnyUserData {
|
||||
///
|
||||
/// This method is the only way to borrow scoped userdata (created inside [`Lua::scope`]).
|
||||
pub fn borrow_scoped<T: 'static, R>(&self, f: impl FnOnce(&T) -> R) -> Result<R> {
|
||||
self.inspect(|ud| ud.try_borrow_scoped(|ud| f(ud)))
|
||||
let lua = self.0.lua.lock();
|
||||
let type_id = lua.get_userdata_ref_type_id(&self.0)?;
|
||||
let type_hints = TypeIdHints::new::<T>();
|
||||
unsafe { borrow_userdata_scoped(lua.ref_thread(), self.0.index, type_id, type_hints, f) }
|
||||
}
|
||||
|
||||
/// Borrow this userdata mutably if it is of type `T`.
|
||||
@@ -660,7 +670,8 @@ impl AnyUserData {
|
||||
/// [`UserDataTypeMismatch`]: crate::Error::UserDataTypeMismatch
|
||||
#[inline]
|
||||
pub fn borrow_mut<T: 'static>(&self) -> Result<UserDataRefMut<T>> {
|
||||
self.inspect(|ud| ud.try_borrow_owned_mut())
|
||||
let lua = self.0.lua.lock();
|
||||
unsafe { UserDataRefMut::borrow_from_stack(&lua, lua.ref_thread(), self.0.index) }
|
||||
}
|
||||
|
||||
/// Borrow this userdata mutably if it is of type `T`, passing the borrowed value
|
||||
@@ -668,7 +679,10 @@ impl AnyUserData {
|
||||
///
|
||||
/// This method is the only way to borrow scoped userdata (created inside [`Lua::scope`]).
|
||||
pub fn borrow_mut_scoped<T: 'static, R>(&self, f: impl FnOnce(&mut T) -> R) -> Result<R> {
|
||||
self.inspect(|ud| ud.try_borrow_scoped_mut(|ud| f(ud)))
|
||||
let lua = self.0.lua.lock();
|
||||
let type_id = lua.get_userdata_ref_type_id(&self.0)?;
|
||||
let type_hints = TypeIdHints::new::<T>();
|
||||
unsafe { borrow_userdata_scoped_mut(lua.ref_thread(), self.0.index, type_id, type_hints, f) }
|
||||
}
|
||||
|
||||
/// Takes the value out of this userdata.
|
||||
@@ -687,9 +701,11 @@ impl AnyUserData {
|
||||
let type_id = lua.push_userdata_ref(&self.0)?;
|
||||
match type_id {
|
||||
Some(type_id) if type_id == TypeId::of::<T>() => {
|
||||
// Try to borrow userdata exclusively
|
||||
let _ = (*get_userdata::<UserDataStorage<T>>(state, -1)).try_borrow_mut()?;
|
||||
take_userdata::<UserDataStorage<T>>(state).into_inner()
|
||||
if (*get_userdata::<UserDataStorage<T>>(state, -1)).has_exclusive_access() {
|
||||
take_userdata::<UserDataStorage<T>>(state).into_inner()
|
||||
} else {
|
||||
Err(Error::UserDataBorrowMutError)
|
||||
}
|
||||
}
|
||||
_ => Err(Error::UserDataTypeMismatch),
|
||||
}
|
||||
@@ -910,6 +926,15 @@ impl AnyUserData {
|
||||
self.0.to_pointer()
|
||||
}
|
||||
|
||||
/// Returns [`TypeId`] of this userdata if it is registered and `'static`.
|
||||
///
|
||||
/// This method is not available for scoped userdata.
|
||||
#[inline]
|
||||
pub fn type_id(&self) -> Option<TypeId> {
|
||||
let lua = self.0.lua.lock();
|
||||
lua.get_userdata_ref_type_id(&self.0).ok().flatten()
|
||||
}
|
||||
|
||||
/// Returns a type name of this `UserData` (from a metatable field).
|
||||
pub(crate) fn type_name(&self) -> Result<Option<StdString>> {
|
||||
let lua = self.0.lua.lock();
|
||||
@@ -965,24 +990,6 @@ impl AnyUserData {
|
||||
};
|
||||
is_serializable().unwrap_or(false)
|
||||
}
|
||||
|
||||
pub(crate) fn inspect<T, F, R>(&self, func: F) -> Result<R>
|
||||
where
|
||||
T: 'static,
|
||||
F: FnOnce(&UserDataStorage<T>) -> Result<R>,
|
||||
{
|
||||
let lua = self.0.lua.lock();
|
||||
unsafe {
|
||||
let type_id = lua.get_userdata_ref_type_id(&self.0)?;
|
||||
match type_id {
|
||||
Some(type_id) if type_id == TypeId::of::<T>() => {
|
||||
let ud = get_userdata::<UserDataStorage<T>>(lua.ref_thread(), self.0.index);
|
||||
func(&*ud)
|
||||
}
|
||||
_ => Err(Error::UserDataTypeMismatch),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Handle to a [`AnyUserData`] metatable.
|
||||
@@ -1073,7 +1080,7 @@ impl Serialize for AnyUserData {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct WrappedUserdata<F: FnOnce(&Lua) -> Result<AnyUserData>>(F);
|
||||
struct WrappedUserdata<F: FnOnce(&Lua) -> Result<AnyUserData>>(F);
|
||||
|
||||
impl AnyUserData {
|
||||
/// Wraps any Rust type, returning an opaque type that implements [`IntoLua`] trait.
|
||||
@@ -1106,6 +1113,7 @@ where
|
||||
mod cell;
|
||||
mod lock;
|
||||
mod object;
|
||||
mod r#ref;
|
||||
mod registry;
|
||||
mod util;
|
||||
|
||||
|
||||
+71
-325
@@ -1,22 +1,13 @@
|
||||
use std::any::{type_name, TypeId};
|
||||
use std::cell::{Cell, RefCell, UnsafeCell};
|
||||
use std::fmt;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use std::os::raw::c_int;
|
||||
use std::cell::{RefCell, UnsafeCell};
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
use serde::ser::{Serialize, Serializer};
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
use crate::state::{Lua, RawLua};
|
||||
use crate::traits::FromLua;
|
||||
use crate::types::XRc;
|
||||
use crate::userdata::AnyUserData;
|
||||
use crate::util::get_userdata;
|
||||
use crate::value::Value;
|
||||
|
||||
use super::lock::{RawLock, UserDataLock};
|
||||
use super::util::is_sync;
|
||||
use super::r#ref::{UserDataRef, UserDataRefMut};
|
||||
|
||||
#[cfg(all(feature = "serialize", not(feature = "send")))]
|
||||
type DynSerialize = dyn erased_serde::Serialize;
|
||||
@@ -34,7 +25,7 @@ pub(crate) enum UserDataStorage<T> {
|
||||
pub(crate) enum UserDataVariant<T> {
|
||||
Default(XRc<UserDataCell<T>>),
|
||||
#[cfg(feature = "serialize")]
|
||||
Serializable(XRc<UserDataCell<Box<DynSerialize>>>),
|
||||
Serializable(XRc<UserDataCell<Box<DynSerialize>>>, bool), // bool is `is_sync`
|
||||
}
|
||||
|
||||
impl<T> Clone for UserDataVariant<T> {
|
||||
@@ -43,16 +34,28 @@ impl<T> Clone for UserDataVariant<T> {
|
||||
match self {
|
||||
Self::Default(inner) => Self::Default(XRc::clone(inner)),
|
||||
#[cfg(feature = "serialize")]
|
||||
Self::Serializable(inner) => Self::Serializable(XRc::clone(inner)),
|
||||
Self::Serializable(inner, is_sync) => Self::Serializable(XRc::clone(inner), *is_sync),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> UserDataVariant<T> {
|
||||
// Immutably borrows the wrapped value in-place.
|
||||
#[inline(always)]
|
||||
fn try_borrow(&self) -> Result<UserDataBorrowRef<T>> {
|
||||
UserDataBorrowRef::try_from(self)
|
||||
pub(super) fn try_borrow_scoped<R>(&self, f: impl FnOnce(&T) -> R) -> Result<R> {
|
||||
// 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.
|
||||
let _guard = (self.raw_lock().try_lock_shared_guarded()).map_err(|_| Error::UserDataBorrowError)?;
|
||||
Ok(f(unsafe { &*self.as_ptr() }))
|
||||
}
|
||||
|
||||
// Mutably borrows the wrapped value in-place.
|
||||
#[inline(always)]
|
||||
fn try_borrow_scoped_mut<R>(&self, f: impl FnOnce(&mut T) -> R) -> Result<R> {
|
||||
let _guard =
|
||||
(self.raw_lock().try_lock_exclusive_guarded()).map_err(|_| Error::UserDataBorrowMutError)?;
|
||||
Ok(f(unsafe { &mut *self.as_ptr() }))
|
||||
}
|
||||
|
||||
// Immutably borrows the wrapped value and returns an owned reference.
|
||||
@@ -61,12 +64,6 @@ impl<T> UserDataVariant<T> {
|
||||
UserDataRef::try_from(self.clone())
|
||||
}
|
||||
|
||||
// Mutably borrows the wrapped value in-place.
|
||||
#[inline(always)]
|
||||
fn try_borrow_mut(&self) -> Result<UserDataBorrowMut<T>> {
|
||||
UserDataBorrowMut::try_from(self)
|
||||
}
|
||||
|
||||
// Mutably borrows the wrapped value and returns an owned reference.
|
||||
#[inline(always)]
|
||||
fn try_borrow_owned_mut(&self) -> Result<UserDataRefMut<T>> {
|
||||
@@ -83,7 +80,7 @@ impl<T> UserDataVariant<T> {
|
||||
Ok(match self {
|
||||
Self::Default(inner) => XRc::into_inner(inner).unwrap().value.into_inner(),
|
||||
#[cfg(feature = "serialize")]
|
||||
Self::Serializable(inner) => unsafe {
|
||||
Self::Serializable(inner, _) => unsafe {
|
||||
let raw = Box::into_raw(XRc::into_inner(inner).unwrap().value.into_inner());
|
||||
*Box::from_raw(raw as *mut T)
|
||||
},
|
||||
@@ -91,29 +88,29 @@ impl<T> UserDataVariant<T> {
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn raw_lock(&self) -> &RawLock {
|
||||
fn strong_count(&self) -> usize {
|
||||
match self {
|
||||
Self::Default(inner) => XRc::strong_count(inner),
|
||||
#[cfg(feature = "serialize")]
|
||||
Self::Serializable(inner, _) => XRc::strong_count(inner),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub(super) fn raw_lock(&self) -> &RawLock {
|
||||
match self {
|
||||
Self::Default(inner) => &inner.raw_lock,
|
||||
#[cfg(feature = "serialize")]
|
||||
Self::Serializable(inner) => &inner.raw_lock,
|
||||
Self::Serializable(inner, _) => &inner.raw_lock,
|
||||
}
|
||||
}
|
||||
|
||||
#[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 {
|
||||
pub(super) fn as_ptr(&self) -> *mut T {
|
||||
match self {
|
||||
Self::Default(inner) => inner.value.get(),
|
||||
#[cfg(feature = "serialize")]
|
||||
Self::Serializable(inner) => unsafe { &mut **(inner.value.get() as *mut Box<T>) },
|
||||
Self::Serializable(inner, _) => unsafe { &mut **(inner.value.get() as *mut Box<T>) },
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -122,14 +119,24 @@ impl<T> UserDataVariant<T> {
|
||||
impl Serialize for UserDataStorage<()> {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error> {
|
||||
match self {
|
||||
Self::Owned(UserDataVariant::Serializable(inner)) => unsafe {
|
||||
// We need to borrow the inner value exclusively to serialize it.
|
||||
Self::Owned(variant @ UserDataVariant::Serializable(inner, is_sync)) => unsafe {
|
||||
#[cfg(feature = "send")]
|
||||
let _guard = self.try_borrow_mut().map_err(serde::ser::Error::custom)?;
|
||||
// No need to do this if the `send` feature is disabled.
|
||||
if *is_sync {
|
||||
let _guard = (variant.raw_lock().try_lock_shared_guarded())
|
||||
.map_err(|_| serde::ser::Error::custom(Error::UserDataBorrowError))?;
|
||||
(*inner.value.get()).serialize(serializer)
|
||||
} else {
|
||||
let _guard = (variant.raw_lock().try_lock_exclusive_guarded())
|
||||
.map_err(|_| serde::ser::Error::custom(Error::UserDataBorrowError))?;
|
||||
(*inner.value.get()).serialize(serializer)
|
||||
}
|
||||
#[cfg(not(feature = "send"))]
|
||||
let _guard = self.try_borrow().map_err(serde::ser::Error::custom)?;
|
||||
(*inner.value.get()).serialize(serializer)
|
||||
{
|
||||
let _ = is_sync;
|
||||
let _guard = (variant.raw_lock().try_lock_shared_guarded())
|
||||
.map_err(|_| serde::ser::Error::custom(Error::UserDataBorrowError))?;
|
||||
(*inner.value.get()).serialize(serializer)
|
||||
}
|
||||
},
|
||||
_ => Err(serde::ser::Error::custom("cannot serialize <userdata>")),
|
||||
}
|
||||
@@ -139,7 +146,6 @@ 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>,
|
||||
}
|
||||
|
||||
@@ -153,242 +159,11 @@ impl<T> UserDataCell<T> {
|
||||
fn new(value: T) -> Self {
|
||||
UserDataCell {
|
||||
raw_lock: RawLock::INIT,
|
||||
borrow_count: Cell::new(0),
|
||||
value: UnsafeCell::new(value),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A wrapper type for a userdata value that provides read access.
|
||||
///
|
||||
/// It implements [`FromLua`] and can be used to receive a typed userdata from Lua.
|
||||
pub struct UserDataRef<T>(UserDataVariant<T>);
|
||||
|
||||
impl<T> Deref for UserDataRef<T> {
|
||||
type Target = T;
|
||||
|
||||
#[inline]
|
||||
fn deref(&self) -> &T {
|
||||
unsafe { &*self.0.as_ptr() }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Drop for UserDataRef<T> {
|
||||
#[inline]
|
||||
fn drop(&mut self) {
|
||||
if !cfg!(feature = "send") || is_sync::<T>() {
|
||||
unsafe { self.0.raw_lock().unlock_shared() };
|
||||
} else {
|
||||
unsafe { self.0.raw_lock().unlock_exclusive() };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: fmt::Debug> fmt::Debug for UserDataRef<T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
(**self).fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: fmt::Display> fmt::Display for UserDataRef<T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
(**self).fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> TryFrom<UserDataVariant<T>> for UserDataRef<T> {
|
||||
type Error = Error;
|
||||
|
||||
#[inline]
|
||||
fn try_from(variant: UserDataVariant<T>) -> Result<Self> {
|
||||
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))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: 'static> FromLua for UserDataRef<T> {
|
||||
fn from_lua(value: Value, _: &Lua) -> Result<Self> {
|
||||
try_value_to_userdata::<T>(value)?.borrow()
|
||||
}
|
||||
|
||||
unsafe fn from_stack(idx: c_int, lua: &RawLua) -> Result<Self> {
|
||||
let type_id = lua.get_userdata_type_id::<T>(idx)?;
|
||||
match type_id {
|
||||
Some(type_id) if type_id == TypeId::of::<T>() => {
|
||||
(*get_userdata::<UserDataStorage<T>>(lua.state(), idx)).try_borrow_owned()
|
||||
}
|
||||
_ => Err(Error::UserDataTypeMismatch),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A wrapper type for a userdata value that provides read and write access.
|
||||
///
|
||||
/// It implements [`FromLua`] and can be used to receive a typed userdata from Lua.
|
||||
pub struct UserDataRefMut<T>(UserDataVariant<T>);
|
||||
|
||||
impl<T> Deref for UserDataRefMut<T> {
|
||||
type Target = T;
|
||||
|
||||
#[inline]
|
||||
fn deref(&self) -> &Self::Target {
|
||||
unsafe { &*self.0.as_ptr() }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> DerefMut for UserDataRefMut<T> {
|
||||
#[inline]
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
unsafe { &mut *self.0.as_ptr() }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Drop for UserDataRefMut<T> {
|
||||
#[inline]
|
||||
fn drop(&mut self) {
|
||||
unsafe { self.0.raw_lock().unlock_exclusive() };
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: fmt::Debug> fmt::Debug for UserDataRefMut<T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
(**self).fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: fmt::Display> fmt::Display for UserDataRefMut<T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
(**self).fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> TryFrom<UserDataVariant<T>> for UserDataRefMut<T> {
|
||||
type Error = Error;
|
||||
|
||||
#[inline]
|
||||
fn try_from(variant: UserDataVariant<T>) -> Result<Self> {
|
||||
if !variant.raw_lock().try_lock_exclusive() {
|
||||
return Err(Error::UserDataBorrowMutError);
|
||||
}
|
||||
Ok(UserDataRefMut(variant))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: 'static> FromLua for UserDataRefMut<T> {
|
||||
fn from_lua(value: Value, _: &Lua) -> Result<Self> {
|
||||
try_value_to_userdata::<T>(value)?.borrow_mut()
|
||||
}
|
||||
|
||||
unsafe fn from_stack(idx: c_int, lua: &RawLua) -> Result<Self> {
|
||||
let type_id = lua.get_userdata_type_id::<T>(idx)?;
|
||||
match type_id {
|
||||
Some(type_id) if type_id == TypeId::of::<T>() => {
|
||||
(*get_userdata::<UserDataStorage<T>>(lua.state(), idx)).try_borrow_owned_mut()
|
||||
}
|
||||
_ => Err(Error::UserDataTypeMismatch),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A type that provides read access to a userdata value (borrowing the value).
|
||||
pub(crate) struct UserDataBorrowRef<'a, T>(&'a UserDataVariant<T>);
|
||||
|
||||
impl<T> Drop for UserDataBorrowRef<'_, T> {
|
||||
#[inline]
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
self.0.borrow_count().set(self.0.borrow_count().get() - 1);
|
||||
self.0.raw_lock().unlock_shared();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Deref for UserDataBorrowRef<'_, T> {
|
||||
type Target = T;
|
||||
|
||||
#[inline]
|
||||
fn deref(&self) -> &T {
|
||||
// SAFETY: `UserDataBorrowRef` is only created with shared access to the value.
|
||||
unsafe { &*self.0.as_ptr() }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> TryFrom<&'a UserDataVariant<T>> for UserDataBorrowRef<'a, T> {
|
||||
type Error = Error;
|
||||
|
||||
#[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))
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct UserDataBorrowMut<'a, T>(&'a UserDataVariant<T>);
|
||||
|
||||
impl<T> Drop for UserDataBorrowMut<'_, T> {
|
||||
#[inline]
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
self.0.borrow_count().set(self.0.borrow_count().get() - 1);
|
||||
self.0.raw_lock().unlock_exclusive();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Deref for UserDataBorrowMut<'_, T> {
|
||||
type Target = T;
|
||||
|
||||
#[inline]
|
||||
fn deref(&self) -> &T {
|
||||
unsafe { &*self.0.as_ptr() }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> DerefMut for UserDataBorrowMut<'_, T> {
|
||||
#[inline]
|
||||
fn deref_mut(&mut self) -> &mut T {
|
||||
unsafe { &mut *self.0.as_ptr() }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> TryFrom<&'a UserDataVariant<T>> for UserDataBorrowMut<'a, T> {
|
||||
type Error = Error;
|
||||
|
||||
#[inline(always)]
|
||||
fn try_from(variant: &'a UserDataVariant<T>) -> Result<Self> {
|
||||
if !variant.raw_lock().try_lock_exclusive() {
|
||||
return Err(Error::UserDataBorrowMutError);
|
||||
}
|
||||
variant.borrow_count().set(variant.borrow_count().get() + 1);
|
||||
Ok(UserDataBorrowMut(variant))
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn try_value_to_userdata<T>(value: Value) -> Result<AnyUserData> {
|
||||
match value {
|
||||
Value::UserData(ud) => Ok(ud),
|
||||
_ => Err(Error::FromLuaConversionError {
|
||||
from: value.type_name(),
|
||||
to: "userdata".to_string(),
|
||||
message: Some(format!("expected userdata of type {}", type_name::<T>())),
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) enum ScopedUserDataVariant<T> {
|
||||
Ref(*const T),
|
||||
RefMut(RefCell<*mut T>),
|
||||
@@ -429,13 +204,15 @@ impl<T: 'static> UserDataStorage<T> {
|
||||
T: Serialize + crate::types::MaybeSend,
|
||||
{
|
||||
let data = Box::new(data) as Box<DynSerialize>;
|
||||
Self::Owned(UserDataVariant::Serializable(XRc::new(UserDataCell::new(data))))
|
||||
let is_sync = super::util::is_sync::<T>();
|
||||
let variant = UserDataVariant::Serializable(XRc::new(UserDataCell::new(data)), is_sync);
|
||||
Self::Owned(variant)
|
||||
}
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
#[inline(always)]
|
||||
pub(crate) fn is_serializable(&self) -> bool {
|
||||
matches!(self, Self::Owned(UserDataVariant::Serializable(_)))
|
||||
matches!(self, Self::Owned(UserDataVariant::Serializable(..)))
|
||||
}
|
||||
|
||||
// Immutably borrows the wrapped value and returns an owned reference.
|
||||
@@ -447,23 +224,6 @@ impl<T: 'static> UserDataStorage<T> {
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
#[inline(always)]
|
||||
pub(crate) fn try_borrow(&self) -> Result<UserDataBorrowRef<T>> {
|
||||
match self {
|
||||
Self::Owned(data) => data.try_borrow(),
|
||||
Self::Scoped(_) => Err(Error::UserDataTypeMismatch),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub(crate) fn try_borrow_mut(&self) -> Result<UserDataBorrowMut<T>> {
|
||||
match self {
|
||||
Self::Owned(data) => data.try_borrow_mut(),
|
||||
Self::Scoped(_) => Err(Error::UserDataTypeMismatch),
|
||||
}
|
||||
}
|
||||
|
||||
// Mutably borrows the wrapped value and returns an owned reference.
|
||||
#[inline(always)]
|
||||
pub(crate) fn try_borrow_owned_mut(&self) -> Result<UserDataRefMut<T>> {
|
||||
@@ -489,18 +249,31 @@ impl<T> UserDataStorage<T> {
|
||||
Self::Scoped(ScopedUserDataVariant::Boxed(RefCell::new(data)))
|
||||
}
|
||||
|
||||
/// Returns `true` if it's safe to destroy the container.
|
||||
///
|
||||
/// It's safe to destroy the container if the reference count is greater than 1 or the lock is
|
||||
/// not acquired.
|
||||
#[inline(always)]
|
||||
pub(crate) fn is_borrowed(&self) -> bool {
|
||||
pub(crate) fn is_safe_to_destroy(&self) -> bool {
|
||||
match self {
|
||||
Self::Owned(variant) => variant.borrow_count().get() > 0,
|
||||
Self::Scoped(_) => true,
|
||||
Self::Owned(variant) => variant.strong_count() > 1 || !variant.raw_lock().is_locked(),
|
||||
Self::Scoped(_) => false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `true` if the container has exclusive access to the value.
|
||||
#[inline(always)]
|
||||
pub(crate) fn has_exclusive_access(&self) -> bool {
|
||||
match self {
|
||||
Self::Owned(variant) => !variant.raw_lock().is_locked(),
|
||||
Self::Scoped(_) => false,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn try_borrow_scoped<R>(&self, f: impl FnOnce(&T) -> R) -> Result<R> {
|
||||
match self {
|
||||
Self::Owned(data) => Ok(f(&*data.try_borrow()?)),
|
||||
Self::Owned(data) => data.try_borrow_scoped(f),
|
||||
Self::Scoped(ScopedUserDataVariant::Ref(value)) => Ok(f(unsafe { &**value })),
|
||||
Self::Scoped(ScopedUserDataVariant::RefMut(value) | ScopedUserDataVariant::Boxed(value)) => {
|
||||
let t = value.try_borrow().map_err(|_| Error::UserDataBorrowError)?;
|
||||
@@ -512,7 +285,7 @@ impl<T> UserDataStorage<T> {
|
||||
#[inline]
|
||||
pub(crate) fn try_borrow_scoped_mut<R>(&self, f: impl FnOnce(&mut T) -> R) -> Result<R> {
|
||||
match self {
|
||||
Self::Owned(data) => Ok(f(&mut *data.try_borrow_mut()?)),
|
||||
Self::Owned(data) => data.try_borrow_scoped_mut(f),
|
||||
Self::Scoped(ScopedUserDataVariant::Ref(_)) => Err(Error::UserDataBorrowMutError),
|
||||
Self::Scoped(ScopedUserDataVariant::RefMut(value) | ScopedUserDataVariant::Boxed(value)) => {
|
||||
let mut t = value
|
||||
@@ -523,30 +296,3 @@ impl<T> UserDataStorage<T> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod assertions {
|
||||
use super::*;
|
||||
|
||||
#[cfg(feature = "send")]
|
||||
static_assertions::assert_impl_all!(UserDataRef<()>: Send, Sync);
|
||||
#[cfg(feature = "send")]
|
||||
static_assertions::assert_not_impl_all!(UserDataRef<std::rc::Rc<()>>: Send, Sync);
|
||||
#[cfg(feature = "send")]
|
||||
static_assertions::assert_impl_all!(UserDataRefMut<()>: Sync, Send);
|
||||
#[cfg(feature = "send")]
|
||||
static_assertions::assert_not_impl_all!(UserDataRefMut<std::rc::Rc<()>>: Send, Sync);
|
||||
#[cfg(feature = "send")]
|
||||
static_assertions::assert_impl_all!(UserDataBorrowRef<'_, ()>: Send, Sync);
|
||||
#[cfg(feature = "send")]
|
||||
static_assertions::assert_impl_all!(UserDataBorrowMut<'_, ()>: Send, Sync);
|
||||
|
||||
#[cfg(not(feature = "send"))]
|
||||
static_assertions::assert_not_impl_all!(UserDataRef<()>: Send, Sync);
|
||||
#[cfg(not(feature = "send"))]
|
||||
static_assertions::assert_not_impl_all!(UserDataRefMut<()>: Send, Sync);
|
||||
#[cfg(not(feature = "send"))]
|
||||
static_assertions::assert_not_impl_all!(UserDataBorrowRef<'_, ()>: Send, Sync);
|
||||
#[cfg(not(feature = "send"))]
|
||||
static_assertions::assert_not_impl_all!(UserDataBorrowMut<'_, ()>: Send, Sync);
|
||||
}
|
||||
|
||||
@@ -1,11 +1,51 @@
|
||||
pub(crate) trait UserDataLock {
|
||||
const INIT: Self;
|
||||
|
||||
fn is_locked(&self) -> bool;
|
||||
fn try_lock_shared(&self) -> bool;
|
||||
fn try_lock_exclusive(&self) -> bool;
|
||||
|
||||
unsafe fn unlock_shared(&self);
|
||||
unsafe fn unlock_exclusive(&self);
|
||||
|
||||
fn try_lock_shared_guarded(&self) -> Result<LockGuard<'_, Self>, ()> {
|
||||
if self.try_lock_shared() {
|
||||
Ok(LockGuard {
|
||||
lock: self,
|
||||
exclusive: false,
|
||||
})
|
||||
} else {
|
||||
Err(())
|
||||
}
|
||||
}
|
||||
|
||||
fn try_lock_exclusive_guarded(&self) -> Result<LockGuard<'_, Self>, ()> {
|
||||
if self.try_lock_exclusive() {
|
||||
Ok(LockGuard {
|
||||
lock: self,
|
||||
exclusive: true,
|
||||
})
|
||||
} else {
|
||||
Err(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct LockGuard<'a, L: UserDataLock + ?Sized> {
|
||||
lock: &'a L,
|
||||
exclusive: bool,
|
||||
}
|
||||
|
||||
impl<L: UserDataLock + ?Sized> Drop for LockGuard<'_, L> {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
if self.exclusive {
|
||||
self.lock.unlock_exclusive();
|
||||
} else {
|
||||
self.lock.unlock_shared();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) use lock_impl::RawLock;
|
||||
@@ -25,6 +65,11 @@ mod lock_impl {
|
||||
#[allow(clippy::declare_interior_mutable_const)]
|
||||
const INIT: Self = Cell::new(UNUSED);
|
||||
|
||||
#[inline(always)]
|
||||
fn is_locked(&self) -> bool {
|
||||
self.get() != UNUSED
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn try_lock_shared(&self) -> bool {
|
||||
let flag = self.get().wrapping_add(1);
|
||||
@@ -71,6 +116,11 @@ mod lock_impl {
|
||||
#[allow(clippy::declare_interior_mutable_const)]
|
||||
const INIT: Self = <Self as parking_lot::lock_api::RawRwLock>::INIT;
|
||||
|
||||
#[inline(always)]
|
||||
fn is_locked(&self) -> bool {
|
||||
RawRwLock::is_locked(self)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn try_lock_shared(&self) -> bool {
|
||||
RawRwLock::try_lock_shared(self)
|
||||
|
||||
@@ -0,0 +1,474 @@
|
||||
use std::any::TypeId;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use std::os::raw::c_int;
|
||||
use std::{fmt, mem};
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
use crate::state::{Lua, RawLua};
|
||||
use crate::traits::FromLua;
|
||||
use crate::userdata::AnyUserData;
|
||||
use crate::util::{get_userdata, short_type_name};
|
||||
use crate::value::Value;
|
||||
|
||||
use super::cell::{UserDataStorage, UserDataVariant};
|
||||
use super::lock::{LockGuard, RawLock, UserDataLock};
|
||||
use super::util::is_sync;
|
||||
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
use {
|
||||
parking_lot::{
|
||||
Mutex as MutexPL, MutexGuard as MutexGuardPL, RwLock as RwLockPL,
|
||||
RwLockReadGuard as RwLockReadGuardPL, RwLockWriteGuard as RwLockWriteGuardPL,
|
||||
},
|
||||
std::sync::Arc,
|
||||
};
|
||||
#[cfg(all(feature = "userdata-wrappers", not(feature = "send")))]
|
||||
use {
|
||||
std::cell::{Ref, RefCell, RefMut},
|
||||
std::rc::Rc,
|
||||
};
|
||||
|
||||
/// A wrapper type for a userdata value that provides read access.
|
||||
///
|
||||
/// It implements [`FromLua`] and can be used to receive a typed userdata from Lua.
|
||||
pub struct UserDataRef<T: 'static> {
|
||||
// It's important to drop the guard first, as it refers to the `inner` data.
|
||||
_guard: LockGuard<'static, RawLock>,
|
||||
inner: UserDataRefInner<T>,
|
||||
}
|
||||
|
||||
impl<T> Deref for UserDataRef<T> {
|
||||
type Target = T;
|
||||
|
||||
#[inline]
|
||||
fn deref(&self) -> &T {
|
||||
&self.inner
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: fmt::Debug> fmt::Debug for UserDataRef<T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
(**self).fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: fmt::Display> fmt::Display for UserDataRef<T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
(**self).fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> TryFrom<UserDataVariant<T>> for UserDataRef<T> {
|
||||
type Error = Error;
|
||||
|
||||
#[inline]
|
||||
fn try_from(variant: UserDataVariant<T>) -> Result<Self> {
|
||||
let guard = if !cfg!(feature = "send") || is_sync::<T>() {
|
||||
variant.raw_lock().try_lock_shared_guarded()
|
||||
} else {
|
||||
variant.raw_lock().try_lock_exclusive_guarded()
|
||||
};
|
||||
let guard = guard.map_err(|_| Error::UserDataBorrowError)?;
|
||||
let guard = unsafe { mem::transmute::<LockGuard<_>, LockGuard<'static, _>>(guard) };
|
||||
Ok(UserDataRef::from_parts(UserDataRefInner::Default(variant), guard))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: 'static> FromLua for UserDataRef<T> {
|
||||
fn from_lua(value: Value, _: &Lua) -> Result<Self> {
|
||||
try_value_to_userdata::<T>(value)?.borrow()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn from_stack(idx: c_int, lua: &RawLua) -> Result<Self> {
|
||||
Self::borrow_from_stack(lua, lua.state(), idx)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: 'static> UserDataRef<T> {
|
||||
#[inline(always)]
|
||||
fn from_parts(inner: UserDataRefInner<T>, guard: LockGuard<'static, RawLock>) -> Self {
|
||||
Self { _guard: guard, inner }
|
||||
}
|
||||
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
fn remap<U>(
|
||||
self,
|
||||
f: impl FnOnce(UserDataVariant<T>) -> Result<UserDataRefInner<U>>,
|
||||
) -> Result<UserDataRef<U>> {
|
||||
match &self.inner {
|
||||
UserDataRefInner::Default(variant) => {
|
||||
let inner = f(variant.clone())?;
|
||||
Ok(UserDataRef::from_parts(inner, self._guard))
|
||||
}
|
||||
_ => Err(Error::UserDataTypeMismatch),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn borrow_from_stack(
|
||||
lua: &RawLua,
|
||||
state: *mut ffi::lua_State,
|
||||
idx: c_int,
|
||||
) -> Result<Self> {
|
||||
let type_id = lua.get_userdata_type_id::<T>(state, idx)?;
|
||||
match type_id {
|
||||
Some(type_id) if type_id == TypeId::of::<T>() => {
|
||||
let ud = get_userdata::<UserDataStorage<T>>(state, idx);
|
||||
(*ud).try_borrow_owned()
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "userdata-wrappers", not(feature = "send")))]
|
||||
Some(type_id) if type_id == TypeId::of::<Rc<T>>() => {
|
||||
let ud = get_userdata::<UserDataStorage<Rc<T>>>(state, idx);
|
||||
((*ud).try_borrow_owned()).and_then(|ud| ud.transform_rc())
|
||||
}
|
||||
#[cfg(all(feature = "userdata-wrappers", not(feature = "send")))]
|
||||
Some(type_id) if type_id == TypeId::of::<Rc<RefCell<T>>>() => {
|
||||
let ud = get_userdata::<UserDataStorage<Rc<RefCell<T>>>>(state, idx);
|
||||
((*ud).try_borrow_owned()).and_then(|ud| ud.transform_rc_refcell())
|
||||
}
|
||||
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
Some(type_id) if type_id == TypeId::of::<Arc<T>>() => {
|
||||
let ud = get_userdata::<UserDataStorage<Arc<T>>>(state, idx);
|
||||
((*ud).try_borrow_owned()).and_then(|ud| ud.transform_arc())
|
||||
}
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
Some(type_id) if type_id == TypeId::of::<Arc<MutexPL<T>>>() => {
|
||||
let ud = get_userdata::<UserDataStorage<Arc<MutexPL<T>>>>(state, idx);
|
||||
((*ud).try_borrow_owned()).and_then(|ud| ud.transform_arc_mutex_pl())
|
||||
}
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
Some(type_id) if type_id == TypeId::of::<Arc<RwLockPL<T>>>() => {
|
||||
let ud = get_userdata::<UserDataStorage<Arc<RwLockPL<T>>>>(state, idx);
|
||||
((*ud).try_borrow_owned()).and_then(|ud| ud.transform_arc_rwlock_pl())
|
||||
}
|
||||
_ => Err(Error::UserDataTypeMismatch),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "userdata-wrappers", not(feature = "send")))]
|
||||
impl<T> UserDataRef<Rc<T>> {
|
||||
fn transform_rc(self) -> Result<UserDataRef<T>> {
|
||||
self.remap(|variant| Ok(UserDataRefInner::Rc(variant)))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "userdata-wrappers", not(feature = "send")))]
|
||||
impl<T> UserDataRef<Rc<RefCell<T>>> {
|
||||
fn transform_rc_refcell(self) -> Result<UserDataRef<T>> {
|
||||
self.remap(|variant| unsafe {
|
||||
let obj = &*variant.as_ptr();
|
||||
let r#ref = obj.try_borrow().map_err(|_| Error::UserDataBorrowError)?;
|
||||
let borrow = std::mem::transmute::<Ref<T>, Ref<'static, T>>(r#ref);
|
||||
Ok(UserDataRefInner::RcRefCell(borrow, variant))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
impl<T> UserDataRef<Arc<T>> {
|
||||
fn transform_arc(self) -> Result<UserDataRef<T>> {
|
||||
self.remap(|variant| Ok(UserDataRefInner::Arc(variant)))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
impl<T> UserDataRef<Arc<MutexPL<T>>> {
|
||||
fn transform_arc_mutex_pl(self) -> Result<UserDataRef<T>> {
|
||||
self.remap(|variant| unsafe {
|
||||
let obj = &*variant.as_ptr();
|
||||
let guard = obj.try_lock().ok_or(Error::UserDataBorrowError)?;
|
||||
let borrow = std::mem::transmute::<MutexGuardPL<T>, MutexGuardPL<'static, T>>(guard);
|
||||
Ok(UserDataRefInner::ArcMutexPL(borrow, variant))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
impl<T> UserDataRef<Arc<RwLockPL<T>>> {
|
||||
fn transform_arc_rwlock_pl(self) -> Result<UserDataRef<T>> {
|
||||
self.remap(|variant| unsafe {
|
||||
let obj = &*variant.as_ptr();
|
||||
let guard = obj.try_read().ok_or(Error::UserDataBorrowError)?;
|
||||
let borrow = std::mem::transmute::<RwLockReadGuardPL<T>, RwLockReadGuardPL<'static, T>>(guard);
|
||||
Ok(UserDataRefInner::ArcRwLockPL(borrow, variant))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
enum UserDataRefInner<T: 'static> {
|
||||
Default(UserDataVariant<T>),
|
||||
|
||||
#[cfg(all(feature = "userdata-wrappers", not(feature = "send")))]
|
||||
Rc(UserDataVariant<Rc<T>>),
|
||||
#[cfg(all(feature = "userdata-wrappers", not(feature = "send")))]
|
||||
RcRefCell(Ref<'static, T>, UserDataVariant<Rc<RefCell<T>>>),
|
||||
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
Arc(UserDataVariant<Arc<T>>),
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
ArcMutexPL(MutexGuardPL<'static, T>, UserDataVariant<Arc<MutexPL<T>>>),
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
ArcRwLockPL(RwLockReadGuardPL<'static, T>, UserDataVariant<Arc<RwLockPL<T>>>),
|
||||
}
|
||||
|
||||
impl<T> Deref for UserDataRefInner<T> {
|
||||
type Target = T;
|
||||
|
||||
#[inline]
|
||||
fn deref(&self) -> &T {
|
||||
match self {
|
||||
Self::Default(inner) => unsafe { &*inner.as_ptr() },
|
||||
|
||||
#[cfg(all(feature = "userdata-wrappers", not(feature = "send")))]
|
||||
Self::Rc(inner) => unsafe { &*Rc::as_ptr(&*inner.as_ptr()) },
|
||||
#[cfg(all(feature = "userdata-wrappers", not(feature = "send")))]
|
||||
Self::RcRefCell(x, ..) => x,
|
||||
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
Self::Arc(inner) => unsafe { &*Arc::as_ptr(&*inner.as_ptr()) },
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
Self::ArcMutexPL(x, ..) => x,
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
Self::ArcRwLockPL(x, ..) => x,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A wrapper type for a userdata value that provides read and write access.
|
||||
///
|
||||
/// It implements [`FromLua`] and can be used to receive a typed userdata from Lua.
|
||||
pub struct UserDataRefMut<T: 'static> {
|
||||
// It's important to drop the guard first, as it refers to the `inner` data.
|
||||
_guard: LockGuard<'static, RawLock>,
|
||||
inner: UserDataRefMutInner<T>,
|
||||
}
|
||||
|
||||
impl<T> Deref for UserDataRefMut<T> {
|
||||
type Target = T;
|
||||
|
||||
#[inline]
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.inner
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> DerefMut for UserDataRefMut<T> {
|
||||
#[inline]
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.inner
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: fmt::Debug> fmt::Debug for UserDataRefMut<T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
(**self).fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: fmt::Display> fmt::Display for UserDataRefMut<T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
(**self).fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> TryFrom<UserDataVariant<T>> for UserDataRefMut<T> {
|
||||
type Error = Error;
|
||||
|
||||
#[inline]
|
||||
fn try_from(variant: UserDataVariant<T>) -> Result<Self> {
|
||||
let guard = variant.raw_lock().try_lock_exclusive_guarded();
|
||||
let guard = guard.map_err(|_| Error::UserDataBorrowMutError)?;
|
||||
let guard = unsafe { mem::transmute::<LockGuard<_>, LockGuard<'static, _>>(guard) };
|
||||
Ok(UserDataRefMut::from_parts(
|
||||
UserDataRefMutInner::Default(variant),
|
||||
guard,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: 'static> FromLua for UserDataRefMut<T> {
|
||||
fn from_lua(value: Value, _: &Lua) -> Result<Self> {
|
||||
try_value_to_userdata::<T>(value)?.borrow_mut()
|
||||
}
|
||||
|
||||
unsafe fn from_stack(idx: c_int, lua: &RawLua) -> Result<Self> {
|
||||
Self::borrow_from_stack(lua, lua.state(), idx)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: 'static> UserDataRefMut<T> {
|
||||
#[inline(always)]
|
||||
fn from_parts(inner: UserDataRefMutInner<T>, guard: LockGuard<'static, RawLock>) -> Self {
|
||||
Self { _guard: guard, inner }
|
||||
}
|
||||
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
fn remap<U>(
|
||||
self,
|
||||
f: impl FnOnce(UserDataVariant<T>) -> Result<UserDataRefMutInner<U>>,
|
||||
) -> Result<UserDataRefMut<U>> {
|
||||
match &self.inner {
|
||||
UserDataRefMutInner::Default(variant) => {
|
||||
let inner = f(variant.clone())?;
|
||||
Ok(UserDataRefMut::from_parts(inner, self._guard))
|
||||
}
|
||||
_ => Err(Error::UserDataTypeMismatch),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn borrow_from_stack(
|
||||
lua: &RawLua,
|
||||
state: *mut ffi::lua_State,
|
||||
idx: c_int,
|
||||
) -> Result<Self> {
|
||||
let type_id = lua.get_userdata_type_id::<T>(state, idx)?;
|
||||
match type_id {
|
||||
Some(type_id) if type_id == TypeId::of::<T>() => {
|
||||
let ud = get_userdata::<UserDataStorage<T>>(state, idx);
|
||||
(*ud).try_borrow_owned_mut()
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "userdata-wrappers", not(feature = "send")))]
|
||||
Some(type_id) if type_id == TypeId::of::<Rc<T>>() => Err(Error::UserDataBorrowMutError),
|
||||
#[cfg(all(feature = "userdata-wrappers", not(feature = "send")))]
|
||||
Some(type_id) if type_id == TypeId::of::<Rc<RefCell<T>>>() => {
|
||||
let ud = get_userdata::<UserDataStorage<Rc<RefCell<T>>>>(state, idx);
|
||||
((*ud).try_borrow_owned_mut()).and_then(|ud| ud.transform_rc_refcell())
|
||||
}
|
||||
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
Some(type_id) if type_id == TypeId::of::<Arc<T>>() => Err(Error::UserDataBorrowMutError),
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
Some(type_id) if type_id == TypeId::of::<Arc<MutexPL<T>>>() => {
|
||||
let ud = get_userdata::<UserDataStorage<Arc<MutexPL<T>>>>(state, idx);
|
||||
((*ud).try_borrow_owned_mut()).and_then(|ud| ud.transform_arc_mutex_pl())
|
||||
}
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
Some(type_id) if type_id == TypeId::of::<Arc<RwLockPL<T>>>() => {
|
||||
let ud = get_userdata::<UserDataStorage<Arc<RwLockPL<T>>>>(state, idx);
|
||||
((*ud).try_borrow_owned_mut()).and_then(|ud| ud.transform_arc_rwlock_pl())
|
||||
}
|
||||
_ => Err(Error::UserDataTypeMismatch),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "userdata-wrappers", not(feature = "send")))]
|
||||
impl<T> UserDataRefMut<Rc<RefCell<T>>> {
|
||||
fn transform_rc_refcell(self) -> Result<UserDataRefMut<T>> {
|
||||
self.remap(|variant| unsafe {
|
||||
let obj = &*variant.as_ptr();
|
||||
let refmut = obj.try_borrow_mut().map_err(|_| Error::UserDataBorrowMutError)?;
|
||||
let borrow = std::mem::transmute::<RefMut<T>, RefMut<'static, T>>(refmut);
|
||||
Ok(UserDataRefMutInner::RcRefCell(borrow, variant))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
impl<T> UserDataRefMut<Arc<MutexPL<T>>> {
|
||||
fn transform_arc_mutex_pl(self) -> Result<UserDataRefMut<T>> {
|
||||
self.remap(|variant| unsafe {
|
||||
let obj = &*variant.as_ptr();
|
||||
let guard = obj.try_lock().ok_or(Error::UserDataBorrowMutError)?;
|
||||
let borrow = std::mem::transmute::<MutexGuardPL<T>, MutexGuardPL<'static, T>>(guard);
|
||||
Ok(UserDataRefMutInner::ArcMutexPL(borrow, variant))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
impl<T> UserDataRefMut<Arc<RwLockPL<T>>> {
|
||||
fn transform_arc_rwlock_pl(self) -> Result<UserDataRefMut<T>> {
|
||||
self.remap(|variant| unsafe {
|
||||
let obj = &*variant.as_ptr();
|
||||
let guard = obj.try_write().ok_or(Error::UserDataBorrowMutError)?;
|
||||
let borrow = std::mem::transmute::<RwLockWriteGuardPL<T>, RwLockWriteGuardPL<'static, T>>(guard);
|
||||
Ok(UserDataRefMutInner::ArcRwLockPL(borrow, variant))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
enum UserDataRefMutInner<T: 'static> {
|
||||
Default(UserDataVariant<T>),
|
||||
|
||||
#[cfg(all(feature = "userdata-wrappers", not(feature = "send")))]
|
||||
RcRefCell(RefMut<'static, T>, UserDataVariant<Rc<RefCell<T>>>),
|
||||
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
ArcMutexPL(MutexGuardPL<'static, T>, UserDataVariant<Arc<MutexPL<T>>>),
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
ArcRwLockPL(RwLockWriteGuardPL<'static, T>, UserDataVariant<Arc<RwLockPL<T>>>),
|
||||
}
|
||||
|
||||
impl<T> Deref for UserDataRefMutInner<T> {
|
||||
type Target = T;
|
||||
|
||||
#[inline]
|
||||
fn deref(&self) -> &T {
|
||||
match self {
|
||||
Self::Default(inner) => unsafe { &*inner.as_ptr() },
|
||||
|
||||
#[cfg(all(feature = "userdata-wrappers", not(feature = "send")))]
|
||||
Self::RcRefCell(x, ..) => x,
|
||||
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
Self::ArcMutexPL(x, ..) => x,
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
Self::ArcRwLockPL(x, ..) => x,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> DerefMut for UserDataRefMutInner<T> {
|
||||
#[inline]
|
||||
fn deref_mut(&mut self) -> &mut T {
|
||||
match self {
|
||||
Self::Default(inner) => unsafe { &mut *inner.as_ptr() },
|
||||
|
||||
#[cfg(all(feature = "userdata-wrappers", not(feature = "send")))]
|
||||
Self::RcRefCell(x, ..) => x,
|
||||
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
Self::ArcMutexPL(x, ..) => x,
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
Self::ArcRwLockPL(x, ..) => x,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn try_value_to_userdata<T>(value: Value) -> Result<AnyUserData> {
|
||||
match value {
|
||||
Value::UserData(ud) => Ok(ud),
|
||||
_ => Err(Error::FromLuaConversionError {
|
||||
from: value.type_name(),
|
||||
to: "userdata".to_string(),
|
||||
message: Some(format!("expected userdata of type {}", short_type_name::<T>())),
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod assertions {
|
||||
use super::*;
|
||||
|
||||
#[cfg(feature = "send")]
|
||||
static_assertions::assert_impl_all!(UserDataRef<()>: Send, Sync);
|
||||
#[cfg(feature = "send")]
|
||||
static_assertions::assert_not_impl_all!(UserDataRef<std::rc::Rc<()>>: Send, Sync);
|
||||
#[cfg(feature = "send")]
|
||||
static_assertions::assert_impl_all!(UserDataRefMut<()>: Sync, Send);
|
||||
#[cfg(feature = "send")]
|
||||
static_assertions::assert_not_impl_all!(UserDataRefMut<std::rc::Rc<()>>: Send, Sync);
|
||||
|
||||
#[cfg(not(feature = "send"))]
|
||||
static_assertions::assert_not_impl_all!(UserDataRef<()>: Send, Sync);
|
||||
#[cfg(not(feature = "send"))]
|
||||
static_assertions::assert_not_impl_all!(UserDataRefMut<()>: Send, Sync);
|
||||
}
|
||||
+126
-293
@@ -7,11 +7,14 @@ use std::os::raw::c_void;
|
||||
use std::string::String as StdString;
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
use crate::state::{Lua, RawLua};
|
||||
use crate::state::{Lua, LuaGuard};
|
||||
use crate::traits::{FromLua, FromLuaMulti, IntoLua, IntoLuaMulti};
|
||||
use crate::types::{Callback, MaybeSend};
|
||||
use crate::userdata::{AnyUserData, MetaMethod, UserData, UserDataFields, UserDataMethods, UserDataStorage};
|
||||
use crate::util::{get_userdata, short_type_name};
|
||||
use crate::userdata::{
|
||||
borrow_userdata_scoped, borrow_userdata_scoped_mut, AnyUserData, MetaMethod, TypeIdHints, UserData,
|
||||
UserDataFields, UserDataMethods, UserDataStorage,
|
||||
};
|
||||
use crate::util::short_type_name;
|
||||
use crate::value::Value;
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
@@ -21,41 +24,26 @@ 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)]
|
||||
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),
|
||||
enum UserDataType {
|
||||
Shared(TypeIdHints),
|
||||
Unique(*mut c_void),
|
||||
}
|
||||
|
||||
/// Handle to registry for userdata methods and metamethods.
|
||||
pub struct UserDataRegistry<T> {
|
||||
lua: LuaGuard,
|
||||
raw: RawUserDataRegistry,
|
||||
r#type: UserDataType,
|
||||
_phantom: PhantomData<T>,
|
||||
}
|
||||
|
||||
pub(crate) struct RawUserDataRegistry {
|
||||
// Fields
|
||||
pub(crate) fields: Vec<(String, StaticFieldCallback)>,
|
||||
pub(crate) fields: Vec<(String, Result<Value>)>,
|
||||
pub(crate) field_getters: Vec<(String, Callback)>,
|
||||
pub(crate) field_setters: Vec<(String, Callback)>,
|
||||
pub(crate) meta_fields: Vec<(String, StaticFieldCallback)>,
|
||||
pub(crate) meta_fields: Vec<(String, Result<Value>)>,
|
||||
|
||||
// Methods
|
||||
pub(crate) methods: Vec<(String, Callback)>,
|
||||
@@ -65,24 +53,40 @@ pub struct UserDataRegistry<T> {
|
||||
#[cfg(feature = "async")]
|
||||
pub(crate) async_meta_methods: Vec<(String, AsyncCallback)>,
|
||||
|
||||
type_id: UserDataTypeId,
|
||||
_type: PhantomData<T>,
|
||||
pub(crate) destructor: ffi::lua_CFunction,
|
||||
pub(crate) type_id: Option<TypeId>,
|
||||
pub(crate) type_name: StdString,
|
||||
}
|
||||
|
||||
impl UserDataType {
|
||||
#[inline]
|
||||
pub(crate) fn type_id(&self) -> Option<TypeId> {
|
||||
match self {
|
||||
UserDataType::Shared(hints) => Some(hints.type_id()),
|
||||
UserDataType::Unique(_) => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "send")]
|
||||
unsafe impl Send for UserDataType {}
|
||||
|
||||
impl<T: 'static> UserDataRegistry<T> {
|
||||
#[inline(always)]
|
||||
pub(crate) fn new(lua: &Lua) -> Self {
|
||||
Self::with_type(lua, UserDataType::Shared(TypeIdHints::new::<T>()))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> UserDataRegistry<T> {
|
||||
#[inline(always)]
|
||||
pub(crate) fn new(type_id: TypeId) -> Self {
|
||||
Self::with_type_id(UserDataTypeId::Shared(type_id))
|
||||
pub(crate) fn new_unique(lua: &Lua, ud_ptr: *mut c_void) -> Self {
|
||||
Self::with_type(lua, UserDataType::Unique(ud_ptr))
|
||||
}
|
||||
|
||||
#[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 {
|
||||
fn with_type(lua: &Lua, r#type: UserDataType) -> Self {
|
||||
let raw = RawUserDataRegistry {
|
||||
fields: Vec::new(),
|
||||
field_getters: Vec::new(),
|
||||
field_setters: Vec::new(),
|
||||
@@ -93,30 +97,16 @@ impl<T> UserDataRegistry<T> {
|
||||
meta_methods: Vec::new(),
|
||||
#[cfg(feature = "async")]
|
||||
async_meta_methods: Vec::new(),
|
||||
type_id,
|
||||
_type: PhantomData,
|
||||
}
|
||||
}
|
||||
destructor: super::util::destroy_userdata_storage::<T>,
|
||||
type_id: r#type.type_id(),
|
||||
type_name: short_type_name::<T>(),
|
||||
};
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn type_id(&self) -> Option<TypeId> {
|
||||
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),
|
||||
UserDataRegistry {
|
||||
lua: lua.lock_arc(),
|
||||
raw,
|
||||
r#type,
|
||||
_phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -133,7 +123,7 @@ impl<T> UserDataRegistry<T> {
|
||||
};
|
||||
}
|
||||
|
||||
let target_type_id = self.type_id;
|
||||
let target_type = self.r#type;
|
||||
Box::new(move |rawlua, nargs| unsafe {
|
||||
if nargs == 0 {
|
||||
let err = Error::from_lua_conversion("missing argument", "userdata", None);
|
||||
@@ -145,103 +135,24 @@ impl<T> UserDataRegistry<T> {
|
||||
// Self was at position 1, so we pass 2 here
|
||||
let args = A::from_stack_args(nargs - 1, 2, Some(&name), rawlua);
|
||||
|
||||
match target_type_id {
|
||||
match target_type {
|
||||
#[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| {
|
||||
UserDataType::Shared(type_hints) => {
|
||||
let type_id = try_self_arg!(rawlua.get_userdata_type_id::<T>(state, self_index));
|
||||
try_self_arg!(borrow_userdata_scoped(state, self_index, type_id, type_hints, |ud| {
|
||||
method(rawlua.lua(), ud, args?)?.push_into_stack_multi(rawlua)
|
||||
}))
|
||||
}
|
||||
#[rustfmt::skip]
|
||||
UserDataTypeId::Unique(target_ptr)
|
||||
if get_userdata::<UserDataStorage<T>>(state, self_index) as usize == target_ptr =>
|
||||
{
|
||||
UserDataType::Unique(target_ptr) if ffi::lua_touserdata(state, self_index) == 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)
|
||||
}))
|
||||
UserDataType::Unique(_) => {
|
||||
try_self_arg!(rawlua.get_userdata_type_id::<T>(state, self_index));
|
||||
Err(Error::bad_self_argument(&name, Error::UserDataTypeMismatch))
|
||||
}
|
||||
#[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)),
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -260,7 +171,7 @@ impl<T> UserDataRegistry<T> {
|
||||
}
|
||||
|
||||
let method = RefCell::new(method);
|
||||
let target_type_id = self.type_id;
|
||||
let target_type = self.r#type;
|
||||
Box::new(move |rawlua, nargs| unsafe {
|
||||
let mut method = method.try_borrow_mut().map_err(|_| Error::RecursiveMutCallback)?;
|
||||
if nargs == 0 {
|
||||
@@ -273,97 +184,24 @@ impl<T> UserDataRegistry<T> {
|
||||
// Self was at position 1, so we pass 2 here
|
||||
let args = A::from_stack_args(nargs - 1, 2, Some(&name), rawlua);
|
||||
|
||||
match target_type_id {
|
||||
match target_type {
|
||||
#[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| {
|
||||
UserDataType::Shared(type_hints) => {
|
||||
let type_id = try_self_arg!(rawlua.get_userdata_type_id::<T>(state, self_index));
|
||||
try_self_arg!(borrow_userdata_scoped_mut(state, self_index, type_id, type_hints, |ud| {
|
||||
method(rawlua.lua(), ud, args?)?.push_into_stack_multi(rawlua)
|
||||
}))
|
||||
}
|
||||
#[rustfmt::skip]
|
||||
UserDataTypeId::Unique(target_ptr)
|
||||
if get_userdata::<UserDataStorage<T>>(state, self_index) as usize == target_ptr =>
|
||||
{
|
||||
UserDataType::Unique(target_ptr) if ffi::lua_touserdata(state, self_index) == 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)
|
||||
}))
|
||||
UserDataType::Unique(_) => {
|
||||
try_self_arg!(rawlua.get_userdata_type_id::<T>(state, self_index));
|
||||
Err(Error::bad_self_argument(&name, Error::UserDataTypeMismatch))
|
||||
}
|
||||
#[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)),
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -514,6 +352,11 @@ impl<T> UserDataRegistry<T> {
|
||||
}
|
||||
value.into_lua(lua)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub(crate) fn into_raw(self) -> RawUserDataRegistry {
|
||||
self.raw
|
||||
}
|
||||
}
|
||||
|
||||
// Returns function name for the type `T`, without the module path
|
||||
@@ -527,10 +370,7 @@ impl<T> UserDataFields<T> for UserDataRegistry<T> {
|
||||
V: IntoLua + 'static,
|
||||
{
|
||||
let name = name.to_string();
|
||||
self.fields.push((
|
||||
name,
|
||||
Box::new(move |rawlua| unsafe { value.push_into_stack(rawlua) }),
|
||||
));
|
||||
self.raw.fields.push((name, value.into_lua(self.lua.lua())));
|
||||
}
|
||||
|
||||
fn add_field_method_get<M, R>(&mut self, name: impl ToString, method: M)
|
||||
@@ -540,7 +380,7 @@ impl<T> UserDataFields<T> for UserDataRegistry<T> {
|
||||
{
|
||||
let name = name.to_string();
|
||||
let callback = self.box_method(&name, move |lua, data, ()| method(lua, data));
|
||||
self.field_getters.push((name, callback));
|
||||
self.raw.field_getters.push((name, callback));
|
||||
}
|
||||
|
||||
fn add_field_method_set<M, A>(&mut self, name: impl ToString, method: M)
|
||||
@@ -550,7 +390,7 @@ impl<T> UserDataFields<T> for UserDataRegistry<T> {
|
||||
{
|
||||
let name = name.to_string();
|
||||
let callback = self.box_method_mut(&name, method);
|
||||
self.field_setters.push((name, callback));
|
||||
self.raw.field_setters.push((name, callback));
|
||||
}
|
||||
|
||||
fn add_field_function_get<F, R>(&mut self, name: impl ToString, function: F)
|
||||
@@ -560,7 +400,7 @@ impl<T> UserDataFields<T> for UserDataRegistry<T> {
|
||||
{
|
||||
let name = name.to_string();
|
||||
let callback = self.box_function(&name, function);
|
||||
self.field_getters.push((name, callback));
|
||||
self.raw.field_getters.push((name, callback));
|
||||
}
|
||||
|
||||
fn add_field_function_set<F, A>(&mut self, name: impl ToString, mut function: F)
|
||||
@@ -570,20 +410,17 @@ impl<T> UserDataFields<T> for UserDataRegistry<T> {
|
||||
{
|
||||
let name = name.to_string();
|
||||
let callback = self.box_function_mut(&name, move |lua, (data, val)| function(lua, data, val));
|
||||
self.field_setters.push((name, callback));
|
||||
self.raw.field_setters.push((name, callback));
|
||||
}
|
||||
|
||||
fn add_meta_field<V>(&mut self, name: impl ToString, value: V)
|
||||
where
|
||||
V: IntoLua + 'static,
|
||||
{
|
||||
let lua = self.lua.lua();
|
||||
let name = name.to_string();
|
||||
self.meta_fields.push((
|
||||
name.clone(),
|
||||
Box::new(move |rawlua| unsafe {
|
||||
Self::check_meta_field(rawlua.lua(), &name, value)?.push_into_stack(rawlua)
|
||||
}),
|
||||
));
|
||||
let field = Self::check_meta_field(lua, &name, value).and_then(|v| v.into_lua(lua));
|
||||
self.raw.meta_fields.push((name, field));
|
||||
}
|
||||
|
||||
fn add_meta_field_with<F, R>(&mut self, name: impl ToString, f: F)
|
||||
@@ -591,14 +428,10 @@ impl<T> UserDataFields<T> for UserDataRegistry<T> {
|
||||
F: FnOnce(&Lua) -> Result<R> + 'static,
|
||||
R: IntoLua,
|
||||
{
|
||||
let lua = self.lua.lua();
|
||||
let name = name.to_string();
|
||||
self.meta_fields.push((
|
||||
name.clone(),
|
||||
Box::new(move |rawlua| unsafe {
|
||||
let lua = rawlua.lua();
|
||||
Self::check_meta_field(lua, &name, f(lua)?)?.push_into_stack(rawlua)
|
||||
}),
|
||||
));
|
||||
let field = f(lua).and_then(|v| Self::check_meta_field(lua, &name, v).and_then(|v| v.into_lua(lua)));
|
||||
self.raw.meta_fields.push((name, field));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -611,7 +444,7 @@ impl<T> UserDataMethods<T> for UserDataRegistry<T> {
|
||||
{
|
||||
let name = name.to_string();
|
||||
let callback = self.box_method(&name, method);
|
||||
self.methods.push((name, callback));
|
||||
self.raw.methods.push((name, callback));
|
||||
}
|
||||
|
||||
fn add_method_mut<M, A, R>(&mut self, name: impl ToString, method: M)
|
||||
@@ -622,7 +455,7 @@ impl<T> UserDataMethods<T> for UserDataRegistry<T> {
|
||||
{
|
||||
let name = name.to_string();
|
||||
let callback = self.box_method_mut(&name, method);
|
||||
self.methods.push((name, callback));
|
||||
self.raw.methods.push((name, callback));
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
@@ -636,7 +469,7 @@ impl<T> UserDataMethods<T> for UserDataRegistry<T> {
|
||||
{
|
||||
let name = name.to_string();
|
||||
let callback = self.box_async_method(&name, method);
|
||||
self.async_methods.push((name, callback));
|
||||
self.raw.async_methods.push((name, callback));
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
@@ -650,7 +483,7 @@ impl<T> UserDataMethods<T> for UserDataRegistry<T> {
|
||||
{
|
||||
let name = name.to_string();
|
||||
let callback = self.box_async_method_mut(&name, method);
|
||||
self.async_methods.push((name, callback));
|
||||
self.raw.async_methods.push((name, callback));
|
||||
}
|
||||
|
||||
fn add_function<F, A, R>(&mut self, name: impl ToString, function: F)
|
||||
@@ -661,7 +494,7 @@ impl<T> UserDataMethods<T> for UserDataRegistry<T> {
|
||||
{
|
||||
let name = name.to_string();
|
||||
let callback = self.box_function(&name, function);
|
||||
self.methods.push((name, callback));
|
||||
self.raw.methods.push((name, callback));
|
||||
}
|
||||
|
||||
fn add_function_mut<F, A, R>(&mut self, name: impl ToString, function: F)
|
||||
@@ -672,7 +505,7 @@ impl<T> UserDataMethods<T> for UserDataRegistry<T> {
|
||||
{
|
||||
let name = name.to_string();
|
||||
let callback = self.box_function_mut(&name, function);
|
||||
self.methods.push((name, callback));
|
||||
self.raw.methods.push((name, callback));
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
@@ -685,7 +518,7 @@ impl<T> UserDataMethods<T> for UserDataRegistry<T> {
|
||||
{
|
||||
let name = name.to_string();
|
||||
let callback = self.box_async_function(&name, function);
|
||||
self.async_methods.push((name, callback));
|
||||
self.raw.async_methods.push((name, callback));
|
||||
}
|
||||
|
||||
fn add_meta_method<M, A, R>(&mut self, name: impl ToString, method: M)
|
||||
@@ -696,7 +529,7 @@ impl<T> UserDataMethods<T> for UserDataRegistry<T> {
|
||||
{
|
||||
let name = name.to_string();
|
||||
let callback = self.box_method(&name, method);
|
||||
self.meta_methods.push((name, callback));
|
||||
self.raw.meta_methods.push((name, callback));
|
||||
}
|
||||
|
||||
fn add_meta_method_mut<M, A, R>(&mut self, name: impl ToString, method: M)
|
||||
@@ -707,7 +540,7 @@ impl<T> UserDataMethods<T> for UserDataRegistry<T> {
|
||||
{
|
||||
let name = name.to_string();
|
||||
let callback = self.box_method_mut(&name, method);
|
||||
self.meta_methods.push((name, callback));
|
||||
self.raw.meta_methods.push((name, callback));
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "async", not(any(feature = "lua51", feature = "luau"))))]
|
||||
@@ -721,7 +554,7 @@ impl<T> UserDataMethods<T> for UserDataRegistry<T> {
|
||||
{
|
||||
let name = name.to_string();
|
||||
let callback = self.box_async_method(&name, method);
|
||||
self.async_meta_methods.push((name, callback));
|
||||
self.raw.async_meta_methods.push((name, callback));
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "async", not(any(feature = "lua51", feature = "luau"))))]
|
||||
@@ -735,7 +568,7 @@ impl<T> UserDataMethods<T> for UserDataRegistry<T> {
|
||||
{
|
||||
let name = name.to_string();
|
||||
let callback = self.box_async_method_mut(&name, method);
|
||||
self.async_meta_methods.push((name, callback));
|
||||
self.raw.async_meta_methods.push((name, callback));
|
||||
}
|
||||
|
||||
fn add_meta_function<F, A, R>(&mut self, name: impl ToString, function: F)
|
||||
@@ -746,7 +579,7 @@ impl<T> UserDataMethods<T> for UserDataRegistry<T> {
|
||||
{
|
||||
let name = name.to_string();
|
||||
let callback = self.box_function(&name, function);
|
||||
self.meta_methods.push((name, callback));
|
||||
self.raw.meta_methods.push((name, callback));
|
||||
}
|
||||
|
||||
fn add_meta_function_mut<F, A, R>(&mut self, name: impl ToString, function: F)
|
||||
@@ -757,7 +590,7 @@ impl<T> UserDataMethods<T> for UserDataRegistry<T> {
|
||||
{
|
||||
let name = name.to_string();
|
||||
let callback = self.box_function_mut(&name, function);
|
||||
self.meta_methods.push((name, callback));
|
||||
self.raw.meta_methods.push((name, callback));
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "async", not(any(feature = "lua51", feature = "luau"))))]
|
||||
@@ -770,34 +603,28 @@ impl<T> UserDataMethods<T> for UserDataRegistry<T> {
|
||||
{
|
||||
let name = name.to_string();
|
||||
let callback = self.box_async_function(&name, function);
|
||||
self.async_meta_methods.push((name, callback));
|
||||
self.raw.async_meta_methods.push((name, callback));
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! lua_userdata_impl {
|
||||
($type:ty => $type_variant:tt) => {
|
||||
lua_userdata_impl!($type, UserDataTypeId::$type_variant(TypeId::of::<$type>()));
|
||||
};
|
||||
|
||||
($type:ty, $type_id:expr) => {
|
||||
($type:ty) => {
|
||||
impl<T: UserData + 'static> UserData for $type {
|
||||
fn register(registry: &mut UserDataRegistry<Self>) {
|
||||
let mut orig_registry = UserDataRegistry::with_type_id($type_id);
|
||||
let mut orig_registry = UserDataRegistry::new(registry.lua.lua());
|
||||
T::register(&mut orig_registry);
|
||||
|
||||
// Copy all fields, methods, etc. from the original registry
|
||||
registry.fields.extend(orig_registry.fields);
|
||||
registry.field_getters.extend(orig_registry.field_getters);
|
||||
registry.field_setters.extend(orig_registry.field_setters);
|
||||
registry.meta_fields.extend(orig_registry.meta_fields);
|
||||
registry.methods.extend(orig_registry.methods);
|
||||
(registry.raw.fields).extend(orig_registry.raw.fields);
|
||||
(registry.raw.field_getters).extend(orig_registry.raw.field_getters);
|
||||
(registry.raw.field_setters).extend(orig_registry.raw.field_setters);
|
||||
(registry.raw.meta_fields).extend(orig_registry.raw.meta_fields);
|
||||
(registry.raw.methods).extend(orig_registry.raw.methods);
|
||||
#[cfg(feature = "async")]
|
||||
registry.async_methods.extend(orig_registry.async_methods);
|
||||
registry.meta_methods.extend(orig_registry.meta_methods);
|
||||
(registry.raw.async_methods).extend(orig_registry.raw.async_methods);
|
||||
(registry.raw.meta_methods).extend(orig_registry.raw.meta_methods);
|
||||
#[cfg(feature = "async")]
|
||||
registry
|
||||
.async_meta_methods
|
||||
.extend(orig_registry.async_meta_methods);
|
||||
(registry.raw.async_meta_methods).extend(orig_registry.raw.async_meta_methods);
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -806,19 +633,25 @@ 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>, UserDataTypeId::Shared(TypeId::of::<T>()));
|
||||
lua_userdata_impl!(UserDataProxy<T>);
|
||||
|
||||
#[cfg(all(feature = "userdata-wrappers", not(feature = "send")))]
|
||||
lua_userdata_impl!(Rc<T> => Rc);
|
||||
lua_userdata_impl!(std::rc::Rc<T>);
|
||||
#[cfg(all(feature = "userdata-wrappers", not(feature = "send")))]
|
||||
lua_userdata_impl!(Rc<RefCell<T>> => RcRefCell);
|
||||
lua_userdata_impl!(std::rc::Rc<std::cell::RefCell<T>>);
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
lua_userdata_impl!(Arc<T> => Arc);
|
||||
lua_userdata_impl!(std::sync::Arc<T>);
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
lua_userdata_impl!(Arc<Mutex<T>> => ArcMutex);
|
||||
lua_userdata_impl!(std::sync::Arc<std::sync::Mutex<T>>);
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
lua_userdata_impl!(Arc<RwLock<T>> => ArcRwLock);
|
||||
lua_userdata_impl!(std::sync::Arc<std::sync::RwLock<T>>);
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
lua_userdata_impl!(Arc<parking_lot::Mutex<T>> => ArcParkingLotMutex);
|
||||
lua_userdata_impl!(std::sync::Arc<parking_lot::Mutex<T>>);
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
lua_userdata_impl!(Arc<parking_lot::RwLock<T>> => ArcParkingLotRwLock);
|
||||
lua_userdata_impl!(std::sync::Arc<parking_lot::RwLock<T>>);
|
||||
|
||||
#[cfg(test)]
|
||||
mod assertions {
|
||||
#[cfg(feature = "send")]
|
||||
static_assertions::assert_impl_all!(super::RawUserDataRegistry: Send);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
use std::any::TypeId;
|
||||
use std::cell::Cell;
|
||||
use std::marker::PhantomData;
|
||||
use std::os::raw::c_int;
|
||||
use std::ptr;
|
||||
|
||||
use super::UserDataStorage;
|
||||
use crate::error::{Error, Result};
|
||||
use crate::util::{get_userdata, rawget_field, rawset_field, take_userdata};
|
||||
|
||||
// This is a trick to check if a type is `Sync` or not.
|
||||
// It uses leaked specialization feature from stdlib.
|
||||
@@ -29,3 +36,424 @@ pub(crate) fn is_sync<T>() -> bool {
|
||||
.clone();
|
||||
is_sync.get()
|
||||
}
|
||||
|
||||
// Userdata type hints, used to match types of wrapped userdata
|
||||
#[derive(Clone, Copy)]
|
||||
pub(crate) struct TypeIdHints {
|
||||
t: TypeId,
|
||||
|
||||
#[cfg(all(feature = "userdata-wrappers", not(feature = "send")))]
|
||||
rc: TypeId,
|
||||
#[cfg(all(feature = "userdata-wrappers", not(feature = "send")))]
|
||||
rc_refcell: TypeId,
|
||||
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
arc: TypeId,
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
arc_mutex: TypeId,
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
arc_rwlock: TypeId,
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
arc_pl_mutex: TypeId,
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
arc_pl_rwlock: TypeId,
|
||||
}
|
||||
|
||||
impl TypeIdHints {
|
||||
pub(crate) fn new<T: 'static>() -> Self {
|
||||
Self {
|
||||
t: TypeId::of::<T>(),
|
||||
|
||||
#[cfg(all(feature = "userdata-wrappers", not(feature = "send")))]
|
||||
rc: TypeId::of::<std::rc::Rc<T>>(),
|
||||
#[cfg(all(feature = "userdata-wrappers", not(feature = "send")))]
|
||||
rc_refcell: TypeId::of::<std::rc::Rc<std::cell::RefCell<T>>>(),
|
||||
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
arc: TypeId::of::<std::sync::Arc<T>>(),
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
arc_mutex: TypeId::of::<std::sync::Arc<std::sync::Mutex<T>>>(),
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
arc_rwlock: TypeId::of::<std::sync::Arc<std::sync::RwLock<T>>>(),
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
arc_pl_mutex: TypeId::of::<std::sync::Arc<parking_lot::Mutex<T>>>(),
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
arc_pl_rwlock: TypeId::of::<std::sync::Arc<parking_lot::RwLock<T>>>(),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub(crate) fn type_id(&self) -> TypeId {
|
||||
self.t
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn borrow_userdata_scoped<T, R>(
|
||||
state: *mut ffi::lua_State,
|
||||
idx: c_int,
|
||||
type_id: Option<TypeId>,
|
||||
type_hints: TypeIdHints,
|
||||
f: impl FnOnce(&T) -> R,
|
||||
) -> Result<R> {
|
||||
match type_id {
|
||||
Some(type_id) if type_id == type_hints.t => {
|
||||
let ud = get_userdata::<UserDataStorage<T>>(state, idx);
|
||||
(*ud).try_borrow_scoped(|ud| f(ud))
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "userdata-wrappers", not(feature = "send")))]
|
||||
Some(type_id) if type_id == type_hints.rc => {
|
||||
let ud = get_userdata::<UserDataStorage<std::rc::Rc<T>>>(state, idx);
|
||||
(*ud).try_borrow_scoped(|ud| f(ud))
|
||||
}
|
||||
#[cfg(all(feature = "userdata-wrappers", not(feature = "send")))]
|
||||
Some(type_id) if type_id == type_hints.rc_refcell => {
|
||||
let ud = get_userdata::<UserDataStorage<std::rc::Rc<std::cell::RefCell<T>>>>(state, idx);
|
||||
(*ud).try_borrow_scoped(|ud| {
|
||||
let ud = ud.try_borrow().map_err(|_| Error::UserDataBorrowError)?;
|
||||
Ok(f(&ud))
|
||||
})?
|
||||
}
|
||||
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
Some(type_id) if type_id == type_hints.arc => {
|
||||
let ud = get_userdata::<UserDataStorage<std::sync::Arc<T>>>(state, idx);
|
||||
(*ud).try_borrow_scoped(|ud| f(ud))
|
||||
}
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
Some(type_id) if type_id == type_hints.arc_mutex => {
|
||||
let ud = get_userdata::<UserDataStorage<std::sync::Arc<std::sync::Mutex<T>>>>(state, idx);
|
||||
(*ud).try_borrow_scoped(|ud| {
|
||||
let ud = ud.try_lock().map_err(|_| Error::UserDataBorrowError)?;
|
||||
Ok(f(&ud))
|
||||
})?
|
||||
}
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
Some(type_id) if type_id == type_hints.arc_rwlock => {
|
||||
let ud = get_userdata::<UserDataStorage<std::sync::Arc<std::sync::RwLock<T>>>>(state, idx);
|
||||
(*ud).try_borrow_scoped(|ud| {
|
||||
let ud = ud.try_read().map_err(|_| Error::UserDataBorrowError)?;
|
||||
Ok(f(&ud))
|
||||
})?
|
||||
}
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
Some(type_id) if type_id == type_hints.arc_pl_mutex => {
|
||||
let ud = get_userdata::<UserDataStorage<std::sync::Arc<parking_lot::Mutex<T>>>>(state, idx);
|
||||
(*ud).try_borrow_scoped(|ud| {
|
||||
let ud = ud.try_lock().ok_or(Error::UserDataBorrowError)?;
|
||||
Ok(f(&ud))
|
||||
})?
|
||||
}
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
Some(type_id) if type_id == type_hints.arc_pl_rwlock => {
|
||||
let ud = get_userdata::<UserDataStorage<std::sync::Arc<parking_lot::RwLock<T>>>>(state, idx);
|
||||
(*ud).try_borrow_scoped(|ud| {
|
||||
let ud = ud.try_read().ok_or(Error::UserDataBorrowError)?;
|
||||
Ok(f(&ud))
|
||||
})?
|
||||
}
|
||||
_ => Err(Error::UserDataTypeMismatch),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn borrow_userdata_scoped_mut<T, R>(
|
||||
state: *mut ffi::lua_State,
|
||||
idx: c_int,
|
||||
type_id: Option<TypeId>,
|
||||
type_hints: TypeIdHints,
|
||||
f: impl FnOnce(&mut T) -> R,
|
||||
) -> Result<R> {
|
||||
match type_id {
|
||||
Some(type_id) if type_id == type_hints.t => {
|
||||
let ud = get_userdata::<UserDataStorage<T>>(state, idx);
|
||||
(*ud).try_borrow_scoped_mut(|ud| f(ud))
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "userdata-wrappers", not(feature = "send")))]
|
||||
Some(type_id) if type_id == type_hints.rc => {
|
||||
let ud = get_userdata::<UserDataStorage<std::rc::Rc<T>>>(state, idx);
|
||||
(*ud).try_borrow_scoped_mut(|ud| match std::rc::Rc::get_mut(ud) {
|
||||
Some(ud) => Ok(f(ud)),
|
||||
None => Err(Error::UserDataBorrowMutError),
|
||||
})?
|
||||
}
|
||||
#[cfg(all(feature = "userdata-wrappers", not(feature = "send")))]
|
||||
Some(type_id) if type_id == type_hints.rc_refcell => {
|
||||
let ud = get_userdata::<UserDataStorage<std::rc::Rc<std::cell::RefCell<T>>>>(state, idx);
|
||||
(*ud).try_borrow_scoped(|ud| {
|
||||
let mut ud = ud.try_borrow_mut().map_err(|_| Error::UserDataBorrowMutError)?;
|
||||
Ok(f(&mut ud))
|
||||
})?
|
||||
}
|
||||
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
Some(type_id) if type_id == type_hints.arc => {
|
||||
let ud = get_userdata::<UserDataStorage<std::sync::Arc<T>>>(state, idx);
|
||||
(*ud).try_borrow_scoped_mut(|ud| match std::sync::Arc::get_mut(ud) {
|
||||
Some(ud) => Ok(f(ud)),
|
||||
None => Err(Error::UserDataBorrowMutError),
|
||||
})?
|
||||
}
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
Some(type_id) if type_id == type_hints.arc_mutex => {
|
||||
let ud = get_userdata::<UserDataStorage<std::sync::Arc<std::sync::Mutex<T>>>>(state, idx);
|
||||
(*ud).try_borrow_scoped_mut(|ud| {
|
||||
let mut ud = ud.try_lock().map_err(|_| Error::UserDataBorrowMutError)?;
|
||||
Ok(f(&mut ud))
|
||||
})?
|
||||
}
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
Some(type_id) if type_id == type_hints.arc_rwlock => {
|
||||
let ud = get_userdata::<UserDataStorage<std::sync::Arc<std::sync::RwLock<T>>>>(state, idx);
|
||||
(*ud).try_borrow_scoped_mut(|ud| {
|
||||
let mut ud = ud.try_write().map_err(|_| Error::UserDataBorrowMutError)?;
|
||||
Ok(f(&mut ud))
|
||||
})?
|
||||
}
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
Some(type_id) if type_id == type_hints.arc_pl_mutex => {
|
||||
let ud = get_userdata::<UserDataStorage<std::sync::Arc<parking_lot::Mutex<T>>>>(state, idx);
|
||||
(*ud).try_borrow_scoped_mut(|ud| {
|
||||
let mut ud = ud.try_lock().ok_or(Error::UserDataBorrowMutError)?;
|
||||
Ok(f(&mut ud))
|
||||
})?
|
||||
}
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
Some(type_id) if type_id == type_hints.arc_pl_rwlock => {
|
||||
let ud = get_userdata::<UserDataStorage<std::sync::Arc<parking_lot::RwLock<T>>>>(state, idx);
|
||||
(*ud).try_borrow_scoped_mut(|ud| {
|
||||
let mut ud = ud.try_write().ok_or(Error::UserDataBorrowMutError)?;
|
||||
Ok(f(&mut ud))
|
||||
})?
|
||||
}
|
||||
_ => Err(Error::UserDataTypeMismatch),
|
||||
}
|
||||
}
|
||||
|
||||
// Populates the given table with the appropriate members to be a userdata metatable for the given
|
||||
// type. This function takes the given table at the `metatable` index, and adds an appropriate
|
||||
// `__gc` member to it for the given type and a `__metatable` entry to protect the table from script
|
||||
// access. The function also, if given a `field_getters` or `methods` tables, will create an
|
||||
// `__index` metamethod (capturing previous one) to lookup in `field_getters` first, then `methods`
|
||||
// and falling back to the captured `__index` if no matches found.
|
||||
// The same is also applicable for `__newindex` metamethod and `field_setters` table.
|
||||
// Internally uses 9 stack spaces and does not call checkstack.
|
||||
pub(crate) unsafe fn init_userdata_metatable(
|
||||
state: *mut ffi::lua_State,
|
||||
metatable: c_int,
|
||||
field_getters: Option<c_int>,
|
||||
field_setters: Option<c_int>,
|
||||
methods: Option<c_int>,
|
||||
) -> Result<()> {
|
||||
if field_getters.is_some() || methods.is_some() {
|
||||
// Push `__index` generator function
|
||||
init_userdata_metatable_index(state)?;
|
||||
|
||||
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] {
|
||||
if let Some(idx) = idx {
|
||||
ffi::lua_pushvalue(state, idx);
|
||||
} else {
|
||||
ffi::lua_pushnil(state);
|
||||
}
|
||||
}
|
||||
|
||||
// Generate `__index`
|
||||
protect_lua!(state, 4, 1, fn(state) ffi::lua_call(state, 3, 1))?;
|
||||
}
|
||||
_ => mlua_panic!("improper `__index` type: {}", index_type),
|
||||
}
|
||||
|
||||
rawset_field(state, metatable, "__index")?;
|
||||
}
|
||||
|
||||
if let Some(field_setters) = field_setters {
|
||||
// Push `__newindex` generator function
|
||||
init_userdata_metatable_newindex(state)?;
|
||||
|
||||
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),
|
||||
}
|
||||
|
||||
rawset_field(state, metatable, "__newindex")?;
|
||||
}
|
||||
|
||||
ffi::lua_pushboolean(state, 0);
|
||||
rawset_field(state, metatable, "__metatable")?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
unsafe extern "C-unwind" fn lua_error_impl(state: *mut ffi::lua_State) -> c_int {
|
||||
ffi::lua_error(state);
|
||||
}
|
||||
|
||||
unsafe extern "C-unwind" fn lua_isfunction_impl(state: *mut ffi::lua_State) -> c_int {
|
||||
ffi::lua_pushboolean(state, ffi::lua_isfunction(state, -1));
|
||||
1
|
||||
}
|
||||
|
||||
unsafe extern "C-unwind" fn lua_istable_impl(state: *mut ffi::lua_State) -> c_int {
|
||||
ffi::lua_pushboolean(state, ffi::lua_istable(state, -1));
|
||||
1
|
||||
}
|
||||
|
||||
unsafe fn init_userdata_metatable_index(state: *mut ffi::lua_State) -> Result<()> {
|
||||
let index_key = &USERDATA_METATABLE_INDEX as *const u8 as *const _;
|
||||
if ffi::lua_rawgetp(state, ffi::LUA_REGISTRYINDEX, index_key) == ffi::LUA_TFUNCTION {
|
||||
return Ok(());
|
||||
}
|
||||
ffi::lua_pop(state, 1);
|
||||
|
||||
// Create and cache `__index` generator
|
||||
let code = cr#"
|
||||
local error, isfunction, istable = ...
|
||||
return function (__index, field_getters, methods)
|
||||
-- Common case: has field getters and index is a table
|
||||
if field_getters ~= nil and methods == nil and istable(__index) then
|
||||
return function (self, key)
|
||||
local field_getter = field_getters[key]
|
||||
if field_getter ~= nil then
|
||||
return field_getter(self)
|
||||
end
|
||||
return __index[key]
|
||||
end
|
||||
end
|
||||
|
||||
return function (self, key)
|
||||
if field_getters ~= nil then
|
||||
local field_getter = field_getters[key]
|
||||
if field_getter ~= nil then
|
||||
return field_getter(self)
|
||||
end
|
||||
end
|
||||
|
||||
if methods ~= nil then
|
||||
local method = methods[key]
|
||||
if method ~= nil then
|
||||
return method
|
||||
end
|
||||
end
|
||||
|
||||
if isfunction(__index) then
|
||||
return __index(self, key)
|
||||
elseif __index == nil then
|
||||
error("attempt to get an unknown field '"..key.."'")
|
||||
else
|
||||
return __index[key]
|
||||
end
|
||||
end
|
||||
end
|
||||
"#;
|
||||
protect_lua!(state, 0, 1, |state| {
|
||||
let ret = ffi::luaL_loadbuffer(state, code.as_ptr(), code.count_bytes(), cstr!("=__mlua_index"));
|
||||
if ret != ffi::LUA_OK {
|
||||
ffi::lua_error(state);
|
||||
}
|
||||
ffi::lua_pushcfunction(state, lua_error_impl);
|
||||
ffi::lua_pushcfunction(state, lua_isfunction_impl);
|
||||
ffi::lua_pushcfunction(state, lua_istable_impl);
|
||||
ffi::lua_call(state, 3, 1);
|
||||
|
||||
#[cfg(feature = "luau-jit")]
|
||||
if ffi::luau_codegen_supported() != 0 {
|
||||
ffi::luau_codegen_compile(state, -1);
|
||||
}
|
||||
|
||||
// Store in the registry
|
||||
ffi::lua_pushvalue(state, -1);
|
||||
ffi::lua_rawsetp(state, ffi::LUA_REGISTRYINDEX, index_key);
|
||||
})
|
||||
}
|
||||
|
||||
unsafe fn init_userdata_metatable_newindex(state: *mut ffi::lua_State) -> Result<()> {
|
||||
let newindex_key = &USERDATA_METATABLE_NEWINDEX as *const u8 as *const _;
|
||||
if ffi::lua_rawgetp(state, ffi::LUA_REGISTRYINDEX, newindex_key) == ffi::LUA_TFUNCTION {
|
||||
return Ok(());
|
||||
}
|
||||
ffi::lua_pop(state, 1);
|
||||
|
||||
// Create and cache `__newindex` generator
|
||||
let code = cr#"
|
||||
local error, isfunction = ...
|
||||
return function (__newindex, field_setters)
|
||||
return function (self, key, value)
|
||||
if field_setters ~= nil then
|
||||
local field_setter = field_setters[key]
|
||||
if field_setter ~= nil then
|
||||
field_setter(self, value)
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
if isfunction(__newindex) then
|
||||
__newindex(self, key, value)
|
||||
elseif __newindex == nil then
|
||||
error("attempt to set an unknown field '"..key.."'")
|
||||
else
|
||||
__newindex[key] = value
|
||||
end
|
||||
end
|
||||
end
|
||||
"#;
|
||||
protect_lua!(state, 0, 1, |state| {
|
||||
let code_len = code.count_bytes();
|
||||
let ret = ffi::luaL_loadbuffer(state, code.as_ptr(), code_len, cstr!("=__mlua_newindex"));
|
||||
if ret != ffi::LUA_OK {
|
||||
ffi::lua_error(state);
|
||||
}
|
||||
ffi::lua_pushcfunction(state, lua_error_impl);
|
||||
ffi::lua_pushcfunction(state, lua_isfunction_impl);
|
||||
ffi::lua_call(state, 2, 1);
|
||||
|
||||
#[cfg(feature = "luau-jit")]
|
||||
if ffi::luau_codegen_supported() != 0 {
|
||||
ffi::luau_codegen_compile(state, -1);
|
||||
}
|
||||
|
||||
// Store in the registry
|
||||
ffi::lua_pushvalue(state, -1);
|
||||
ffi::lua_rawsetp(state, ffi::LUA_REGISTRYINDEX, newindex_key);
|
||||
})
|
||||
}
|
||||
|
||||
// This method is called by Lua GC when it's time to collect the userdata.
|
||||
//
|
||||
// This method is usually used to collect internal userdata.
|
||||
#[cfg(not(feature = "luau"))]
|
||||
pub(crate) unsafe extern "C-unwind" fn collect_userdata<T>(state: *mut ffi::lua_State) -> c_int {
|
||||
let ud = get_userdata::<T>(state, -1);
|
||||
ptr::drop_in_place(ud);
|
||||
0
|
||||
}
|
||||
|
||||
// This method is called by Luau GC when it's time to collect the userdata.
|
||||
#[cfg(feature = "luau")]
|
||||
pub(crate) unsafe extern "C-unwind" fn collect_userdata<T>(ud: *mut std::os::raw::c_void) {
|
||||
ptr::drop_in_place(ud as *mut T);
|
||||
}
|
||||
|
||||
// This method can be called by user or Lua GC to destroy the userdata.
|
||||
// It checks if the userdata is safe to destroy and sets the "destroyed" metatable
|
||||
// to prevent further GC collection.
|
||||
pub(super) unsafe extern "C-unwind" fn destroy_userdata_storage<T>(state: *mut ffi::lua_State) -> c_int {
|
||||
let ud = get_userdata::<UserDataStorage<T>>(state, -1);
|
||||
if (*ud).is_safe_to_destroy() {
|
||||
take_userdata::<UserDataStorage<T>>(state);
|
||||
ffi::lua_pushboolean(state, 1);
|
||||
} else {
|
||||
ffi::lua_pushboolean(state, 0);
|
||||
}
|
||||
1
|
||||
}
|
||||
|
||||
static USERDATA_METATABLE_INDEX: u8 = 0;
|
||||
static USERDATA_METATABLE_NEWINDEX: u8 = 0;
|
||||
|
||||
+5
-15
@@ -9,9 +9,8 @@ use std::sync::Arc;
|
||||
use crate::error::{Error, Result};
|
||||
use crate::memory::MemoryState;
|
||||
use crate::util::{
|
||||
check_stack, get_internal_metatable, get_internal_userdata, init_internal_metatable,
|
||||
push_internal_userdata, push_string, push_table, rawset_field, to_string, TypeKey,
|
||||
DESTRUCTED_USERDATA_METATABLE,
|
||||
check_stack, get_internal_userdata, init_internal_metatable, push_internal_userdata, push_string,
|
||||
push_table, rawset_field, to_string, TypeKey, DESTRUCTED_USERDATA_METATABLE,
|
||||
};
|
||||
|
||||
static WRAPPED_FAILURE_TYPE_KEY: u8 = 0;
|
||||
@@ -31,12 +30,8 @@ impl TypeKey for WrappedFailure {
|
||||
|
||||
impl WrappedFailure {
|
||||
pub(crate) unsafe fn new_userdata(state: *mut ffi::lua_State) -> *mut Self {
|
||||
#[cfg(feature = "luau")]
|
||||
let ud = ffi::lua_newuserdata_t::<Self>(state);
|
||||
#[cfg(not(feature = "luau"))]
|
||||
let ud = ffi::lua_newuserdata(state, std::mem::size_of::<Self>()) as *mut Self;
|
||||
ptr::write(ud, WrappedFailure::None);
|
||||
ud
|
||||
// Unprotected calls always return `Ok`
|
||||
push_internal_userdata(state, WrappedFailure::None, false).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -90,16 +85,11 @@ where
|
||||
let cause = Arc::new(err);
|
||||
let wrapped_error = WrappedFailure::Error(Error::CallbackError { traceback, cause });
|
||||
ptr::write(ud, wrapped_error);
|
||||
get_internal_metatable::<WrappedFailure>(state);
|
||||
ffi::lua_setmetatable(state, -2);
|
||||
|
||||
ffi::lua_error(state)
|
||||
}
|
||||
Err(p) => {
|
||||
ffi::lua_settop(state, 1);
|
||||
ptr::write(ud, WrappedFailure::Panic(Some(p)));
|
||||
get_internal_metatable::<WrappedFailure>(state);
|
||||
ffi::lua_setmetatable(state, -2);
|
||||
ffi::lua_error(state)
|
||||
}
|
||||
}
|
||||
@@ -262,7 +252,7 @@ where
|
||||
|
||||
pub(crate) unsafe extern "C-unwind" fn error_traceback(state: *mut ffi::lua_State) -> c_int {
|
||||
// Luau calls error handler for memory allocation errors, skip it
|
||||
// See https://github.com/Roblox/luau/issues/880
|
||||
// See https://github.com/luau-lang/luau/issues/880
|
||||
#[cfg(feature = "luau")]
|
||||
if MemoryState::limit_reached(state) {
|
||||
return 0;
|
||||
|
||||
+1
-2
@@ -13,13 +13,12 @@ pub(crate) use short_names::short_type_name;
|
||||
pub(crate) use types::TypeKey;
|
||||
pub(crate) use userdata::{
|
||||
get_destructed_userdata_metatable, get_internal_metatable, get_internal_userdata, get_userdata,
|
||||
init_internal_metatable, init_userdata_metatable, push_internal_userdata, take_userdata,
|
||||
init_internal_metatable, push_internal_userdata, push_userdata, take_userdata,
|
||||
DESTRUCTED_USERDATA_METATABLE,
|
||||
};
|
||||
|
||||
#[cfg(not(feature = "luau"))]
|
||||
pub(crate) use userdata::push_uninit_userdata;
|
||||
pub(crate) use userdata::push_userdata;
|
||||
|
||||
// Checks that Lua has enough free stack space for future stack operations. On failure, this will
|
||||
// panic with an internal error message.
|
||||
|
||||
+39
-223
@@ -1,9 +1,9 @@
|
||||
use std::ffi::CStr;
|
||||
use std::os::raw::{c_int, c_void};
|
||||
use std::{ptr, str};
|
||||
use std::{mem, ptr};
|
||||
|
||||
use crate::error::Result;
|
||||
use crate::util::{check_stack, get_metatable_ptr, push_table, rawget_field, rawset_field, TypeKey};
|
||||
use crate::userdata::collect_userdata;
|
||||
use crate::util::{check_stack, get_metatable_ptr, push_table, rawset_field, TypeKey};
|
||||
|
||||
// Pushes the userdata and attaches a metatable with __gc method.
|
||||
// Internally uses 3 stack spaces, does not call checkstack.
|
||||
@@ -11,11 +11,27 @@ pub(crate) unsafe fn push_internal_userdata<T: TypeKey>(
|
||||
state: *mut ffi::lua_State,
|
||||
t: T,
|
||||
protect: bool,
|
||||
) -> Result<()> {
|
||||
push_userdata(state, t, protect)?;
|
||||
) -> Result<*mut T> {
|
||||
#[cfg(not(feature = "luau"))]
|
||||
let ud_ptr = if protect {
|
||||
protect_lua!(state, 0, 1, move |state| {
|
||||
ffi::lua_newuserdata(state, const { mem::size_of::<T>() }) as *mut T
|
||||
})?
|
||||
} else {
|
||||
ffi::lua_newuserdata(state, const { mem::size_of::<T>() }) as *mut T
|
||||
};
|
||||
|
||||
#[cfg(feature = "luau")]
|
||||
let ud_ptr = if protect {
|
||||
protect_lua!(state, 0, 1, move |state| ffi::lua_newuserdata_t::<T>(state))?
|
||||
} else {
|
||||
ffi::lua_newuserdata_t::<T>(state)
|
||||
};
|
||||
|
||||
ptr::write(ud_ptr, t);
|
||||
get_internal_metatable::<T>(state);
|
||||
ffi::lua_setmetatable(state, -2);
|
||||
Ok(())
|
||||
Ok(ud_ptr)
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
@@ -36,7 +52,7 @@ pub(crate) unsafe fn init_internal_metatable<T: TypeKey>(
|
||||
|
||||
#[cfg(not(feature = "luau"))]
|
||||
{
|
||||
ffi::lua_pushcfunction(state, userdata_destructor::<T>);
|
||||
ffi::lua_pushcfunction(state, collect_userdata::<T>);
|
||||
rawset_field(state, -2, "__gc")?;
|
||||
}
|
||||
|
||||
@@ -82,24 +98,34 @@ pub(crate) unsafe fn get_internal_userdata<T: TypeKey>(
|
||||
pub(crate) unsafe fn push_uninit_userdata<T>(state: *mut ffi::lua_State, protect: bool) -> Result<*mut T> {
|
||||
if protect {
|
||||
protect_lua!(state, 0, 1, |state| {
|
||||
ffi::lua_newuserdata(state, std::mem::size_of::<T>()) as *mut T
|
||||
ffi::lua_newuserdata(state, const { mem::size_of::<T>() }) as *mut T
|
||||
})
|
||||
} else {
|
||||
Ok(ffi::lua_newuserdata(state, std::mem::size_of::<T>()) as *mut T)
|
||||
Ok(ffi::lua_newuserdata(state, const { mem::size_of::<T>() }) as *mut T)
|
||||
}
|
||||
}
|
||||
|
||||
// Internally uses 3 stack spaces, does not call checkstack.
|
||||
#[inline]
|
||||
pub(crate) unsafe fn push_userdata<T>(state: *mut ffi::lua_State, t: T, protect: bool) -> Result<*mut T> {
|
||||
let size = const { mem::size_of::<T>() };
|
||||
|
||||
#[cfg(not(feature = "luau"))]
|
||||
let ud_ptr = push_uninit_userdata(state, protect)?;
|
||||
let ud_ptr = if protect {
|
||||
protect_lua!(state, 0, 1, move |state| ffi::lua_newuserdata(state, size))?
|
||||
} else {
|
||||
ffi::lua_newuserdata(state, size)
|
||||
} as *mut T;
|
||||
|
||||
#[cfg(feature = "luau")]
|
||||
let ud_ptr = if protect {
|
||||
protect_lua!(state, 0, 1, |state| { ffi::lua_newuserdata_t::<T>(state) })?
|
||||
protect_lua!(state, 0, 1, |state| {
|
||||
ffi::lua_newuserdatadtor(state, size, collect_userdata::<T>)
|
||||
})?
|
||||
} else {
|
||||
ffi::lua_newuserdata_t::<T>(state)
|
||||
};
|
||||
ffi::lua_newuserdatadtor(state, size, collect_userdata::<T>)
|
||||
} as *mut T;
|
||||
|
||||
ptr::write(ud_ptr, t);
|
||||
Ok(ud_ptr)
|
||||
}
|
||||
@@ -138,214 +164,4 @@ pub(crate) unsafe fn get_destructed_userdata_metatable(state: *mut ffi::lua_Stat
|
||||
ffi::lua_rawgetp(state, ffi::LUA_REGISTRYINDEX, key);
|
||||
}
|
||||
|
||||
// Populates the given table with the appropriate members to be a userdata metatable for the given
|
||||
// type. This function takes the given table at the `metatable` index, and adds an appropriate
|
||||
// `__gc` member to it for the given type and a `__metatable` entry to protect the table from script
|
||||
// access. The function also, if given a `field_getters` or `methods` tables, will create an
|
||||
// `__index` metamethod (capturing previous one) to lookup in `field_getters` first, then `methods`
|
||||
// and falling back to the captured `__index` if no matches found.
|
||||
// The same is also applicable for `__newindex` metamethod and `field_setters` table.
|
||||
// Internally uses 9 stack spaces and does not call checkstack.
|
||||
pub(crate) unsafe fn init_userdata_metatable(
|
||||
state: *mut ffi::lua_State,
|
||||
metatable: c_int,
|
||||
field_getters: Option<c_int>,
|
||||
field_setters: Option<c_int>,
|
||||
methods: Option<c_int>,
|
||||
) -> Result<()> {
|
||||
if field_getters.is_some() || methods.is_some() {
|
||||
// Push `__index` generator function
|
||||
init_userdata_metatable_index(state)?;
|
||||
|
||||
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] {
|
||||
if let Some(idx) = idx {
|
||||
ffi::lua_pushvalue(state, idx);
|
||||
} else {
|
||||
ffi::lua_pushnil(state);
|
||||
}
|
||||
}
|
||||
|
||||
// Generate `__index`
|
||||
protect_lua!(state, 4, 1, fn(state) ffi::lua_call(state, 3, 1))?;
|
||||
}
|
||||
_ => mlua_panic!("improper `__index` type: {}", index_type),
|
||||
}
|
||||
|
||||
rawset_field(state, metatable, "__index")?;
|
||||
}
|
||||
|
||||
if let Some(field_setters) = field_setters {
|
||||
// Push `__newindex` generator function
|
||||
init_userdata_metatable_newindex(state)?;
|
||||
|
||||
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),
|
||||
}
|
||||
|
||||
rawset_field(state, metatable, "__newindex")?;
|
||||
}
|
||||
|
||||
ffi::lua_pushboolean(state, 0);
|
||||
rawset_field(state, metatable, "__metatable")?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
unsafe extern "C-unwind" fn lua_error_impl(state: *mut ffi::lua_State) -> c_int {
|
||||
ffi::lua_error(state);
|
||||
}
|
||||
|
||||
unsafe extern "C-unwind" fn lua_isfunction_impl(state: *mut ffi::lua_State) -> c_int {
|
||||
ffi::lua_pushboolean(state, ffi::lua_isfunction(state, -1));
|
||||
1
|
||||
}
|
||||
|
||||
unsafe extern "C-unwind" fn lua_istable_impl(state: *mut ffi::lua_State) -> c_int {
|
||||
ffi::lua_pushboolean(state, ffi::lua_istable(state, -1));
|
||||
1
|
||||
}
|
||||
|
||||
unsafe fn init_userdata_metatable_index(state: *mut ffi::lua_State) -> Result<()> {
|
||||
let index_key = &USERDATA_METATABLE_INDEX as *const u8 as *const _;
|
||||
if ffi::lua_rawgetp(state, ffi::LUA_REGISTRYINDEX, index_key) == ffi::LUA_TFUNCTION {
|
||||
return Ok(());
|
||||
}
|
||||
ffi::lua_pop(state, 1);
|
||||
|
||||
// Create and cache `__index` generator
|
||||
let code = cstr!(
|
||||
r#"
|
||||
local error, isfunction, istable = ...
|
||||
return function (__index, field_getters, methods)
|
||||
-- Common case: has field getters and index is a table
|
||||
if field_getters ~= nil and methods == nil and istable(__index) then
|
||||
return function (self, key)
|
||||
local field_getter = field_getters[key]
|
||||
if field_getter ~= nil then
|
||||
return field_getter(self)
|
||||
end
|
||||
return __index[key]
|
||||
end
|
||||
end
|
||||
|
||||
return function (self, key)
|
||||
if field_getters ~= nil then
|
||||
local field_getter = field_getters[key]
|
||||
if field_getter ~= nil then
|
||||
return field_getter(self)
|
||||
end
|
||||
end
|
||||
|
||||
if methods ~= nil then
|
||||
local method = methods[key]
|
||||
if method ~= nil then
|
||||
return method
|
||||
end
|
||||
end
|
||||
|
||||
if isfunction(__index) then
|
||||
return __index(self, key)
|
||||
elseif __index == nil then
|
||||
error("attempt to get an unknown field '"..key.."'")
|
||||
else
|
||||
return __index[key]
|
||||
end
|
||||
end
|
||||
end
|
||||
"#
|
||||
);
|
||||
let code_len = CStr::from_ptr(code).to_bytes().len();
|
||||
protect_lua!(state, 0, 1, |state| {
|
||||
let ret = ffi::luaL_loadbuffer(state, code, code_len, cstr!("__mlua_index"));
|
||||
if ret != ffi::LUA_OK {
|
||||
ffi::lua_error(state);
|
||||
}
|
||||
ffi::lua_pushcfunction(state, lua_error_impl);
|
||||
ffi::lua_pushcfunction(state, lua_isfunction_impl);
|
||||
ffi::lua_pushcfunction(state, lua_istable_impl);
|
||||
ffi::lua_call(state, 3, 1);
|
||||
|
||||
#[cfg(feature = "luau-jit")]
|
||||
if ffi::luau_codegen_supported() != 0 {
|
||||
ffi::luau_codegen_compile(state, -1);
|
||||
}
|
||||
|
||||
// Store in the registry
|
||||
ffi::lua_pushvalue(state, -1);
|
||||
ffi::lua_rawsetp(state, ffi::LUA_REGISTRYINDEX, index_key);
|
||||
})
|
||||
}
|
||||
|
||||
unsafe fn init_userdata_metatable_newindex(state: *mut ffi::lua_State) -> Result<()> {
|
||||
let newindex_key = &USERDATA_METATABLE_NEWINDEX as *const u8 as *const _;
|
||||
if ffi::lua_rawgetp(state, ffi::LUA_REGISTRYINDEX, newindex_key) == ffi::LUA_TFUNCTION {
|
||||
return Ok(());
|
||||
}
|
||||
ffi::lua_pop(state, 1);
|
||||
|
||||
// Create and cache `__newindex` generator
|
||||
let code = cstr!(
|
||||
r#"
|
||||
local error, isfunction = ...
|
||||
return function (__newindex, field_setters)
|
||||
return function (self, key, value)
|
||||
if field_setters ~= nil then
|
||||
local field_setter = field_setters[key]
|
||||
if field_setter ~= nil then
|
||||
field_setter(self, value)
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
if isfunction(__newindex) then
|
||||
__newindex(self, key, value)
|
||||
elseif __newindex == nil then
|
||||
error("attempt to set an unknown field '"..key.."'")
|
||||
else
|
||||
__newindex[key] = value
|
||||
end
|
||||
end
|
||||
end
|
||||
"#
|
||||
);
|
||||
let code_len = CStr::from_ptr(code).to_bytes().len();
|
||||
protect_lua!(state, 0, 1, |state| {
|
||||
let ret = ffi::luaL_loadbuffer(state, code, code_len, cstr!("__mlua_newindex"));
|
||||
if ret != ffi::LUA_OK {
|
||||
ffi::lua_error(state);
|
||||
}
|
||||
ffi::lua_pushcfunction(state, lua_error_impl);
|
||||
ffi::lua_pushcfunction(state, lua_isfunction_impl);
|
||||
ffi::lua_call(state, 2, 1);
|
||||
|
||||
#[cfg(feature = "luau-jit")]
|
||||
if ffi::luau_codegen_supported() != 0 {
|
||||
ffi::luau_codegen_compile(state, -1);
|
||||
}
|
||||
|
||||
// Store in the registry
|
||||
ffi::lua_pushvalue(state, -1);
|
||||
ffi::lua_rawsetp(state, ffi::LUA_REGISTRYINDEX, newindex_key);
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "luau"))]
|
||||
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);
|
||||
0
|
||||
}
|
||||
|
||||
pub(crate) static DESTRUCTED_USERDATA_METATABLE: u8 = 0;
|
||||
static USERDATA_METATABLE_INDEX: u8 = 0;
|
||||
static USERDATA_METATABLE_NEWINDEX: u8 = 0;
|
||||
|
||||
+16
-2
@@ -67,8 +67,7 @@ pub enum Value {
|
||||
/// `Error` is a special builtin userdata type. When received from Lua it is implicitly cloned.
|
||||
Error(Box<Error>),
|
||||
/// Any other value not known to mlua (eg. LuaJIT CData).
|
||||
#[allow(private_interfaces)]
|
||||
Other(ValueRef),
|
||||
Other(#[doc(hidden)] ValueRef),
|
||||
}
|
||||
|
||||
pub use self::Value::Nil;
|
||||
@@ -570,6 +569,12 @@ impl Value {
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Value {
|
||||
fn default() -> Self {
|
||||
Self::Nil
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Value {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
if fmt.alternate() {
|
||||
@@ -692,6 +697,15 @@ impl<'a> SerializableValue<'a> {
|
||||
self.options.sort_keys = enabled;
|
||||
self
|
||||
}
|
||||
|
||||
/// If true, empty Lua tables will be encoded as array, instead of map.
|
||||
///
|
||||
/// Default: **false**
|
||||
#[must_use]
|
||||
pub const fn encode_empty_tables_as_array(mut self, enabled: bool) -> Self {
|
||||
self.options.encode_empty_tables_as_array = enabled;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
|
||||
+36
-1
@@ -1,6 +1,24 @@
|
||||
use std::{fs, io};
|
||||
|
||||
use mlua::{Lua, Result};
|
||||
use mlua::{Chunk, ChunkMode, Lua, Result};
|
||||
|
||||
#[test]
|
||||
fn test_chunk_methods() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
#[cfg(unix)]
|
||||
assert!(lua.load("return 123").name().contains("tests/chunk.rs"));
|
||||
let chunk2 = lua.load("return 123").set_name("@new_name");
|
||||
assert_eq!(chunk2.name(), "@new_name");
|
||||
|
||||
let env = lua.create_table_from([("a", 987)])?;
|
||||
let chunk3 = lua.load("return a").set_environment(env.clone());
|
||||
assert_eq!(chunk3.environment().unwrap(), &env);
|
||||
assert_eq!(chunk3.mode(), ChunkMode::Text);
|
||||
assert_eq!(chunk3.call::<i32>(())?, 987);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_chunk_path() -> Result<()> {
|
||||
@@ -121,3 +139,20 @@ fn test_compiler() -> Result<()> {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_chunk_wrap() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let f = Chunk::wrap("return 123");
|
||||
lua.globals().set("f", f)?;
|
||||
lua.load("assert(f() == 123)").exec().unwrap();
|
||||
|
||||
lua.globals().set("f2", Chunk::wrap("c()"))?;
|
||||
assert!(
|
||||
(lua.load("f2()").exec().err().unwrap().to_string()).contains(file!()),
|
||||
"wrong chunk location"
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1,32 +1,28 @@
|
||||
error[E0277]: the type `UnsafeCell<mlua::state::raw::RawLua>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
|
||||
error[E0277]: the type `UnsafeCell<*mut lua_State>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
|
||||
--> tests/compile/lua_norefunwindsafe.rs:7:18
|
||||
|
|
||||
7 | catch_unwind(|| lua.create_table().unwrap());
|
||||
| ------------ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `UnsafeCell<mlua::state::raw::RawLua>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
|
||||
| ------------ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `UnsafeCell<*mut lua_State>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
|
||||
| |
|
||||
| required by a bound introduced by this call
|
||||
|
|
||||
= help: within `Lua`, the trait `RefUnwindSafe` is not implemented for `UnsafeCell<mlua::state::raw::RawLua>`, which is required by `{closure@$DIR/tests/compile/lua_norefunwindsafe.rs:7:18: 7:20}: UnwindSafe`
|
||||
note: required because it appears within the type `lock_api::remutex::ReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId, mlua::state::raw::RawLua>`
|
||||
--> $CARGO/lock_api-0.4.12/src/remutex.rs
|
||||
= help: within `mlua::types::sync::inner::ReentrantMutex<mlua::state::raw::RawLua>`, the trait `RefUnwindSafe` is not implemented for `UnsafeCell<*mut lua_State>`
|
||||
note: required because it appears within the type `Cell<*mut lua_State>`
|
||||
--> $RUST/core/src/cell.rs
|
||||
|
|
||||
| pub struct ReentrantMutex<R, G, T: ?Sized> {
|
||||
| ^^^^^^^^^^^^^^
|
||||
note: required because it appears within the type `alloc::sync::ArcInner<lock_api::remutex::ReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId, mlua::state::raw::RawLua>>`
|
||||
--> $RUST/alloc/src/sync.rs
|
||||
| pub struct Cell<T: ?Sized> {
|
||||
| ^^^^
|
||||
note: required because it appears within the type `mlua::state::raw::RawLua`
|
||||
--> src/state/raw.rs
|
||||
|
|
||||
| struct ArcInner<T: ?Sized> {
|
||||
| ^^^^^^^^
|
||||
note: required because it appears within the type `PhantomData<alloc::sync::ArcInner<lock_api::remutex::ReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId, mlua::state::raw::RawLua>>>`
|
||||
--> $RUST/core/src/marker.rs
|
||||
| pub struct RawLua {
|
||||
| ^^^^^^
|
||||
note: required because it appears within the type `mlua::types::sync::inner::ReentrantMutex<mlua::state::raw::RawLua>`
|
||||
--> src/types/sync.rs
|
||||
|
|
||||
| pub struct PhantomData<T: ?Sized>;
|
||||
| ^^^^^^^^^^^
|
||||
note: required because it appears within the type `Arc<lock_api::remutex::ReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId, mlua::state::raw::RawLua>>`
|
||||
--> $RUST/alloc/src/sync.rs
|
||||
|
|
||||
| pub struct Arc<
|
||||
| ^^^
|
||||
| pub(crate) struct ReentrantMutex<T>(T);
|
||||
| ^^^^^^^^^^^^^^
|
||||
= note: required for `Rc<mlua::types::sync::inner::ReentrantMutex<mlua::state::raw::RawLua>>` to implement `RefUnwindSafe`
|
||||
note: required because it appears within the type `Lua`
|
||||
--> src/state.rs
|
||||
|
|
||||
@@ -44,45 +40,27 @@ note: required by a bound in `std::panic::catch_unwind`
|
||||
| pub fn catch_unwind<F: FnOnce() -> R + UnwindSafe, R>(f: F) -> Result<R> {
|
||||
| ^^^^^^^^^^ required by this bound in `catch_unwind`
|
||||
|
||||
error[E0277]: the type `UnsafeCell<usize>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
|
||||
error[E0277]: the type `UnsafeCell<mlua::state::extra::ExtraData>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
|
||||
--> tests/compile/lua_norefunwindsafe.rs:7:18
|
||||
|
|
||||
7 | catch_unwind(|| lua.create_table().unwrap());
|
||||
| ------------ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `UnsafeCell<usize>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
|
||||
| ------------ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `UnsafeCell<mlua::state::extra::ExtraData>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
|
||||
| |
|
||||
| required by a bound introduced by this call
|
||||
|
|
||||
= help: within `Lua`, the trait `RefUnwindSafe` is not implemented for `UnsafeCell<usize>`, which is required by `{closure@$DIR/tests/compile/lua_norefunwindsafe.rs:7:18: 7:20}: UnwindSafe`
|
||||
note: required because it appears within the type `Cell<usize>`
|
||||
--> $RUST/core/src/cell.rs
|
||||
= help: the trait `RefUnwindSafe` is not implemented for `UnsafeCell<mlua::state::extra::ExtraData>`
|
||||
= note: required for `Rc<UnsafeCell<mlua::state::extra::ExtraData>>` to implement `RefUnwindSafe`
|
||||
note: required because it appears within the type `mlua::state::raw::RawLua`
|
||||
--> src/state/raw.rs
|
||||
|
|
||||
| pub struct Cell<T: ?Sized> {
|
||||
| ^^^^
|
||||
note: required because it appears within the type `lock_api::remutex::RawReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId>`
|
||||
--> $CARGO/lock_api-0.4.12/src/remutex.rs
|
||||
| pub struct RawLua {
|
||||
| ^^^^^^
|
||||
note: required because it appears within the type `mlua::types::sync::inner::ReentrantMutex<mlua::state::raw::RawLua>`
|
||||
--> src/types/sync.rs
|
||||
|
|
||||
| pub struct RawReentrantMutex<R, G> {
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
note: required because it appears within the type `lock_api::remutex::ReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId, mlua::state::raw::RawLua>`
|
||||
--> $CARGO/lock_api-0.4.12/src/remutex.rs
|
||||
|
|
||||
| pub struct ReentrantMutex<R, G, T: ?Sized> {
|
||||
| ^^^^^^^^^^^^^^
|
||||
note: required because it appears within the type `alloc::sync::ArcInner<lock_api::remutex::ReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId, mlua::state::raw::RawLua>>`
|
||||
--> $RUST/alloc/src/sync.rs
|
||||
|
|
||||
| struct ArcInner<T: ?Sized> {
|
||||
| ^^^^^^^^
|
||||
note: required because it appears within the type `PhantomData<alloc::sync::ArcInner<lock_api::remutex::ReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId, mlua::state::raw::RawLua>>>`
|
||||
--> $RUST/core/src/marker.rs
|
||||
|
|
||||
| pub struct PhantomData<T: ?Sized>;
|
||||
| ^^^^^^^^^^^
|
||||
note: required because it appears within the type `Arc<lock_api::remutex::ReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId, mlua::state::raw::RawLua>>`
|
||||
--> $RUST/alloc/src/sync.rs
|
||||
|
|
||||
| pub struct Arc<
|
||||
| ^^^
|
||||
| pub(crate) struct ReentrantMutex<T>(T);
|
||||
| ^^^^^^^^^^^^^^
|
||||
= note: required for `Rc<mlua::types::sync::inner::ReentrantMutex<mlua::state::raw::RawLua>>` to implement `RefUnwindSafe`
|
||||
note: required because it appears within the type `Lua`
|
||||
--> src/state.rs
|
||||
|
|
||||
|
||||
@@ -8,7 +8,7 @@ error[E0277]: `Rc<Cell<i32>>` cannot be sent between threads safely
|
||||
| | within this `{closure@$DIR/tests/compile/non_send.rs:11:25: 11:37}`
|
||||
| required by a bound introduced by this call
|
||||
|
|
||||
= help: within `{closure@$DIR/tests/compile/non_send.rs:11:25: 11:37}`, the trait `Send` is not implemented for `Rc<Cell<i32>>`, which is required by `{closure@$DIR/tests/compile/non_send.rs:11:25: 11:37}: MaybeSend`
|
||||
= help: within `{closure@$DIR/tests/compile/non_send.rs:11:25: 11:37}`, the trait `Send` is not implemented for `Rc<Cell<i32>>`
|
||||
note: required because it's used within this closure
|
||||
--> tests/compile/non_send.rs:11:25
|
||||
|
|
||||
|
||||
@@ -1,38 +1,38 @@
|
||||
error[E0277]: the type `UnsafeCell<mlua::state::raw::RawLua>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
|
||||
error[E0277]: the type `UnsafeCell<usize>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
|
||||
--> tests/compile/ref_nounwindsafe.rs:8:18
|
||||
|
|
||||
8 | catch_unwind(move || table.set("a", "b").unwrap());
|
||||
| ------------ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `UnsafeCell<mlua::state::raw::RawLua>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
|
||||
| ------------ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `UnsafeCell<usize>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
|
||||
| |
|
||||
| required by a bound introduced by this call
|
||||
|
|
||||
= help: within `alloc::sync::ArcInner<lock_api::remutex::ReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId, mlua::state::raw::RawLua>>`, the trait `RefUnwindSafe` is not implemented for `UnsafeCell<mlua::state::raw::RawLua>`, which is required by `{closure@$DIR/tests/compile/ref_nounwindsafe.rs:8:18: 8:25}: UnwindSafe`
|
||||
note: required because it appears within the type `lock_api::remutex::ReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId, mlua::state::raw::RawLua>`
|
||||
--> $CARGO/lock_api-0.4.12/src/remutex.rs
|
||||
= help: within `rc::RcInner<mlua::types::sync::inner::ReentrantMutex<mlua::state::raw::RawLua>>`, the trait `RefUnwindSafe` is not implemented for `UnsafeCell<usize>`
|
||||
note: required because it appears within the type `Cell<usize>`
|
||||
--> $RUST/core/src/cell.rs
|
||||
|
|
||||
| pub struct ReentrantMutex<R, G, T: ?Sized> {
|
||||
| ^^^^^^^^^^^^^^
|
||||
note: required because it appears within the type `alloc::sync::ArcInner<lock_api::remutex::ReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId, mlua::state::raw::RawLua>>`
|
||||
--> $RUST/alloc/src/sync.rs
|
||||
| pub struct Cell<T: ?Sized> {
|
||||
| ^^^^
|
||||
note: required because it appears within the type `rc::RcInner<mlua::types::sync::inner::ReentrantMutex<mlua::state::raw::RawLua>>`
|
||||
--> $RUST/alloc/src/rc.rs
|
||||
|
|
||||
| struct ArcInner<T: ?Sized> {
|
||||
| ^^^^^^^^
|
||||
= note: required for `NonNull<alloc::sync::ArcInner<lock_api::remutex::ReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId, mlua::state::raw::RawLua>>>` to implement `UnwindSafe`
|
||||
note: required because it appears within the type `std::sync::Weak<lock_api::remutex::ReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId, mlua::state::raw::RawLua>>`
|
||||
--> $RUST/alloc/src/sync.rs
|
||||
| struct RcInner<T: ?Sized> {
|
||||
| ^^^^^^^
|
||||
= note: required for `NonNull<rc::RcInner<mlua::types::sync::inner::ReentrantMutex<mlua::state::raw::RawLua>>>` to implement `UnwindSafe`
|
||||
note: required because it appears within the type `std::rc::Weak<mlua::types::sync::inner::ReentrantMutex<mlua::state::raw::RawLua>>`
|
||||
--> $RUST/alloc/src/rc.rs
|
||||
|
|
||||
| pub struct Weak<
|
||||
| ^^^^
|
||||
note: required because it appears within the type `mlua::state::WeakLua`
|
||||
note: required because it appears within the type `WeakLua`
|
||||
--> src/state.rs
|
||||
|
|
||||
| pub(crate) struct WeakLua(XWeak<ReentrantMutex<RawLua>>);
|
||||
| ^^^^^^^
|
||||
note: required because it appears within the type `mlua::types::ValueRef`
|
||||
--> src/types.rs
|
||||
| pub struct WeakLua(XWeak<ReentrantMutex<RawLua>>);
|
||||
| ^^^^^^^
|
||||
note: required because it appears within the type `mlua::types::value_ref::ValueRef`
|
||||
--> src/types/value_ref.rs
|
||||
|
|
||||
| pub(crate) struct ValueRef {
|
||||
| ^^^^^^^^
|
||||
| pub struct ValueRef {
|
||||
| ^^^^^^^^
|
||||
note: required because it appears within the type `LuaTable`
|
||||
--> src/table.rs
|
||||
|
|
||||
@@ -49,51 +49,108 @@ note: required by a bound in `std::panic::catch_unwind`
|
||||
| pub fn catch_unwind<F: FnOnce() -> R + UnwindSafe, R>(f: F) -> Result<R> {
|
||||
| ^^^^^^^^^^ required by this bound in `catch_unwind`
|
||||
|
||||
error[E0277]: the type `UnsafeCell<usize>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
|
||||
error[E0277]: the type `UnsafeCell<*mut lua_State>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
|
||||
--> tests/compile/ref_nounwindsafe.rs:8:18
|
||||
|
|
||||
8 | catch_unwind(move || table.set("a", "b").unwrap());
|
||||
| ------------ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `UnsafeCell<usize>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
|
||||
| ------------ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `UnsafeCell<*mut lua_State>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
|
||||
| |
|
||||
| required by a bound introduced by this call
|
||||
|
|
||||
= help: within `alloc::sync::ArcInner<lock_api::remutex::ReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId, mlua::state::raw::RawLua>>`, the trait `RefUnwindSafe` is not implemented for `UnsafeCell<usize>`, which is required by `{closure@$DIR/tests/compile/ref_nounwindsafe.rs:8:18: 8:25}: UnwindSafe`
|
||||
note: required because it appears within the type `Cell<usize>`
|
||||
= help: within `rc::RcInner<mlua::types::sync::inner::ReentrantMutex<mlua::state::raw::RawLua>>`, the trait `RefUnwindSafe` is not implemented for `UnsafeCell<*mut lua_State>`
|
||||
note: required because it appears within the type `Cell<*mut lua_State>`
|
||||
--> $RUST/core/src/cell.rs
|
||||
|
|
||||
| pub struct Cell<T: ?Sized> {
|
||||
| ^^^^
|
||||
note: required because it appears within the type `lock_api::remutex::RawReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId>`
|
||||
--> $CARGO/lock_api-0.4.12/src/remutex.rs
|
||||
note: required because it appears within the type `mlua::state::raw::RawLua`
|
||||
--> src/state/raw.rs
|
||||
|
|
||||
| pub struct RawReentrantMutex<R, G> {
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
note: required because it appears within the type `lock_api::remutex::ReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId, mlua::state::raw::RawLua>`
|
||||
--> $CARGO/lock_api-0.4.12/src/remutex.rs
|
||||
| pub struct RawLua {
|
||||
| ^^^^^^
|
||||
note: required because it appears within the type `mlua::types::sync::inner::ReentrantMutex<mlua::state::raw::RawLua>`
|
||||
--> src/types/sync.rs
|
||||
|
|
||||
| pub struct ReentrantMutex<R, G, T: ?Sized> {
|
||||
| ^^^^^^^^^^^^^^
|
||||
note: required because it appears within the type `alloc::sync::ArcInner<lock_api::remutex::ReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId, mlua::state::raw::RawLua>>`
|
||||
--> $RUST/alloc/src/sync.rs
|
||||
| pub(crate) struct ReentrantMutex<T>(T);
|
||||
| ^^^^^^^^^^^^^^
|
||||
note: required because it appears within the type `rc::RcInner<mlua::types::sync::inner::ReentrantMutex<mlua::state::raw::RawLua>>`
|
||||
--> $RUST/alloc/src/rc.rs
|
||||
|
|
||||
| struct ArcInner<T: ?Sized> {
|
||||
| ^^^^^^^^
|
||||
= note: required for `NonNull<alloc::sync::ArcInner<lock_api::remutex::ReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId, mlua::state::raw::RawLua>>>` to implement `UnwindSafe`
|
||||
note: required because it appears within the type `std::sync::Weak<lock_api::remutex::ReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId, mlua::state::raw::RawLua>>`
|
||||
--> $RUST/alloc/src/sync.rs
|
||||
| struct RcInner<T: ?Sized> {
|
||||
| ^^^^^^^
|
||||
= note: required for `NonNull<rc::RcInner<mlua::types::sync::inner::ReentrantMutex<mlua::state::raw::RawLua>>>` to implement `UnwindSafe`
|
||||
note: required because it appears within the type `std::rc::Weak<mlua::types::sync::inner::ReentrantMutex<mlua::state::raw::RawLua>>`
|
||||
--> $RUST/alloc/src/rc.rs
|
||||
|
|
||||
| pub struct Weak<
|
||||
| ^^^^
|
||||
note: required because it appears within the type `mlua::state::WeakLua`
|
||||
note: required because it appears within the type `WeakLua`
|
||||
--> src/state.rs
|
||||
|
|
||||
| pub(crate) struct WeakLua(XWeak<ReentrantMutex<RawLua>>);
|
||||
| ^^^^^^^
|
||||
note: required because it appears within the type `mlua::types::ValueRef`
|
||||
--> src/types.rs
|
||||
| pub struct WeakLua(XWeak<ReentrantMutex<RawLua>>);
|
||||
| ^^^^^^^
|
||||
note: required because it appears within the type `mlua::types::value_ref::ValueRef`
|
||||
--> src/types/value_ref.rs
|
||||
|
|
||||
| pub(crate) struct ValueRef {
|
||||
| ^^^^^^^^
|
||||
| pub struct ValueRef {
|
||||
| ^^^^^^^^
|
||||
note: required because it appears within the type `LuaTable`
|
||||
--> src/table.rs
|
||||
|
|
||||
| pub struct Table(pub(crate) ValueRef);
|
||||
| ^^^^^
|
||||
note: required because it's used within this closure
|
||||
--> tests/compile/ref_nounwindsafe.rs:8:18
|
||||
|
|
||||
8 | catch_unwind(move || table.set("a", "b").unwrap());
|
||||
| ^^^^^^^
|
||||
note: required by a bound in `std::panic::catch_unwind`
|
||||
--> $RUST/std/src/panic.rs
|
||||
|
|
||||
| pub fn catch_unwind<F: FnOnce() -> R + UnwindSafe, R>(f: F) -> Result<R> {
|
||||
| ^^^^^^^^^^ required by this bound in `catch_unwind`
|
||||
|
||||
error[E0277]: the type `UnsafeCell<mlua::state::extra::ExtraData>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
|
||||
--> tests/compile/ref_nounwindsafe.rs:8:18
|
||||
|
|
||||
8 | catch_unwind(move || table.set("a", "b").unwrap());
|
||||
| ------------ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `UnsafeCell<mlua::state::extra::ExtraData>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
|
||||
| |
|
||||
| required by a bound introduced by this call
|
||||
|
|
||||
= help: the trait `RefUnwindSafe` is not implemented for `UnsafeCell<mlua::state::extra::ExtraData>`
|
||||
= note: required for `Rc<UnsafeCell<mlua::state::extra::ExtraData>>` to implement `RefUnwindSafe`
|
||||
note: required because it appears within the type `mlua::state::raw::RawLua`
|
||||
--> src/state/raw.rs
|
||||
|
|
||||
| pub struct RawLua {
|
||||
| ^^^^^^
|
||||
note: required because it appears within the type `mlua::types::sync::inner::ReentrantMutex<mlua::state::raw::RawLua>`
|
||||
--> src/types/sync.rs
|
||||
|
|
||||
| pub(crate) struct ReentrantMutex<T>(T);
|
||||
| ^^^^^^^^^^^^^^
|
||||
note: required because it appears within the type `rc::RcInner<mlua::types::sync::inner::ReentrantMutex<mlua::state::raw::RawLua>>`
|
||||
--> $RUST/alloc/src/rc.rs
|
||||
|
|
||||
| struct RcInner<T: ?Sized> {
|
||||
| ^^^^^^^
|
||||
= note: required for `NonNull<rc::RcInner<mlua::types::sync::inner::ReentrantMutex<mlua::state::raw::RawLua>>>` to implement `UnwindSafe`
|
||||
note: required because it appears within the type `std::rc::Weak<mlua::types::sync::inner::ReentrantMutex<mlua::state::raw::RawLua>>`
|
||||
--> $RUST/alloc/src/rc.rs
|
||||
|
|
||||
| pub struct Weak<
|
||||
| ^^^^
|
||||
note: required because it appears within the type `WeakLua`
|
||||
--> src/state.rs
|
||||
|
|
||||
| pub struct WeakLua(XWeak<ReentrantMutex<RawLua>>);
|
||||
| ^^^^^^^
|
||||
note: required because it appears within the type `mlua::types::value_ref::ValueRef`
|
||||
--> src/types/value_ref.rs
|
||||
|
|
||||
| pub struct ValueRef {
|
||||
| ^^^^^^^^
|
||||
note: required because it appears within the type `LuaTable`
|
||||
--> src/table.rs
|
||||
|
|
||||
|
||||
@@ -2,7 +2,7 @@ error[E0373]: closure may outlive the current function, but it borrows `inner`,
|
||||
--> tests/compile/scope_callback_capture.rs:7:43
|
||||
|
|
||||
5 | lua.scope(|scope| {
|
||||
| ----- has type `&'1 mut mlua::scope::Scope<'1, '_>`
|
||||
| ----- has type `&'1 mlua::Scope<'1, '_>`
|
||||
6 | let mut inner: Option<Table> = None;
|
||||
7 | let f = scope.create_function_mut(|_, t: Table| {
|
||||
| ^^^^^^^^^^^^^ may outlive borrowed value `inner`
|
||||
|
||||
@@ -2,7 +2,7 @@ error[E0373]: closure may outlive the current function, but it borrows `test.fie
|
||||
--> tests/compile/scope_invariance.rs:13:39
|
||||
|
|
||||
9 | lua.scope(|scope| {
|
||||
| ----- has type `&'1 mut mlua::scope::Scope<'1, '_>`
|
||||
| ----- has type `&'1 mlua::Scope<'1, '_>`
|
||||
...
|
||||
13 | scope.create_function_mut(|_, ()| {
|
||||
| ^^^^^^^ may outlive borrowed value `test.field`
|
||||
|
||||
@@ -2,7 +2,7 @@ error[E0499]: cannot borrow `i` as mutable more than once at a time
|
||||
--> tests/compile/scope_mutable_aliasing.rs:12:51
|
||||
|
|
||||
10 | lua.scope(|scope| {
|
||||
| ----- has type `&mut mlua::scope::Scope<'_, '1>`
|
||||
| ----- has type `&mlua::Scope<'_, '1>`
|
||||
11 | let _a = scope.create_userdata(MyUserData(&mut i)).unwrap();
|
||||
| -----------------------------------------
|
||||
| | |
|
||||
|
||||
@@ -2,7 +2,7 @@ error[E0597]: `ibad` does not live long enough
|
||||
--> tests/compile/scope_userdata_borrow.rs:15:46
|
||||
|
|
||||
11 | lua.scope(|scope| {
|
||||
| ----- has type `&mut mlua::scope::Scope<'_, '1>`
|
||||
| ----- has type `&mlua::Scope<'_, '1>`
|
||||
...
|
||||
14 | let ibad = 42;
|
||||
| ---- binding `ibad` declared here
|
||||
|
||||
+93
-2
@@ -6,8 +6,8 @@ use std::path::PathBuf;
|
||||
use bstr::BString;
|
||||
use maplit::{btreemap, btreeset, hashmap, hashset};
|
||||
use mlua::{
|
||||
AnyUserData, Either, Error, Function, IntoLua, Lua, RegistryKey, Result, Table, Thread, UserDataRef,
|
||||
Value,
|
||||
AnyUserData, BorrowedBytes, BorrowedStr, Either, Error, Function, IntoLua, Lua, RegistryKey, Result,
|
||||
Table, Thread, UserDataRef, Value,
|
||||
};
|
||||
|
||||
#[test]
|
||||
@@ -60,6 +60,66 @@ fn test_string_from_lua() -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_borrowedstr_into_lua() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
// Direct conversion
|
||||
let s = lua.create_string("hello, world!")?;
|
||||
let bs = s.to_str()?;
|
||||
let bs2 = (&bs).into_lua(&lua)?;
|
||||
assert_eq!(bs2.as_string().unwrap(), "hello, world!");
|
||||
|
||||
// Push into stack
|
||||
let table = lua.create_table()?;
|
||||
table.set("bs", &bs)?;
|
||||
assert_eq!(bs, table.get::<String>("bs")?);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_borrowedstr_from_lua() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
// From stack
|
||||
let f = lua.create_function(|_, s: BorrowedStr| Ok(s))?;
|
||||
let s = f.call::<String>("hello, world!")?;
|
||||
assert_eq!(s, "hello, world!");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_borrowedbytes_into_lua() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
// Direct conversion
|
||||
let s = lua.create_string("hello, world!")?;
|
||||
let bb = s.as_bytes();
|
||||
let bb2 = (&bb).into_lua(&lua)?;
|
||||
assert_eq!(bb2.as_string().unwrap(), "hello, world!");
|
||||
|
||||
// Push into stack
|
||||
let table = lua.create_table()?;
|
||||
table.set("bb", &bb)?;
|
||||
assert_eq!(bb, table.get::<String>("bb")?.as_bytes());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_borrowedbytes_from_lua() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
// From stack
|
||||
let f = lua.create_function(|_, s: BorrowedBytes| Ok(s))?;
|
||||
let s = f.call::<String>("hello, world!")?;
|
||||
assert_eq!(s, "hello, world!");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_table_into_lua() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
@@ -657,3 +717,34 @@ fn test_either_from_lua() -> Result<()> {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_char_into_lua() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let v = '🦀';
|
||||
let v2 = v.into_lua(&lua)?;
|
||||
assert_eq!(Some(v.to_string()), v2.as_string_lossy());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_char_from_lua() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
assert_eq!(lua.convert::<char>("A")?, 'A');
|
||||
assert_eq!(lua.convert::<char>(65)?, 'A');
|
||||
assert_eq!(lua.convert::<char>(128175)?, '💯');
|
||||
assert!(lua
|
||||
.convert::<char>(5456324)
|
||||
.is_err_and(|e| e.to_string().contains("integer out of range")));
|
||||
assert!(lua
|
||||
.convert::<char>("hello")
|
||||
.is_err_and(|e| e.to_string().contains("expected string to have exactly one char")));
|
||||
assert!(lua
|
||||
.convert::<char>(HashMap::<String, String>::new())
|
||||
.is_err_and(|e| e.to_string().contains("expected string or integer")));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
+1
-1
@@ -214,7 +214,7 @@ fn test_function_dump() -> Result<()> {
|
||||
|
||||
#[cfg(feature = "luau")]
|
||||
#[test]
|
||||
fn test_finction_coverage() -> Result<()> {
|
||||
fn test_function_coverage() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
lua.set_compiler(mlua::Compiler::default().set_coverage_level(1));
|
||||
|
||||
@@ -75,9 +75,21 @@ fn test_function_calls() -> Result<()> {
|
||||
|
||||
let output = output.lock().unwrap();
|
||||
if cfg!(feature = "luajit") && lua.load("jit.version_num").eval::<i64>()? >= 20100 {
|
||||
#[cfg(not(force_memory_limit))]
|
||||
assert_eq!(*output, vec![(None, "main"), (Some("len".to_string()), "Lua")]);
|
||||
#[cfg(force_memory_limit)]
|
||||
assert_eq!(
|
||||
*output,
|
||||
vec![(None, "C"), (None, "main"), (Some("len".to_string()), "Lua")]
|
||||
);
|
||||
} else {
|
||||
#[cfg(not(force_memory_limit))]
|
||||
assert_eq!(*output, vec![(None, "main"), (Some("len".to_string()), "C")]);
|
||||
#[cfg(force_memory_limit)]
|
||||
assert_eq!(
|
||||
*output,
|
||||
vec![(None, "C"), (None, "main"), (Some("len".to_string()), "C")]
|
||||
);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
||||
@@ -282,6 +282,20 @@ fn test_sandbox() -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sandbox_safeenv() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
lua.sandbox(true)?;
|
||||
lua.globals().set("state", lua.create_table()?)?;
|
||||
lua.globals().set_safeenv(false);
|
||||
lua.load("state.a = 123").exec()?;
|
||||
let a: i32 = lua.load("state.a = 321; return state.a").eval()?;
|
||||
assert_eq!(a, 321);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sandbox_nolibs() -> Result<()> {
|
||||
let lua = Lua::new_with(StdLib::NONE, LuaOptions::default()).unwrap();
|
||||
|
||||
+11
-1
@@ -31,6 +31,16 @@ fn test_memory_limit() -> Result<()> {
|
||||
lua.set_memory_limit(0)?;
|
||||
f.call::<()>(()).expect("should trigger no memory limit");
|
||||
|
||||
// Test memory limit during chunk loading
|
||||
lua.set_memory_limit(1024)?;
|
||||
match lua
|
||||
.load("local t = {}; for i = 1,10000 do t[i] = i end")
|
||||
.into_function()
|
||||
{
|
||||
Err(Error::MemoryError(_)) => {}
|
||||
_ => panic!("did not trigger memory error"),
|
||||
};
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -47,8 +57,8 @@ fn test_memory_limit_thread() -> Result<()> {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
lua.set_memory_limit(lua.used_memory() + 10000)?;
|
||||
let thread = lua.create_thread(f)?;
|
||||
lua.set_memory_limit(lua.used_memory() + 10000)?;
|
||||
match thread.resume::<()>(()) {
|
||||
Err(Error::MemoryError(_)) => {}
|
||||
something_else => panic!("did not trigger memory error: {:?}", something_else),
|
||||
|
||||
+46
-21
@@ -235,12 +235,14 @@ fn test_scope_userdata_values() -> Result<()> {
|
||||
|
||||
#[test]
|
||||
fn test_scope_userdata_mismatch() -> Result<()> {
|
||||
struct MyUserData<'a>(&'a Cell<i64>);
|
||||
struct MyUserData<'a>(&'a mut i64);
|
||||
|
||||
impl<'a> UserData for MyUserData<'a> {
|
||||
fn register(reg: &mut UserDataRegistry<Self>) {
|
||||
reg.add_method("inc", |_, data, ()| {
|
||||
data.0.set(data.0.get() + 1);
|
||||
reg.add_method("get", |_, data, ()| Ok(*data.0));
|
||||
|
||||
reg.add_method_mut("inc", |_, data, ()| {
|
||||
*data.0 = data.0.wrapping_add(1);
|
||||
Ok(())
|
||||
});
|
||||
}
|
||||
@@ -251,30 +253,53 @@ fn test_scope_userdata_mismatch() -> Result<()> {
|
||||
lua.load(
|
||||
r#"
|
||||
function inc(a, b) a.inc(b) end
|
||||
function get(a, b) a.get(b) end
|
||||
"#,
|
||||
)
|
||||
.exec()?;
|
||||
|
||||
let a = Cell::new(1);
|
||||
let b = Cell::new(1);
|
||||
let mut a = 1;
|
||||
let mut b = 1;
|
||||
|
||||
let inc: Function = lua.globals().get("inc")?;
|
||||
lua.scope(|scope| {
|
||||
let au = scope.create_userdata(MyUserData(&a))?;
|
||||
let bu = scope.create_userdata(MyUserData(&b))?;
|
||||
assert!(inc.call::<()>((&au, &au)).is_ok());
|
||||
match inc.call::<()>((&au, &bu)) {
|
||||
Err(Error::CallbackError { ref cause, .. }) => match cause.as_ref() {
|
||||
Error::BadArgument { to, pos, name, cause } => {
|
||||
assert_eq!(to.as_deref(), Some("MyUserData.inc"));
|
||||
assert_eq!(*pos, 1);
|
||||
assert_eq!(name.as_deref(), Some("self"));
|
||||
assert!(matches!(*cause.as_ref(), Error::UserDataTypeMismatch));
|
||||
}
|
||||
let au = scope.create_userdata(MyUserData(&mut a))?;
|
||||
let bu = scope.create_userdata(MyUserData(&mut b))?;
|
||||
for method_name in ["get", "inc"] {
|
||||
let f: Function = lua.globals().get(method_name)?;
|
||||
let full_name = format!("MyUserData.{method_name}");
|
||||
let full_name = full_name.as_str();
|
||||
|
||||
assert!(f.call::<()>((&au, &au)).is_ok());
|
||||
match f.call::<()>((&au, &bu)) {
|
||||
Err(Error::CallbackError { ref cause, .. }) => match cause.as_ref() {
|
||||
Error::BadArgument { to, pos, name, cause } => {
|
||||
assert_eq!(to.as_deref(), Some(full_name));
|
||||
assert_eq!(*pos, 1);
|
||||
assert_eq!(name.as_deref(), Some("self"));
|
||||
assert!(matches!(*cause.as_ref(), Error::UserDataTypeMismatch));
|
||||
}
|
||||
other => panic!("wrong error type {other:?}"),
|
||||
},
|
||||
Err(other) => panic!("wrong error type {other:?}"),
|
||||
Ok(_) => panic!("incorrectly returned Ok"),
|
||||
}
|
||||
|
||||
// Pass non-userdata type
|
||||
let err = f.call::<()>((&au, 321)).err().unwrap();
|
||||
match err {
|
||||
Error::CallbackError { ref cause, .. } => match cause.as_ref() {
|
||||
Error::BadArgument { to, pos, name, cause } => {
|
||||
assert_eq!(to.as_deref(), Some(full_name));
|
||||
assert_eq!(*pos, 1);
|
||||
assert_eq!(name.as_deref(), Some("self"));
|
||||
assert!(matches!(*cause.as_ref(), Error::FromLuaConversionError { .. }));
|
||||
}
|
||||
other => panic!("wrong error type {other:?}"),
|
||||
},
|
||||
other => panic!("wrong error type {other:?}"),
|
||||
},
|
||||
Err(other) => panic!("wrong error type {other:?}"),
|
||||
Ok(_) => panic!("incorrectly returned Ok"),
|
||||
}
|
||||
let err_msg = format!("bad argument `self` to `{full_name}`: error converting Lua number to userdata (expected userdata of type 'MyUserData')");
|
||||
assert!(err.to_string().contains(&err_msg));
|
||||
}
|
||||
Ok(())
|
||||
})?;
|
||||
@@ -318,7 +343,7 @@ fn test_scope_userdata_drop() -> Result<()> {
|
||||
|
||||
let ud = lua.globals().get::<AnyUserData>("ud")?;
|
||||
match ud.borrow_scoped::<MyUserData, _>(|_| Ok::<_, Error>(())) {
|
||||
Ok(_) => panic!("succesfull borrow for destructed userdata"),
|
||||
Ok(_) => panic!("successful borrow for destructed userdata"),
|
||||
Err(Error::UserDataDestructed) => {}
|
||||
Err(err) => panic!("improper borrow error for destructed userdata: {err:?}"),
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
use std::collections::HashMap;
|
||||
use std::error::Error as StdError;
|
||||
|
||||
use bstr::BString;
|
||||
use mlua::{
|
||||
AnyUserData, DeserializeOptions, Error, ExternalResult, IntoLua, Lua, LuaSerdeExt, Result as LuaResult,
|
||||
SerializeOptions, UserData, Value,
|
||||
@@ -248,6 +249,26 @@ fn test_serialize_same_table_twice() -> LuaResult<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_serialize_empty_table() -> LuaResult<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let table = Value::Table(lua.create_table()?);
|
||||
let json = serde_json::to_string(&table.to_serializable()).unwrap();
|
||||
assert_eq!(json, "{}");
|
||||
|
||||
// Set the option to encode empty tables as array
|
||||
let json = serde_json::to_string(&table.to_serializable().encode_empty_tables_as_array(true)).unwrap();
|
||||
assert_eq!(json, "[]");
|
||||
|
||||
// Check hashmap table with this option
|
||||
table.as_table().unwrap().set("hello", "world")?;
|
||||
let json = serde_json::to_string(&table.to_serializable().encode_empty_tables_as_array(true)).unwrap();
|
||||
assert_eq!(json, r#"{"hello":"world"}"#);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_to_value_struct() -> LuaResult<()> {
|
||||
let lua = Lua::new();
|
||||
@@ -420,6 +441,7 @@ fn test_from_value_struct() -> Result<(), Box<dyn StdError>> {
|
||||
map: HashMap<i32, i32>,
|
||||
empty: Vec<()>,
|
||||
tuple: (u8, u8, u8),
|
||||
bytes: BString,
|
||||
}
|
||||
|
||||
let value = lua
|
||||
@@ -431,6 +453,7 @@ fn test_from_value_struct() -> Result<(), Box<dyn StdError>> {
|
||||
map = {2, [4] = 1},
|
||||
empty = {},
|
||||
tuple = {10, 20, 30},
|
||||
bytes = "\240\040\140\040",
|
||||
}
|
||||
"#,
|
||||
)
|
||||
@@ -443,6 +466,7 @@ fn test_from_value_struct() -> Result<(), Box<dyn StdError>> {
|
||||
map: vec![(1, 2), (4, 1)].into_iter().collect(),
|
||||
empty: vec![],
|
||||
tuple: (10, 20, 30),
|
||||
bytes: BString::from([240, 40, 140, 40]),
|
||||
},
|
||||
got
|
||||
);
|
||||
@@ -663,6 +687,37 @@ fn test_from_value_userdata() -> Result<(), Box<dyn StdError>> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_from_value_empty_table() -> Result<(), Box<dyn StdError>> {
|
||||
let lua = Lua::new();
|
||||
|
||||
// By default we encode empty tables as objects
|
||||
let t = lua.create_table()?;
|
||||
let got = lua.from_value::<serde_json::Value>(Value::Table(t.clone()))?;
|
||||
assert_eq!(got, serde_json::json!({}));
|
||||
|
||||
// Set the option to encode empty tables as array
|
||||
let got = lua
|
||||
.from_value_with::<serde_json::Value>(
|
||||
Value::Table(t.clone()),
|
||||
DeserializeOptions::new().encode_empty_tables_as_array(true),
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(got, serde_json::json!([]));
|
||||
|
||||
// Check hashmap table with this option
|
||||
t.raw_set("hello", "world")?;
|
||||
let got = lua
|
||||
.from_value_with::<serde_json::Value>(
|
||||
Value::Table(t),
|
||||
DeserializeOptions::new().encode_empty_tables_as_array(true),
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(got, serde_json::json!({"hello": "world"}));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_from_value_sorted() -> Result<(), Box<dyn StdError>> {
|
||||
let lua = Lua::new();
|
||||
|
||||
+31
-2
@@ -96,8 +96,8 @@ fn test_string_fmt_debug() -> Result<()> {
|
||||
assert_eq!(format!("{:?}", s.as_bytes()), "[104, 101, 108, 108, 111]");
|
||||
|
||||
// Invalid utf8
|
||||
let s = lua.create_string(b"hello\0world\r\n\t\xF0\x90\x80")?;
|
||||
assert_eq!(format!("{s:?}"), r#"b"hello\0world\r\n\t\xF0\x90\x80""#);
|
||||
let s = lua.create_string(b"hello\0world\r\n\t\xf0\x90\x80")?;
|
||||
assert_eq!(format!("{s:?}"), r#"b"hello\0world\r\n\t\xf0\x90\x80""#);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -128,3 +128,32 @@ fn test_string_display() -> Result<()> {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_string_wrap() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let s = String::wrap("hello, world");
|
||||
lua.globals().set("s", s)?;
|
||||
assert_eq!(lua.globals().get::<String>("s")?, "hello, world");
|
||||
|
||||
let s2 = String::wrap("hello, world (owned)".to_string());
|
||||
lua.globals().set("s2", s2)?;
|
||||
assert_eq!(lua.globals().get::<String>("s2")?, "hello, world (owned)");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_bytes_into_iter() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let s = lua.create_string("hello")?;
|
||||
let bytes = s.as_bytes();
|
||||
|
||||
for (i, &b) in bytes.into_iter().enumerate() {
|
||||
assert_eq!(b, s.as_bytes()[i]);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
+44
-1
@@ -12,6 +12,24 @@ use mlua::{
|
||||
Value, Variadic,
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn test_weak_lua() {
|
||||
let lua = Lua::new();
|
||||
let weak_lua = lua.weak();
|
||||
assert!(weak_lua.try_upgrade().is_some());
|
||||
drop(lua);
|
||||
assert!(weak_lua.try_upgrade().is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected = "Lua instance is destroyed")]
|
||||
fn test_weak_lua_panic() {
|
||||
let lua = Lua::new();
|
||||
let weak_lua = lua.weak();
|
||||
drop(lua);
|
||||
let _ = weak_lua.upgrade();
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "luau"))]
|
||||
#[test]
|
||||
fn test_safety() -> Result<()> {
|
||||
@@ -322,7 +340,7 @@ fn test_error() -> Result<()> {
|
||||
let return_string_error = globals.get::<Function>("return_string_error")?;
|
||||
assert!(return_string_error.call::<Error>(()).is_ok());
|
||||
|
||||
match lua.load("if youre happy and you know it syntax error").exec() {
|
||||
match lua.load("if you are happy and you know it syntax error").exec() {
|
||||
Err(Error::SyntaxError {
|
||||
incomplete_input: false,
|
||||
..
|
||||
@@ -1289,6 +1307,13 @@ fn test_warnings() -> Result<()> {
|
||||
if matches!(*cause, Error::RuntimeError(ref err) if err == "warning error")
|
||||
));
|
||||
|
||||
// Recursive warning
|
||||
lua.set_warning_function(|lua, _, _| {
|
||||
lua.warning("inner", false);
|
||||
Ok(())
|
||||
});
|
||||
lua.warning("hello", false);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1377,3 +1402,21 @@ fn test_exec_raw() -> Result<()> {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_gc_drop_ref_thread() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let t = lua.create_table()?;
|
||||
lua.create_function(move |_, ()| {
|
||||
_ = &t;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
for _ in 0..10000 {
|
||||
// GC will run eventually to collect the function and the table above
|
||||
lua.create_table()?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
+3
-7
@@ -107,7 +107,6 @@ fn test_thread() -> Result<()> {
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "lua54", feature = "luau"))]
|
||||
fn test_thread_reset() -> Result<()> {
|
||||
use mlua::{AnyUserData, UserData};
|
||||
use std::sync::Arc;
|
||||
@@ -120,7 +119,8 @@ fn test_thread_reset() -> Result<()> {
|
||||
let arc = Arc::new(());
|
||||
|
||||
let func: Function = lua.load(r#"function(ud) coroutine.yield(ud) end"#).eval()?;
|
||||
let thread = lua.create_thread(func.clone())?;
|
||||
let thread = lua.create_thread(lua.load("return 0").into_function()?)?; // Dummy function first
|
||||
assert!(thread.reset(func.clone()).is_ok());
|
||||
|
||||
for _ in 0..2 {
|
||||
assert_eq!(thread.status(), ThreadStatus::Resumable);
|
||||
@@ -145,11 +145,7 @@ fn test_thread_reset() -> Result<()> {
|
||||
assert!(thread.reset(func.clone()).is_err());
|
||||
// Reset behavior has changed in Lua v5.4.4
|
||||
// It's became possible to force reset thread by popping error object
|
||||
assert!(matches!(
|
||||
thread.status(),
|
||||
ThreadStatus::Finished | ThreadStatus::Error
|
||||
));
|
||||
// Would pass in 5.4.4
|
||||
assert!(matches!(thread.status(), ThreadStatus::Finished));
|
||||
assert!(thread.reset(func.clone()).is_ok());
|
||||
assert_eq!(thread.status(), ThreadStatus::Resumable);
|
||||
}
|
||||
|
||||
+298
-67
@@ -1,3 +1,4 @@
|
||||
use std::any::TypeId;
|
||||
use std::collections::HashMap;
|
||||
use std::string::String as StdString;
|
||||
use std::sync::Arc;
|
||||
@@ -23,9 +24,11 @@ fn test_userdata() -> Result<()> {
|
||||
let userdata2 = lua.create_userdata(UserData2(Box::new(2)))?;
|
||||
|
||||
assert!(userdata1.is::<UserData1>());
|
||||
assert!(userdata1.type_id() == Some(TypeId::of::<UserData1>()));
|
||||
assert!(!userdata1.is::<UserData2>());
|
||||
assert!(userdata2.is::<UserData2>());
|
||||
assert!(!userdata2.is::<UserData1>());
|
||||
assert!(userdata2.type_id() == Some(TypeId::of::<UserData2>()));
|
||||
|
||||
assert_eq!(userdata1.borrow::<UserData1>()?.0, 1);
|
||||
assert_eq!(*userdata2.borrow::<UserData2>()?.0, 2);
|
||||
@@ -261,7 +264,7 @@ fn test_gc_userdata() -> Result<()> {
|
||||
impl UserData for MyUserdata {
|
||||
fn add_methods<M: UserDataMethods<Self>>(methods: &mut M) {
|
||||
methods.add_method("access", |_, this, ()| {
|
||||
assert!(this.id == 123);
|
||||
assert_eq!(this.id, 123);
|
||||
Ok(())
|
||||
});
|
||||
}
|
||||
@@ -410,7 +413,7 @@ fn test_userdata_destroy() -> Result<()> {
|
||||
let ud_ref = ud.borrow::<MyUserdata>()?;
|
||||
// With active `UserDataRef` this methods only marks userdata as destructed
|
||||
// without running destructor
|
||||
ud.destroy()?;
|
||||
ud.destroy().unwrap();
|
||||
assert_eq!(Arc::strong_count(&rc), 2);
|
||||
drop(ud_ref);
|
||||
assert_eq!(Arc::strong_count(&rc), 1);
|
||||
@@ -419,7 +422,7 @@ fn test_userdata_destroy() -> Result<()> {
|
||||
let ud = lua.create_userdata(MyUserdata(rc.clone()))?;
|
||||
lua.globals().set("ud", &ud)?;
|
||||
lua.load("ud:try_destroy()").exec().unwrap();
|
||||
ud.destroy()?;
|
||||
ud.destroy().unwrap();
|
||||
assert_eq!(Arc::strong_count(&rc), 1);
|
||||
|
||||
Ok(())
|
||||
@@ -913,6 +916,7 @@ fn test_nested_userdata_gc() -> Result<()> {
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
#[test]
|
||||
fn test_userdata_wrappers() -> Result<()> {
|
||||
#[derive(Debug)]
|
||||
struct MyUserData(i64);
|
||||
|
||||
impl UserData for MyUserData {
|
||||
@@ -924,6 +928,10 @@ fn test_userdata_wrappers() -> Result<()> {
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
fn add_methods<M: UserDataMethods<Self>>(methods: &mut M) {
|
||||
methods.add_method("dbg", |_, this, ()| Ok(format!("{this:?}")));
|
||||
}
|
||||
}
|
||||
|
||||
let lua = Lua::new();
|
||||
@@ -932,136 +940,359 @@ fn test_userdata_wrappers() -> Result<()> {
|
||||
// Rc<T>
|
||||
#[cfg(not(feature = "send"))]
|
||||
{
|
||||
let ud = std::rc::Rc::new(MyUserData(1));
|
||||
globals.set("rc_ud", ud.clone())?;
|
||||
use std::rc::Rc;
|
||||
|
||||
let ud = Rc::new(MyUserData(1));
|
||||
globals.set("ud", ud.clone())?;
|
||||
lua.load(
|
||||
r#"
|
||||
assert(rc_ud.static == "constant")
|
||||
local ok, err = pcall(function() rc_ud.data = 2 end)
|
||||
assert(ud.static == "constant")
|
||||
local ok, err = pcall(function() ud.data = 2 end)
|
||||
assert(
|
||||
tostring(err):sub(1, 32) == "error mutably borrowing userdata",
|
||||
"expected error mutably borrowing userdata, got " .. tostring(err)
|
||||
tostring(err):find("error mutably borrowing userdata") ~= nil,
|
||||
"expected 'error mutably borrowing userdata', got '" .. tostring(err) .. "'"
|
||||
)
|
||||
assert(rc_ud.data == 1)
|
||||
assert(ud.data == 1)
|
||||
assert(ud:dbg(), "MyUserData(1)")
|
||||
"#,
|
||||
)
|
||||
.exec()
|
||||
.unwrap();
|
||||
globals.set("rc_ud", Nil)?;
|
||||
|
||||
// Test borrowing original userdata
|
||||
{
|
||||
let ud = globals.get::<AnyUserData>("ud")?;
|
||||
assert!(ud.is::<Rc<MyUserData>>());
|
||||
assert!(!ud.is::<MyUserData>());
|
||||
|
||||
assert_eq!(ud.borrow::<MyUserData>()?.0, 1);
|
||||
assert!(matches!(
|
||||
ud.borrow_mut::<MyUserData>(),
|
||||
Err(Error::UserDataBorrowMutError)
|
||||
));
|
||||
assert!(ud.borrow_mut::<Rc<MyUserData>>().is_ok());
|
||||
|
||||
assert_eq!(ud.borrow_scoped::<MyUserData, _>(|x| x.0)?, 1);
|
||||
assert!(matches!(
|
||||
ud.borrow_mut_scoped::<MyUserData, _>(|_| ()),
|
||||
Err(Error::UserDataBorrowMutError)
|
||||
));
|
||||
}
|
||||
|
||||
// Collect userdata
|
||||
globals.set("ud", Nil)?;
|
||||
lua.gc_collect()?;
|
||||
assert_eq!(std::rc::Rc::strong_count(&ud), 1);
|
||||
assert_eq!(Rc::strong_count(&ud), 1);
|
||||
|
||||
// We must be able to mutate userdata when having one reference only
|
||||
globals.set("ud", ud)?;
|
||||
lua.load(
|
||||
r#"
|
||||
ud.data = 2
|
||||
assert(ud.data == 2)
|
||||
"#,
|
||||
)
|
||||
.exec()
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
// 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())?;
|
||||
use std::cell::RefCell;
|
||||
use std::rc::Rc;
|
||||
|
||||
let ud = Rc::new(RefCell::new(MyUserData(2)));
|
||||
globals.set("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)
|
||||
"#,
|
||||
assert(ud.static == "constant")
|
||||
assert(ud.data == 2)
|
||||
ud.data = 10
|
||||
assert(ud.data == 10)
|
||||
assert(ud:dbg() == "MyUserData(10)")
|
||||
"#,
|
||||
)
|
||||
.exec()?;
|
||||
assert_eq!(ud.borrow().0, 3);
|
||||
globals.set("rc_refcell_ud", Nil)?;
|
||||
.exec()
|
||||
.unwrap();
|
||||
|
||||
// Test borrowing original userdata
|
||||
{
|
||||
let ud = globals.get::<AnyUserData>("ud")?;
|
||||
assert!(ud.is::<Rc<RefCell<MyUserData>>>());
|
||||
assert!(!ud.is::<MyUserData>());
|
||||
|
||||
assert_eq!(ud.borrow::<MyUserData>()?.0, 10);
|
||||
assert_eq!(ud.borrow_mut::<MyUserData>()?.0, 10);
|
||||
ud.borrow_mut::<MyUserData>()?.0 = 20;
|
||||
assert_eq!(ud.borrow::<MyUserData>()?.0, 20);
|
||||
|
||||
assert_eq!(ud.borrow_scoped::<MyUserData, _>(|x| x.0)?, 20);
|
||||
ud.borrow_mut_scoped::<MyUserData, _>(|x| x.0 = 30)?;
|
||||
assert_eq!(ud.borrow::<MyUserData>()?.0, 30);
|
||||
|
||||
// Double (read) borrow is okay
|
||||
let _borrow = ud.borrow::<MyUserData>()?;
|
||||
assert_eq!(ud.borrow::<MyUserData>()?.0, 30);
|
||||
assert!(matches!(
|
||||
ud.borrow_mut::<MyUserData>(),
|
||||
Err(Error::UserDataBorrowMutError)
|
||||
));
|
||||
}
|
||||
|
||||
// Collect userdata
|
||||
globals.set("ud", Nil)?;
|
||||
lua.gc_collect()?;
|
||||
assert_eq!(std::rc::Rc::strong_count(&ud), 1);
|
||||
assert_eq!(Rc::strong_count(&ud), 1);
|
||||
|
||||
// Check destroying wrapped UserDataRef without references in Lua
|
||||
let ud = lua.convert::<UserDataRef<MyUserData>>(ud)?;
|
||||
lua.gc_collect()?;
|
||||
assert_eq!(ud.0, 30);
|
||||
drop(ud);
|
||||
}
|
||||
|
||||
// Arc<T>
|
||||
{
|
||||
let ud = Arc::new(MyUserData(3));
|
||||
globals.set("arc_ud", ud.clone())?;
|
||||
globals.set("ud", ud.clone())?;
|
||||
lua.load(
|
||||
r#"
|
||||
assert(arc_ud.static == "constant")
|
||||
local ok, err = pcall(function() arc_ud.data = 10 end)
|
||||
assert(ud.static == "constant")
|
||||
local ok, err = pcall(function() ud.data = 4 end)
|
||||
assert(
|
||||
tostring(err):sub(1, 32) == "error mutably borrowing userdata",
|
||||
"expected error mutably borrowing userdata, got " .. tostring(err)
|
||||
tostring(err):find("error mutably borrowing userdata") ~= nil,
|
||||
"expected 'error mutably borrowing userdata', got '" .. tostring(err) .. "'"
|
||||
)
|
||||
assert(arc_ud.data == 3)
|
||||
"#,
|
||||
assert(ud.data == 3)
|
||||
assert(ud:dbg() == "MyUserData(3)")
|
||||
"#,
|
||||
)
|
||||
.exec()?;
|
||||
globals.set("arc_ud", Nil)?;
|
||||
.exec()
|
||||
.unwrap();
|
||||
|
||||
// Test borrowing original userdata
|
||||
{
|
||||
let ud = globals.get::<AnyUserData>("ud")?;
|
||||
assert!(ud.is::<Arc<MyUserData>>());
|
||||
assert!(!ud.is::<MyUserData>());
|
||||
|
||||
assert_eq!(ud.borrow::<MyUserData>()?.0, 3);
|
||||
assert!(matches!(
|
||||
ud.borrow_mut::<MyUserData>(),
|
||||
Err(Error::UserDataBorrowMutError)
|
||||
));
|
||||
assert!(ud.borrow_mut::<Arc<MyUserData>>().is_ok());
|
||||
|
||||
assert_eq!(ud.borrow_scoped::<MyUserData, _>(|x| x.0)?, 3);
|
||||
assert!(matches!(
|
||||
ud.borrow_mut_scoped::<MyUserData, _>(|_| ()),
|
||||
Err(Error::UserDataBorrowMutError)
|
||||
));
|
||||
}
|
||||
|
||||
// Collect userdata
|
||||
globals.set("ud", Nil)?;
|
||||
lua.gc_collect()?;
|
||||
assert_eq!(Arc::strong_count(&ud), 1);
|
||||
|
||||
// We must be able to mutate userdata when having one reference only
|
||||
globals.set("ud", ud)?;
|
||||
lua.load(
|
||||
r#"
|
||||
ud.data = 4
|
||||
assert(ud.data == 4)
|
||||
"#,
|
||||
)
|
||||
.exec()
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
// Arc<Mutex<T>>
|
||||
{
|
||||
let ud = Arc::new(std::sync::Mutex::new(MyUserData(4)));
|
||||
globals.set("arc_mutex_ud", ud.clone())?;
|
||||
use std::sync::Mutex;
|
||||
|
||||
let ud = Arc::new(Mutex::new(MyUserData(5)));
|
||||
globals.set("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)
|
||||
"#,
|
||||
assert(ud.static == "constant")
|
||||
assert(ud.data == 5)
|
||||
ud.data = 6
|
||||
assert(ud.data == 6)
|
||||
assert(ud:dbg() == "MyUserData(6)")
|
||||
"#,
|
||||
)
|
||||
.exec()?;
|
||||
assert_eq!(ud.lock().unwrap().0, 5);
|
||||
globals.set("arc_mutex_ud", Nil)?;
|
||||
.exec()
|
||||
.unwrap();
|
||||
|
||||
// Test borrowing original userdata
|
||||
{
|
||||
let ud = globals.get::<AnyUserData>("ud")?;
|
||||
assert!(ud.is::<Arc<Mutex<MyUserData>>>());
|
||||
assert!(!ud.is::<MyUserData>());
|
||||
|
||||
#[rustfmt::skip]
|
||||
assert!(matches!(ud.borrow::<MyUserData>(), Err(Error::UserDataTypeMismatch)));
|
||||
#[rustfmt::skip]
|
||||
assert!(matches!(ud.borrow_mut::<MyUserData>(), Err(Error::UserDataTypeMismatch)));
|
||||
|
||||
assert_eq!(ud.borrow_scoped::<MyUserData, _>(|x| x.0)?, 6);
|
||||
ud.borrow_mut_scoped::<MyUserData, _>(|x| x.0 = 8)?;
|
||||
assert_eq!(ud.borrow_scoped::<MyUserData, _>(|x| x.0)?, 8);
|
||||
}
|
||||
|
||||
// Collect userdata
|
||||
globals.set("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())?;
|
||||
use std::sync::RwLock;
|
||||
|
||||
let ud = Arc::new(RwLock::new(MyUserData(9)));
|
||||
globals.set("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)
|
||||
"#,
|
||||
assert(ud.static == "constant")
|
||||
assert(ud.data == 9)
|
||||
ud.data = 10
|
||||
assert(ud.data == 10)
|
||||
assert(ud:dbg() == "MyUserData(10)")
|
||||
"#,
|
||||
)
|
||||
.exec()?;
|
||||
assert_eq!(ud.read().unwrap().0, 7);
|
||||
globals.set("arc_rwlock_ud", Nil)?;
|
||||
.exec()
|
||||
.unwrap();
|
||||
|
||||
// Test borrowing original userdata
|
||||
{
|
||||
let ud = globals.get::<AnyUserData>("ud")?;
|
||||
assert!(ud.is::<Arc<RwLock<MyUserData>>>());
|
||||
assert!(!ud.is::<MyUserData>());
|
||||
|
||||
#[rustfmt::skip]
|
||||
assert!(matches!(ud.borrow::<MyUserData>(), Err(Error::UserDataTypeMismatch)));
|
||||
#[rustfmt::skip]
|
||||
assert!(matches!(ud.borrow_mut::<MyUserData>(), Err(Error::UserDataTypeMismatch)));
|
||||
|
||||
assert_eq!(ud.borrow_scoped::<MyUserData, _>(|x| x.0)?, 10);
|
||||
ud.borrow_mut_scoped::<MyUserData, _>(|x| x.0 = 12)?;
|
||||
assert_eq!(ud.borrow_scoped::<MyUserData, _>(|x| x.0)?, 12);
|
||||
}
|
||||
|
||||
// Collect userdata
|
||||
globals.set("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())?;
|
||||
use parking_lot::Mutex;
|
||||
|
||||
let ud = Arc::new(Mutex::new(MyUserData(13)));
|
||||
globals.set("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)
|
||||
"#,
|
||||
assert(ud.static == "constant")
|
||||
assert(ud.data == 13)
|
||||
ud.data = 14
|
||||
assert(ud.data == 14)
|
||||
assert(ud:dbg() == "MyUserData(14)")
|
||||
"#,
|
||||
)
|
||||
.exec()?;
|
||||
assert_eq!(ud.lock().0, 9);
|
||||
globals.set("arc_parking_lot_mutex_ud", Nil)?;
|
||||
.exec()
|
||||
.unwrap();
|
||||
|
||||
// Test borrowing original userdata
|
||||
{
|
||||
let ud = globals.get::<AnyUserData>("ud")?;
|
||||
assert!(ud.is::<Arc<Mutex<MyUserData>>>());
|
||||
assert!(!ud.is::<MyUserData>());
|
||||
|
||||
assert_eq!(ud.borrow::<MyUserData>()?.0, 14);
|
||||
assert_eq!(ud.borrow_mut::<MyUserData>()?.0, 14);
|
||||
ud.borrow_mut::<MyUserData>()?.0 = 15;
|
||||
assert_eq!(ud.borrow::<MyUserData>()?.0, 15);
|
||||
|
||||
assert_eq!(ud.borrow_scoped::<MyUserData, _>(|x| x.0)?, 15);
|
||||
ud.borrow_mut_scoped::<MyUserData, _>(|x| x.0 = 16)?;
|
||||
assert_eq!(ud.borrow::<MyUserData>()?.0, 16);
|
||||
|
||||
// Double borrow is not allowed
|
||||
let _borrow = ud.borrow::<MyUserData>()?;
|
||||
assert!(matches!(
|
||||
ud.borrow::<MyUserData>(),
|
||||
Err(Error::UserDataBorrowError)
|
||||
));
|
||||
}
|
||||
|
||||
// Collect userdata
|
||||
globals.set("ud", Nil)?;
|
||||
lua.gc_collect()?;
|
||||
assert_eq!(Arc::strong_count(&ud), 1);
|
||||
|
||||
// Check destroying wrapped UserDataRef without references in Lua
|
||||
let ud = lua.convert::<UserDataRef<MyUserData>>(ud)?;
|
||||
lua.gc_collect()?;
|
||||
assert_eq!(ud.0, 16);
|
||||
drop(ud);
|
||||
}
|
||||
|
||||
// Arc<parking_lot::RwLock<T>>
|
||||
{
|
||||
let ud = Arc::new(parking_lot::RwLock::new(MyUserData(10)));
|
||||
globals.set("arc_parking_lot_rwlock_ud", ud.clone())?;
|
||||
use parking_lot::RwLock;
|
||||
|
||||
let ud = Arc::new(RwLock::new(MyUserData(17)));
|
||||
globals.set("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)
|
||||
"#,
|
||||
assert(ud.static == "constant")
|
||||
assert(ud.data == 17)
|
||||
ud.data = 18
|
||||
assert(ud.data == 18)
|
||||
assert(ud:dbg() == "MyUserData(18)")
|
||||
"#,
|
||||
)
|
||||
.exec()?;
|
||||
assert_eq!(ud.read().0, 11);
|
||||
globals.set("arc_parking_lot_rwlock_ud", Nil)?;
|
||||
.exec()
|
||||
.unwrap();
|
||||
|
||||
// Test borrowing original userdata
|
||||
{
|
||||
let ud = globals.get::<AnyUserData>("ud")?;
|
||||
assert!(ud.is::<Arc<RwLock<MyUserData>>>());
|
||||
assert!(!ud.is::<MyUserData>());
|
||||
|
||||
assert_eq!(ud.borrow::<MyUserData>()?.0, 18);
|
||||
assert_eq!(ud.borrow_mut::<MyUserData>()?.0, 18);
|
||||
ud.borrow_mut::<MyUserData>()?.0 = 19;
|
||||
assert_eq!(ud.borrow::<MyUserData>()?.0, 19);
|
||||
|
||||
assert_eq!(ud.borrow_scoped::<MyUserData, _>(|x| x.0)?, 19);
|
||||
ud.borrow_mut_scoped::<MyUserData, _>(|x| x.0 = 20)?;
|
||||
assert_eq!(ud.borrow::<MyUserData>()?.0, 20);
|
||||
|
||||
// Multiple read borrows are allowed with parking_lot::RwLock
|
||||
let _borrow1 = ud.borrow::<MyUserData>()?;
|
||||
let _borrow2 = ud.borrow::<MyUserData>()?;
|
||||
assert!(matches!(
|
||||
ud.borrow_mut::<MyUserData>(),
|
||||
Err(Error::UserDataBorrowMutError)
|
||||
));
|
||||
}
|
||||
|
||||
// Collect userdata
|
||||
globals.set("ud", Nil)?;
|
||||
lua.gc_collect()?;
|
||||
assert_eq!(Arc::strong_count(&ud), 1);
|
||||
|
||||
// Check destroying wrapped UserDataRef without references in Lua
|
||||
let ud = lua.convert::<UserDataRef<MyUserData>>(ud)?;
|
||||
lua.gc_collect()?;
|
||||
assert_eq!(ud.0, 20);
|
||||
drop(ud);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
||||
@@ -296,3 +296,25 @@ fn test_value_conversions() -> Result<()> {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_value_exhaustive_match() {
|
||||
match Value::Nil {
|
||||
Value::Nil => {}
|
||||
Value::Boolean(_) => {}
|
||||
Value::LightUserData(_) => {}
|
||||
Value::Integer(_) => {}
|
||||
Value::Number(_) => {}
|
||||
#[cfg(feature = "luau")]
|
||||
Value::Vector(_) => {}
|
||||
Value::String(_) => {}
|
||||
Value::Table(_) => {}
|
||||
Value::Function(_) => {}
|
||||
Value::Thread(_) => {}
|
||||
Value::UserData(_) => {}
|
||||
#[cfg(feature = "luau")]
|
||||
Value::Buffer(_) => {}
|
||||
Value::Error(_) => {}
|
||||
Value::Other(_) => {}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user