mirror of
https://github.com/mlua-rs/mlua
synced 2026-06-08 16:05:43 +00:00
Compare commits
66 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c926327a6a | |||
| 7c099500d0 | |||
| 8c889cc353 | |||
| 958abd050e | |||
| 92a8203e1c | |||
| b34b90eca3 | |||
| 7aad0adcb4 | |||
| a3cd25db7a | |||
| a4bfeb7752 | |||
| 0fda512938 | |||
| 58e0661086 | |||
| c7094d470f | |||
| a7d0691e10 | |||
| 05778fbe6f | |||
| b34d67ec41 | |||
| 46ee7ea772 | |||
| 15738dda1f | |||
| 1f32754f05 | |||
| c2eab173c5 | |||
| bb311349ec | |||
| 928e1d9221 | |||
| 5b8681dcf2 | |||
| 4e9a17707b | |||
| 6066089cc1 | |||
| a8d5f23818 | |||
| 5ec4e0338a | |||
| d27d1365b5 | |||
| 76b896edcc | |||
| ddebf56b41 | |||
| 4f56575e05 | |||
| 35fa76263e | |||
| 446d63a77e | |||
| 8d8d521721 | |||
| 0d31a1caa6 | |||
| 3dc58cdfc9 | |||
| 5724b5f112 | |||
| d64d9719c6 | |||
| 75475fc9a8 | |||
| f8fe9246bb | |||
| 5c54361236 | |||
| ec227f9056 | |||
| 93a1a55aaa | |||
| a020b2b5b2 | |||
| 2c756e5958 | |||
| c702077028 | |||
| 08545224f4 | |||
| e122f90837 | |||
| c638d90b02 | |||
| 930fd9c00f | |||
| 2a8db87132 | |||
| cbae4fe59c | |||
| c68e3c4f41 | |||
| 02d4ceff34 | |||
| 98339c57e6 | |||
| 2331995e28 | |||
| c07bdce250 | |||
| 084a85c3d8 | |||
| 735aa22be9 | |||
| 5479546b27 | |||
| 179c54f297 | |||
| f9ae4bf05f | |||
| 9e16e18132 | |||
| 7535a23fa2 | |||
| 3787ff9e8c | |||
| 0a2a70c15a | |||
| 81d7c81532 |
@@ -6,7 +6,7 @@ jobs:
|
||||
name: coverage
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: xd009642/tarpaulin
|
||||
image: xd009642/tarpaulin:develop-nightly
|
||||
options: --security-opt seccomp=unconfined
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
@@ -14,7 +14,7 @@ jobs:
|
||||
|
||||
- name: Generate coverage report
|
||||
run: |
|
||||
cargo tarpaulin --out xml --tests --exclude-files benches/* --exclude-files mlua-sys/src/*/*
|
||||
cargo +nightly tarpaulin --verbose --out xml --tests --exclude-files benches/* --exclude-files mlua-sys/src/*/*
|
||||
|
||||
- name: Upload report to codecov.io
|
||||
uses: codecov/codecov-action@v4
|
||||
|
||||
+12
-12
@@ -27,8 +27,8 @@ jobs:
|
||||
- name: Build ${{ matrix.lua }} vendored
|
||||
run: |
|
||||
cargo build --features "${{ matrix.lua }},vendored"
|
||||
cargo build --features "${{ matrix.lua }},vendored,async,serialize,macros"
|
||||
cargo build --features "${{ matrix.lua }},vendored,async,serialize,macros,send"
|
||||
cargo build --features "${{ matrix.lua }},vendored,async,serialize,macros,anyhow,userdata-wrappers"
|
||||
cargo build --features "${{ matrix.lua }},vendored,async,serialize,macros,anyhow,userdata-wrappers,send"
|
||||
shell: bash
|
||||
- name: Build ${{ matrix.lua }} pkg-config
|
||||
if: ${{ matrix.os == 'ubuntu-latest' }}
|
||||
@@ -51,7 +51,7 @@ jobs:
|
||||
toolchain: stable
|
||||
target: aarch64-apple-darwin
|
||||
- name: Cross-compile
|
||||
run: cargo build --target aarch64-apple-darwin --features "${{ matrix.lua }},vendored,async,send,serialize,macros"
|
||||
run: cargo build --target aarch64-apple-darwin --features "${{ matrix.lua }},vendored,async,send,serialize,macros,anyhow,userdata-wrappers"
|
||||
|
||||
build_aarch64_cross_ubuntu:
|
||||
name: Cross-compile to aarch64-unknown-linux-gnu
|
||||
@@ -72,7 +72,7 @@ jobs:
|
||||
sudo apt-get install -y --no-install-recommends gcc-aarch64-linux-gnu libc6-dev-arm64-cross
|
||||
shell: bash
|
||||
- name: Cross-compile
|
||||
run: cargo build --target aarch64-unknown-linux-gnu --features "${{ matrix.lua }},vendored,async,send,serialize,macros"
|
||||
run: cargo build --target aarch64-unknown-linux-gnu --features "${{ matrix.lua }},vendored,async,send,serialize,macros,anyhow,userdata-wrappers"
|
||||
shell: bash
|
||||
|
||||
build_armv7_cross_ubuntu:
|
||||
@@ -94,7 +94,7 @@ jobs:
|
||||
sudo apt-get install -y --no-install-recommends gcc-arm-linux-gnueabihf libc-dev-armhf-cross
|
||||
shell: bash
|
||||
- name: Cross-compile
|
||||
run: cargo build --target armv7-unknown-linux-gnueabihf --features "${{ matrix.lua }},vendored,async,send,serialize,macros"
|
||||
run: cargo build --target armv7-unknown-linux-gnueabihf --features "${{ matrix.lua }},vendored,async,send,serialize,macros,anyhow,userdata-wrappers"
|
||||
shell: bash
|
||||
|
||||
test:
|
||||
@@ -123,8 +123,8 @@ jobs:
|
||||
- name: Run ${{ matrix.lua }} tests
|
||||
run: |
|
||||
cargo test --features "${{ matrix.lua }},vendored"
|
||||
cargo test --features "${{ matrix.lua }},vendored,async,serialize,macros"
|
||||
cargo test --features "${{ matrix.lua }},vendored,async,serialize,macros,send"
|
||||
cargo test --features "${{ matrix.lua }},vendored,async,serialize,macros,anyhow,userdata-wrappers"
|
||||
cargo test --features "${{ matrix.lua }},vendored,async,serialize,macros,anyhow,userdata-wrappers,send"
|
||||
shell: bash
|
||||
- name: Run compile tests (macos lua54)
|
||||
if: ${{ matrix.os == 'macos-latest' && matrix.lua == 'lua54' }}
|
||||
@@ -154,8 +154,8 @@ jobs:
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
- name: Run ${{ matrix.lua }} tests with address sanitizer
|
||||
run: |
|
||||
cargo test --tests --features "${{ matrix.lua }},vendored,async,serialize,macros" --target x86_64-unknown-linux-gnu -- --skip test_too_many_recursions
|
||||
cargo test --tests --features "${{ matrix.lua }},vendored,async,serialize,macros,send" --target x86_64-unknown-linux-gnu -- --skip test_too_many_recursions
|
||||
cargo test --tests --features "${{ matrix.lua }},vendored,async,serialize,macros,anyhow" --target x86_64-unknown-linux-gnu -- --skip test_too_many_recursions
|
||||
cargo test --tests --features "${{ matrix.lua }},vendored,async,serialize,macros,anyhow,userdata-wrappers,send" --target x86_64-unknown-linux-gnu -- --skip test_too_many_recursions
|
||||
shell: bash
|
||||
env:
|
||||
RUSTFLAGS: -Z sanitizer=address
|
||||
@@ -181,7 +181,7 @@ jobs:
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
- name: Run ${{ matrix.lua }} tests with forced memory limit
|
||||
run: |
|
||||
cargo test --tests --features "${{ matrix.lua }},vendored,async,send,serialize,macros"
|
||||
cargo test --tests --features "${{ matrix.lua }},vendored,async,send,serialize,macros,anyhow,userdata-wrappers"
|
||||
shell: bash
|
||||
env:
|
||||
RUSTFLAGS: --cfg=force_memory_limit
|
||||
@@ -254,7 +254,7 @@ jobs:
|
||||
- name: Run ${{ matrix.lua }} tests
|
||||
run: |
|
||||
cargo test --tests --features "${{ matrix.lua }},vendored"
|
||||
cargo test --tests --features "${{ matrix.lua }},vendored,async,serialize,macros"
|
||||
cargo test --tests --features "${{ matrix.lua }},vendored,async,serialize,macros,anyhow,userdata-wrappers"
|
||||
|
||||
rustfmt:
|
||||
name: Rustfmt
|
||||
@@ -281,4 +281,4 @@ jobs:
|
||||
- uses: giraffate/clippy-action@v1
|
||||
with:
|
||||
reporter: 'github-pr-review'
|
||||
clippy_flags: --features "${{ matrix.lua }},vendored,async,send,serialize,macros"
|
||||
clippy_flags: --features "${{ matrix.lua }},vendored,async,send,serialize,macros,anyhow,userdata-wrappers"
|
||||
|
||||
@@ -1,3 +1,25 @@
|
||||
## v0.10.1 (Nov 9th, 2024)
|
||||
|
||||
- Minimal Luau updated to 0.650
|
||||
- Added Luau native vector library support (this can change behavior if you use `vector` function!)
|
||||
- Added Lua `String::display` method
|
||||
- Improved pretty-printing for Lua tables (#478)
|
||||
- Added `Scope::create_any_userdata` to create Lua objects from any non-`'static` Rust types
|
||||
- Added `AnyUserData::destroy` method
|
||||
- New `userdata-wrappers` feature to `impl UserData` for `Rc<T>`/`Arc<T>`/`Rc<RefCell<T>>`/`Arc<Mutex<T>>` (similar to v0.9)
|
||||
- `UserDataRef` in `send` mode now uses shared lock if `T: Sync` (and exclusive lock otherwise)
|
||||
- Added `Scope::add_destructor` to attach custom destructors
|
||||
- Added `Lua::try_app_data_ref` and `Lua::try_app_data_mut` methods
|
||||
- Added `From<Vec>` and `Into<Vec>` support to `MultiValue` and `Variadic` types
|
||||
- Bug fixes and improvements (#477 #479)
|
||||
|
||||
## v0.10.0 (Oct 25th, 2024)
|
||||
|
||||
Changes since v0.10.0-rc.1
|
||||
|
||||
- Added `error-send` feature flag (disabled by default) to require `Send + Sync` for `Error`
|
||||
- Some performance improvements
|
||||
|
||||
## v0.10.0-rc.1
|
||||
|
||||
- `Lua::scope` is back
|
||||
|
||||
+8
-5
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "mlua"
|
||||
version = "0.10.0-rc.1" # remember to update mlua_derive
|
||||
version = "0.10.1" # remember to update mlua_derive
|
||||
authors = ["Aleksandr Orlenko <zxteam@pm.me>", "kyren <catherine@kyju.org>"]
|
||||
rust-version = "1.79.0"
|
||||
edition = "2021"
|
||||
@@ -38,14 +38,17 @@ luau-vector4 = ["luau", "ffi/luau-vector4"]
|
||||
vendored = ["ffi/vendored"]
|
||||
module = ["dep:mlua_derive", "ffi/module"]
|
||||
async = ["dep:futures-util"]
|
||||
send = ["parking_lot/send_guard"]
|
||||
send = ["parking_lot/send_guard", "error-send"]
|
||||
error-send = []
|
||||
serialize = ["dep:serde", "dep:erased-serde", "dep:serde-value"]
|
||||
macros = ["mlua_derive/macros"]
|
||||
anyhow = ["dep:anyhow"]
|
||||
anyhow = ["dep:anyhow", "error-send"]
|
||||
userdata-wrappers = []
|
||||
|
||||
[dependencies]
|
||||
mlua_derive = { version = "=0.10.0-rc.1", optional = true, path = "mlua_derive" }
|
||||
mlua_derive = { version = "=0.10.0", optional = true, path = "mlua_derive" }
|
||||
bstr = { version = "1.0", features = ["std"], default-features = false }
|
||||
either = "1.0"
|
||||
num-traits = { version = "0.2.14" }
|
||||
rustc-hash = "2.0"
|
||||
futures-util = { version = "0.3", optional = true, default-features = false, features = ["std"] }
|
||||
@@ -55,7 +58,7 @@ serde-value = { version = "0.7", optional = true }
|
||||
parking_lot = { version = "0.12", features = ["arc_lock"] }
|
||||
anyhow = { version = "1.0", optional = true }
|
||||
|
||||
ffi = { package = "mlua-sys", version = "0.6.3", path = "mlua-sys" }
|
||||
ffi = { package = "mlua-sys", version = "0.6.5", path = "mlua-sys" }
|
||||
|
||||
[target.'cfg(unix)'.dependencies]
|
||||
libloading = { version = "0.8", optional = true }
|
||||
|
||||
@@ -17,11 +17,9 @@
|
||||
[Benchmarks]: https://github.com/khvzak/script-bench-rs
|
||||
[FAQ]: FAQ.md
|
||||
|
||||
# The main branch is the v0.10, development version of `mlua`. Please see the [v0.9](https://github.com/mlua-rs/mlua/tree/v0.9) branch for the stable versions of `mlua`.
|
||||
|
||||
> **Note**
|
||||
>
|
||||
> See (upcoming) 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/khvzak/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.
|
||||
@@ -33,7 +31,7 @@ Started as `rlua` fork, `mlua` supports Lua 5.4, 5.3, 5.2, 5.1 (including LuaJIT
|
||||
WebAssembly (WASM) is supported through `wasm32-unknown-emscripten` target for all Lua versions excluding JIT.
|
||||
|
||||
[GitHub Actions]: https://github.com/khvzak/mlua/actions
|
||||
[Roblox Luau]: https://luau-lang.org
|
||||
[Roblox Luau]: https://luau.org
|
||||
|
||||
## Usage
|
||||
|
||||
@@ -55,9 +53,11 @@ Below is a list of the available feature flags. By default `mlua` does not enabl
|
||||
* `module`: enable module mode (building loadable `cdylib` library for Lua)
|
||||
* `async`: enable async/await support (any executor can be used, eg. [tokio] or [async-std])
|
||||
* `send`: make `mlua::Lua: Send + Sync` (adds [`Send`] requirement to `mlua::Function` and `mlua::UserData`)
|
||||
* `error-send`: make `mlua:Error: Send + Sync`
|
||||
* `serialize`: add serialization and deserialization support to `mlua` types using [serde] framework
|
||||
* `macros`: enable procedural macros (such as `chunk!`)
|
||||
* `anyhow`: enable `anyhow::Error` conversion into Lua
|
||||
* `userdata-wrappers`: opt into `impl UserData` for `Rc<T>`/`Arc<T>`/`Rc<RefCell<T>>`/`Arc<Mutex<T>>` where `T: UserData`
|
||||
|
||||
[5.4]: https://www.lua.org/manual/5.4/manual.html
|
||||
[5.3]: https://www.lua.org/manual/5.3/manual.html
|
||||
@@ -133,7 +133,7 @@ Add to `Cargo.toml` :
|
||||
|
||||
``` toml
|
||||
[dependencies]
|
||||
mlua = { version = "0.10.0-rc.1", features = ["lua54", "vendored"] }
|
||||
mlua = { version = "0.10.1", features = ["lua54", "vendored"] }
|
||||
```
|
||||
|
||||
`main.rs`
|
||||
@@ -168,7 +168,7 @@ Add to `Cargo.toml` :
|
||||
crate-type = ["cdylib"]
|
||||
|
||||
[dependencies]
|
||||
mlua = { version = "0.10.0-rc.1", features = ["lua54", "module"] }
|
||||
mlua = { version = "0.10.1", features = ["lua54", "module"] }
|
||||
```
|
||||
|
||||
`lib.rs` :
|
||||
@@ -291,7 +291,7 @@ Please check the [Luau Sandboxing] page if you are interested in running untrust
|
||||
|
||||
`mlua` provides `Lua::sandbox` method for enabling sandbox mode (Luau only).
|
||||
|
||||
[Luau Sandboxing]: https://luau-lang.org/sandbox
|
||||
[Luau Sandboxing]: https://luau.org/sandbox
|
||||
|
||||
## License
|
||||
|
||||
|
||||
@@ -121,3 +121,75 @@ assert_eq!(sum, 15);
|
||||
```
|
||||
|
||||
The `exec_raw` method is longjmp-safe. It's not recommended to move `Drop` types into the closure to avoid possible memory leaks.
|
||||
|
||||
#### `anyhow` feature flag
|
||||
|
||||
The new `anyhow` feature flag adds `IntoLua` and `Into<mlua::Error>` implementation for the `anyhow::Error` type.
|
||||
|
||||
```rust
|
||||
let f = lua.create_function(|_, ()| {
|
||||
Err(anyhow!("error message"))?;
|
||||
Ok(())
|
||||
})?;
|
||||
```
|
||||
|
||||
### Breaking changes
|
||||
|
||||
#### Scope changes
|
||||
|
||||
The following `Scope` methods were changed:
|
||||
- Removed `Scope::create_any_userdata`
|
||||
- `Scope::create_nonstatic_userdata` is renamed to `Scope::create_userdata`
|
||||
|
||||
Instead, scope has comprehensive support for borrowed userdata: `create_any_userdata_ref`, `create_any_userdata_ref_mut`, `create_userdata_ref`, `create_userdata_ref_mut`.
|
||||
|
||||
`UserDataRef` and `UserDataRefMut` are no longer acceptable for scoped userdata access as they require owned underlying data.
|
||||
In mlua v0.9 this can cause read-after-free bug in some edge cases.
|
||||
|
||||
To temporarily borrow underlying data, the `AnyUserData::borrow_scoped` and `AnyUserData::borrow_mut_scoped` methods were introduced:
|
||||
|
||||
```rust
|
||||
let data = "hello".to_string();
|
||||
lua.scope(|scope| {
|
||||
let ud = scope.create_any_userdata_ref(&data)?;
|
||||
|
||||
// We can only borrow scoped userdata using this method
|
||||
ud.borrow_scoped::<String, ()>(|s| {
|
||||
assert_eq!(s, "hello");
|
||||
})?;
|
||||
|
||||
Ok(())
|
||||
})?;
|
||||
```
|
||||
|
||||
Those methods work for scoped and regular userdata objects (but still require `T: 'static`).
|
||||
|
||||
#### String changes
|
||||
|
||||
Since `mlua::String` holds a weak reference to Lua without any guarantees about the lifetime of the underlying data, getting a `&str` or `&[u8]` from it is no longer safe.
|
||||
Lua instance can be destroyed while reference to the data is still alive:
|
||||
|
||||
```rust
|
||||
let lua = Lua::new();
|
||||
let s: mlua::String = lua.create_string("hello, world")?; // only weak reference to Lua!
|
||||
let s_ref: &str = s.to_str()?; // this is not safe!
|
||||
drop(lua);
|
||||
println!("{s_ref}"); // use after free!
|
||||
```
|
||||
|
||||
To solve this issue, return types of `mlua::String::to_str` and `mlua::String::as_bytes` methods changed to `BorrowedStr` and `BorrowedBytes` respectively.
|
||||
|
||||
These new types hold a strong reference to the Lua instance and can be safely converted to `&str` or `&[u8]`:
|
||||
|
||||
```rust
|
||||
let lua = Lua::new();
|
||||
let s: mlua::String = lua.create_string("hello, world")?;
|
||||
let s_ref: mlua::BorrowedStr = s.to_str()?; // The strong reference to Lua is held here
|
||||
drop(lua);
|
||||
println!("{s_ref}"); // ok
|
||||
```
|
||||
|
||||
The good news is that `BorrowedStr` implements `Deref<Target = str>`/`AsRef<str>` as well as `Display`, `Debug`, `Eq`, `PartialEq` and other traits for easy usage.
|
||||
The same applies to `BorrowedBytes`.
|
||||
|
||||
Unfortunately, `mlua::String::to_string_lossy` cannot return `Cow<'a, str>` anymore, because it requires a strong reference to Lua. It now returns Rust `String` instead.
|
||||
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "mlua-sys"
|
||||
version = "0.6.3"
|
||||
version = "0.6.5"
|
||||
authors = ["Aleksandr Orlenko <zxteam@pm.me>"]
|
||||
rust-version = "1.71"
|
||||
edition = "2021"
|
||||
@@ -40,7 +40,7 @@ cfg-if = "1.0"
|
||||
pkg-config = "0.3.17"
|
||||
lua-src = { version = ">= 547.0.0, < 547.1.0", optional = true }
|
||||
luajit-src = { version = ">= 210.5.0, < 210.6.0", optional = true }
|
||||
luau0-src = { version = "0.10.0", optional = true }
|
||||
luau0-src = { version = "0.11.1", optional = true }
|
||||
|
||||
[lints.rust]
|
||||
unexpected_cfgs = { level = "allow", check-cfg = ['cfg(raw_dylib)'] }
|
||||
|
||||
@@ -185,6 +185,7 @@ extern "C-unwind" {
|
||||
|
||||
pub fn lua_pushlightuserdatatagged(L: *mut lua_State, p: *mut c_void, tag: c_int);
|
||||
pub fn lua_newuserdatatagged(L: *mut lua_State, sz: usize, tag: c_int) -> *mut c_void;
|
||||
pub fn lua_newuserdatataggedwithmetatable(L: *mut lua_State, sz: usize, tag: c_int) -> *mut c_void;
|
||||
pub fn lua_newuserdatadtor(L: *mut lua_State, sz: usize, dtor: lua_Udestructor) -> *mut c_void;
|
||||
|
||||
pub fn lua_newbuffer(L: *mut lua_State, sz: usize) -> *mut c_void;
|
||||
@@ -526,6 +527,9 @@ pub struct lua_Callbacks {
|
||||
pub debuginterrupt: Option<unsafe extern "C-unwind" fn(L: *mut lua_State, ar: *mut lua_Debug)>,
|
||||
/// gets called when protected call results in an error
|
||||
pub debugprotectederror: Option<unsafe extern "C-unwind" fn(L: *mut lua_State)>,
|
||||
|
||||
/// gets called when memory is allocated
|
||||
pub onallocate: Option<unsafe extern "C-unwind" fn(L: *mut lua_State, osize: usize, nsize: usize)>,
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
@@ -535,4 +539,5 @@ extern "C" {
|
||||
// Functions from customization lib
|
||||
extern "C" {
|
||||
pub fn luau_setfflag(name: *const c_char, value: c_int) -> c_int;
|
||||
pub fn lua_getmetatablepointer(L: *mut lua_State, idx: c_int) -> *const c_void;
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ pub const LUA_BUFFERLIBNAME: &str = "buffer";
|
||||
pub const LUA_UTF8LIBNAME: &str = "utf8";
|
||||
pub const LUA_MATHLIBNAME: &str = "math";
|
||||
pub const LUA_DBLIBNAME: &str = "debug";
|
||||
pub const LUA_VECLIBNAME: &str = "vector";
|
||||
|
||||
extern "C-unwind" {
|
||||
pub fn luaopen_base(L: *mut lua_State) -> c_int;
|
||||
@@ -25,6 +26,7 @@ extern "C-unwind" {
|
||||
pub fn luaopen_utf8(L: *mut lua_State) -> c_int;
|
||||
pub fn luaopen_math(L: *mut lua_State) -> c_int;
|
||||
pub fn luaopen_debug(L: *mut lua_State) -> c_int;
|
||||
pub fn luaopen_vector(L: *mut lua_State) -> c_int;
|
||||
|
||||
// open all builtin libraries
|
||||
pub fn luaL_openlibs(L: *mut lua_State);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "mlua_derive"
|
||||
version = "0.10.0-rc.1"
|
||||
version = "0.10.0"
|
||||
authors = ["Aleksandr Orlenko <zxteam@pm.me>"]
|
||||
edition = "2021"
|
||||
description = "Procedural macros for the mlua crate."
|
||||
|
||||
+6
-9
@@ -14,7 +14,6 @@ use crate::traits::{FromLuaMulti, IntoLuaMulti};
|
||||
/// 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
|
||||
/// [`Chunk`]: crate::Chunk
|
||||
pub trait AsChunk<'a> {
|
||||
/// Returns optional chunk name
|
||||
fn name(&self) -> Option<StdString> {
|
||||
@@ -95,8 +94,6 @@ impl AsChunk<'static> for PathBuf {
|
||||
}
|
||||
|
||||
/// Returned from [`Lua::load`] and is used to finalize loading and executing Lua main chunks.
|
||||
///
|
||||
/// [`Lua::load`]: crate::Lua::load
|
||||
#[must_use = "`Chunk`s do nothing unless one of `exec`, `eval`, `call`, or `into_function` are called on them"]
|
||||
pub struct Chunk<'a> {
|
||||
pub(crate) lua: WeakLua,
|
||||
@@ -241,7 +238,7 @@ impl Compiler {
|
||||
|
||||
/// Compiles the `source` into bytecode.
|
||||
///
|
||||
/// Returns `Error::SyntaxError` if the source code is invalid.
|
||||
/// Returns [`Error::SyntaxError`] if the source code is invalid.
|
||||
pub fn compile(&self, source: impl AsRef<[u8]>) -> Result<Vec<u8>> {
|
||||
use std::os::raw::c_int;
|
||||
use std::ptr;
|
||||
@@ -304,7 +301,7 @@ impl Compiler {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Chunk<'a> {
|
||||
impl Chunk<'_> {
|
||||
/// Sets the name of this chunk, which results in more informative error traces.
|
||||
pub fn set_name(mut self, name: impl Into<String>) -> Self {
|
||||
self.name = name.into();
|
||||
@@ -361,7 +358,7 @@ impl<'a> Chunk<'a> {
|
||||
///
|
||||
/// Requires `feature = "async"`
|
||||
///
|
||||
/// [`exec`]: #method.exec
|
||||
/// [`exec`]: Chunk::exec
|
||||
#[cfg(feature = "async")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
|
||||
pub async fn exec_async(self) -> Result<()> {
|
||||
@@ -393,7 +390,7 @@ impl<'a> Chunk<'a> {
|
||||
///
|
||||
/// Requires `feature = "async"`
|
||||
///
|
||||
/// [`eval`]: #method.eval
|
||||
/// [`eval`]: Chunk::eval
|
||||
#[cfg(feature = "async")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
|
||||
pub async fn eval_async<R>(self) -> Result<R>
|
||||
@@ -422,7 +419,7 @@ impl<'a> Chunk<'a> {
|
||||
///
|
||||
/// Requires `feature = "async"`
|
||||
///
|
||||
/// [`call`]: #method.call
|
||||
/// [`call`]: Chunk::call
|
||||
#[cfg(feature = "async")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
|
||||
pub async fn call_async<R>(self, args: impl IntoLuaMulti) -> Result<R>
|
||||
@@ -432,7 +429,7 @@ impl<'a> Chunk<'a> {
|
||||
self.into_function()?.call_async(args).await
|
||||
}
|
||||
|
||||
/// Load this chunk into a regular `Function`.
|
||||
/// Load this chunk into a regular [`Function`].
|
||||
///
|
||||
/// This simply compiles the chunk without actually executing it.
|
||||
#[cfg_attr(not(feature = "luau"), allow(unused_mut))]
|
||||
|
||||
+62
-10
@@ -17,7 +17,7 @@ use crate::string::String;
|
||||
use crate::table::Table;
|
||||
use crate::thread::Thread;
|
||||
use crate::traits::{FromLua, IntoLua, ShortTypeName as _};
|
||||
use crate::types::{LightUserData, MaybeSend, RegistryKey};
|
||||
use crate::types::{Either, LightUserData, MaybeSend, RegistryKey};
|
||||
use crate::userdata::{AnyUserData, UserData};
|
||||
use crate::value::{Nil, Value};
|
||||
|
||||
@@ -243,14 +243,10 @@ impl IntoLua for Error {
|
||||
|
||||
impl FromLua for Error {
|
||||
#[inline]
|
||||
fn from_lua(value: Value, lua: &Lua) -> Result<Error> {
|
||||
fn from_lua(value: Value, _: &Lua) -> Result<Error> {
|
||||
match value {
|
||||
Value::Error(err) => Ok(*err),
|
||||
val => Ok(Error::runtime(
|
||||
lua.coerce_string(val)?
|
||||
.and_then(|s| Some(s.to_str().ok()?.to_owned()))
|
||||
.unwrap_or_else(|| "<unprintable error>".to_owned()),
|
||||
)),
|
||||
val => Ok(Error::runtime(val.to_string()?)),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -354,7 +350,7 @@ impl FromLua for LightUserData {
|
||||
}
|
||||
|
||||
#[cfg(feature = "luau")]
|
||||
impl IntoLua for crate::types::Vector {
|
||||
impl IntoLua for crate::Vector {
|
||||
#[inline]
|
||||
fn into_lua(self, _: &Lua) -> Result<Value> {
|
||||
Ok(Value::Vector(self))
|
||||
@@ -362,7 +358,7 @@ impl IntoLua for crate::types::Vector {
|
||||
}
|
||||
|
||||
#[cfg(feature = "luau")]
|
||||
impl FromLua for crate::types::Vector {
|
||||
impl FromLua for crate::Vector {
|
||||
#[inline]
|
||||
fn from_lua(value: Value, _: &Lua) -> Result<Self> {
|
||||
match value {
|
||||
@@ -852,7 +848,7 @@ where
|
||||
match value {
|
||||
#[cfg(feature = "luau")]
|
||||
#[rustfmt::skip]
|
||||
Value::Vector(v) if N == crate::types::Vector::SIZE => unsafe {
|
||||
Value::Vector(v) if N == crate::Vector::SIZE => unsafe {
|
||||
use std::{mem, ptr};
|
||||
let mut arr: [mem::MaybeUninit<T>; N] = mem::MaybeUninit::uninit().assume_init();
|
||||
ptr::write(arr[0].as_mut_ptr() , T::from_lua(Value::Number(v.x() as _), _lua)?);
|
||||
@@ -1043,3 +1039,59 @@ impl<T: FromLua> FromLua for Option<T> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<L: IntoLua, R: IntoLua> IntoLua for Either<L, R> {
|
||||
#[inline]
|
||||
fn into_lua(self, lua: &Lua) -> Result<Value> {
|
||||
match self {
|
||||
Either::Left(l) => l.into_lua(lua),
|
||||
Either::Right(r) => r.into_lua(lua),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn push_into_stack(self, lua: &RawLua) -> Result<()> {
|
||||
match self {
|
||||
Either::Left(l) => l.push_into_stack(lua),
|
||||
Either::Right(r) => r.push_into_stack(lua),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<L: FromLua, R: FromLua> FromLua for Either<L, R> {
|
||||
#[inline]
|
||||
fn from_lua(value: Value, lua: &Lua) -> Result<Self> {
|
||||
let value_type_name = value.type_name();
|
||||
// Try the left type first
|
||||
match L::from_lua(value.clone(), lua) {
|
||||
Ok(l) => Ok(Either::Left(l)),
|
||||
// Try the right type
|
||||
Err(_) => match R::from_lua(value, lua).map(Either::Right) {
|
||||
Ok(r) => Ok(r),
|
||||
Err(_) => Err(Error::FromLuaConversionError {
|
||||
from: value_type_name,
|
||||
to: Self::type_name(),
|
||||
message: None,
|
||||
}),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn from_stack(idx: c_int, lua: &RawLua) -> Result<Self> {
|
||||
match L::from_stack(idx, lua) {
|
||||
Ok(l) => Ok(Either::Left(l)),
|
||||
Err(_) => match R::from_stack(idx, lua).map(Either::Right) {
|
||||
Ok(r) => Ok(r),
|
||||
Err(_) => {
|
||||
let value_type_name = CStr::from_ptr(ffi::luaL_typename(lua.state(), idx));
|
||||
Err(Error::FromLuaConversionError {
|
||||
from: value_type_name.to_str().unwrap(),
|
||||
to: Self::type_name(),
|
||||
message: None,
|
||||
})
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+40
-18
@@ -9,6 +9,12 @@ use std::sync::Arc;
|
||||
|
||||
use crate::private::Sealed;
|
||||
|
||||
#[cfg(feature = "error-send")]
|
||||
type DynStdError = dyn StdError + Send + Sync;
|
||||
|
||||
#[cfg(not(feature = "error-send"))]
|
||||
type DynStdError = dyn StdError;
|
||||
|
||||
/// Error type returned by `mlua` methods.
|
||||
#[derive(Debug, Clone)]
|
||||
#[non_exhaustive]
|
||||
@@ -42,11 +48,11 @@ pub enum Error {
|
||||
GarbageCollectorError(StdString),
|
||||
/// Potentially unsafe action in safe mode.
|
||||
SafetyError(StdString),
|
||||
/// Setting memory limit is not available.
|
||||
/// Memory control is not available.
|
||||
///
|
||||
/// This error can only happen when Lua state was not created by us and does not have the
|
||||
/// custom allocator attached.
|
||||
MemoryLimitNotAvailable,
|
||||
MemoryControlNotAvailable,
|
||||
/// A mutable callback has triggered Lua code that has called the same mutable callback again.
|
||||
///
|
||||
/// This is an error because a mutable callback can only be borrowed mutably once.
|
||||
@@ -61,10 +67,12 @@ pub enum Error {
|
||||
///
|
||||
/// Due to the way `mlua` works, it should not be directly possible to run out of stack space
|
||||
/// during normal use. The only way that this error can be triggered is if a `Function` is
|
||||
/// called with a huge number of arguments, or a rust callback returns a huge number of return
|
||||
/// called with a huge number of arguments, or a Rust callback returns a huge number of return
|
||||
/// values.
|
||||
StackError,
|
||||
/// Too many arguments to `Function::bind`.
|
||||
/// Too many arguments to [`Function::bind`].
|
||||
///
|
||||
/// [`Function::bind`]: crate::Function::bind
|
||||
BindError,
|
||||
/// Bad argument received from Lua (usually when calling a function).
|
||||
///
|
||||
@@ -189,7 +197,7 @@ pub enum Error {
|
||||
/// Returning `Err(ExternalError(...))` from a Rust callback will raise the error as a Lua
|
||||
/// error. The Rust code that originally invoked the Lua code then receives a `CallbackError`,
|
||||
/// from which the original error (and a stack traceback) can be recovered.
|
||||
ExternalError(Arc<dyn StdError + Send + Sync>),
|
||||
ExternalError(Arc<DynStdError>),
|
||||
/// An error with additional context.
|
||||
WithContext {
|
||||
/// A string containing additional context.
|
||||
@@ -218,8 +226,8 @@ impl fmt::Display for Error {
|
||||
Error::SafetyError(msg) => {
|
||||
write!(fmt, "safety error: {msg}")
|
||||
},
|
||||
Error::MemoryLimitNotAvailable => {
|
||||
write!(fmt, "setting memory limit is not available")
|
||||
Error::MemoryControlNotAvailable => {
|
||||
write!(fmt, "memory control is not available")
|
||||
}
|
||||
Error::RecursiveMutCallback => write!(fmt, "mutable callback called recursively"),
|
||||
Error::CallbackDestructed => write!(
|
||||
@@ -309,7 +317,7 @@ impl fmt::Display for Error {
|
||||
Error::DeserializeError(err) => {
|
||||
write!(fmt, "deserialize error: {err}")
|
||||
},
|
||||
Error::ExternalError(err) => write!(fmt, "{err}"),
|
||||
Error::ExternalError(err) => err.fmt(fmt),
|
||||
Error::WithContext { context, cause } => {
|
||||
writeln!(fmt, "{context}")?;
|
||||
write!(fmt, "{cause}")
|
||||
@@ -328,10 +336,7 @@ impl StdError for Error {
|
||||
// returns nothing.
|
||||
Error::CallbackError { .. } => None,
|
||||
Error::ExternalError(err) => err.source(),
|
||||
Error::WithContext { cause, .. } => match cause.as_ref() {
|
||||
Error::ExternalError(err) => err.source(),
|
||||
_ => None,
|
||||
},
|
||||
Error::WithContext { cause, .. } => Self::source(cause),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
@@ -346,7 +351,7 @@ impl Error {
|
||||
|
||||
/// Wraps an external error object.
|
||||
#[inline]
|
||||
pub fn external<T: Into<Box<dyn StdError + Send + Sync>>>(err: T) -> Self {
|
||||
pub fn external<T: Into<Box<DynStdError>>>(err: T) -> Self {
|
||||
Error::ExternalError(err.into().into())
|
||||
}
|
||||
|
||||
@@ -357,10 +362,7 @@ impl Error {
|
||||
{
|
||||
match self {
|
||||
Error::ExternalError(err) => err.downcast_ref(),
|
||||
Error::WithContext { cause, .. } => match cause.as_ref() {
|
||||
Error::ExternalError(err) => err.downcast_ref(),
|
||||
_ => None,
|
||||
},
|
||||
Error::WithContext { cause, .. } => Self::downcast_ref(cause),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
@@ -373,6 +375,16 @@ impl Error {
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the parent of this error.
|
||||
#[doc(hidden)]
|
||||
pub fn parent(&self) -> Option<&Error> {
|
||||
match self {
|
||||
Error::CallbackError { cause, .. } => Some(cause.as_ref()),
|
||||
Error::WithContext { cause, .. } => Some(cause.as_ref()),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn bad_self_argument(to: &str, cause: Error) -> Self {
|
||||
Error::BadArgument {
|
||||
to: Some(to.to_string()),
|
||||
@@ -400,7 +412,7 @@ pub trait ExternalError {
|
||||
fn into_lua_err(self) -> Error;
|
||||
}
|
||||
|
||||
impl<E: Into<Box<dyn StdError + Send + Sync>>> ExternalError for E {
|
||||
impl<E: Into<Box<DynStdError>>> ExternalError for E {
|
||||
fn into_lua_err(self) -> Error {
|
||||
Error::external(self)
|
||||
}
|
||||
@@ -546,3 +558,13 @@ impl<'a> Iterator for Chain<'a> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod assertions {
|
||||
use super::*;
|
||||
|
||||
#[cfg(not(feature = "error-send"))]
|
||||
static_assertions::assert_not_impl_any!(Error: Send, Sync);
|
||||
#[cfg(feature = "send")]
|
||||
static_assertions::assert_impl_all!(Error: Send, Sync);
|
||||
}
|
||||
|
||||
+4
-4
@@ -389,9 +389,9 @@ impl Function {
|
||||
/// If `strip` is true, the binary representation may not include all debug information
|
||||
/// about the function, to save space.
|
||||
///
|
||||
/// For Luau a [Compiler] can be used to compile Lua chunks to bytecode.
|
||||
/// For Luau a [`Compiler`] can be used to compile Lua chunks to bytecode.
|
||||
///
|
||||
/// [Compiler]: crate::chunk::Compiler
|
||||
/// [`Compiler`]: crate::chunk::Compiler
|
||||
#[cfg(not(feature = "luau"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(not(feature = "luau"))))]
|
||||
pub fn dump(&self, strip: bool) -> Vec<u8> {
|
||||
@@ -490,10 +490,10 @@ impl Function {
|
||||
///
|
||||
/// Copies the function prototype and all its upvalues to the
|
||||
/// newly created function.
|
||||
///
|
||||
/// This function returns shallow clone (same handle) for Rust/C functions.
|
||||
///
|
||||
/// Requires `feature = "luau"`
|
||||
#[cfg(feature = "luau")]
|
||||
#[cfg(any(feature = "luau", doc))]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
|
||||
pub fn deep_clone(&self) -> Self {
|
||||
let lua = self.0.lua.lock();
|
||||
|
||||
+14
-9
@@ -16,9 +16,9 @@ use crate::util::{linenumber_to_usize, ptr_to_lossy_str, ptr_to_str};
|
||||
/// The `Debug` structure is provided as a parameter to the hook function set with
|
||||
/// [`Lua::set_hook`]. You may call the methods on this structure to retrieve information about the
|
||||
/// Lua code executing at the time that the hook function was called. Further information can be
|
||||
/// found in the Lua [documentation][lua_doc].
|
||||
/// found in the Lua [documentation].
|
||||
///
|
||||
/// [lua_doc]: https://www.lua.org/manual/5.4/manual.html#lua_Debug
|
||||
/// [documentation]: https://www.lua.org/manual/5.4/manual.html#lua_Debug
|
||||
/// [`Lua::set_hook`]: crate::Lua::set_hook
|
||||
pub struct Debug<'a> {
|
||||
lua: EitherLua<'a>,
|
||||
@@ -66,7 +66,7 @@ impl<'a> Debug<'a> {
|
||||
|
||||
/// Returns the specific event that triggered the hook.
|
||||
///
|
||||
/// For [Lua 5.1] `DebugEvent::TailCall` is used for return events to indicate a return
|
||||
/// For [Lua 5.1] [`DebugEvent::TailCall`] is used for return events to indicate a return
|
||||
/// from a function that did a tail call.
|
||||
///
|
||||
/// [Lua 5.1]: https://www.lua.org/manual/5.1/manual.html#pdf-LUA_HOOKTAILRET
|
||||
@@ -184,8 +184,8 @@ impl<'a> Debug<'a> {
|
||||
);
|
||||
#[cfg(feature = "luau")]
|
||||
mlua_assert!(
|
||||
ffi::lua_getinfo(self.lua.state(), self.level, cstr!("a"), self.ar.get()) != 0,
|
||||
"lua_getinfo failed with `a`"
|
||||
ffi::lua_getinfo(self.lua.state(), self.level, cstr!("au"), self.ar.get()) != 0,
|
||||
"lua_getinfo failed with `au`"
|
||||
);
|
||||
|
||||
#[cfg(not(feature = "luau"))]
|
||||
@@ -198,8 +198,8 @@ impl<'a> Debug<'a> {
|
||||
};
|
||||
#[cfg(feature = "luau")]
|
||||
let stack = DebugStack {
|
||||
num_ups: (*self.ar.get()).nupvals as i32,
|
||||
num_params: (*self.ar.get()).nparams as i32,
|
||||
num_ups: (*self.ar.get()).nupvals,
|
||||
num_params: (*self.ar.get()).nparams,
|
||||
is_vararg: (*self.ar.get()).isvararg != 0,
|
||||
};
|
||||
stack
|
||||
@@ -262,10 +262,15 @@ pub struct DebugSource<'a> {
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct DebugStack {
|
||||
pub num_ups: i32,
|
||||
/// Number of upvalues.
|
||||
pub num_ups: u8,
|
||||
/// Number of parameters.
|
||||
///
|
||||
/// Requires `feature = "lua54/lua53/lua52/luau"`
|
||||
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52", feature = "luau"))]
|
||||
pub num_params: i32,
|
||||
pub num_params: u8,
|
||||
/// Whether the function is a vararg function.
|
||||
///
|
||||
/// Requires `feature = "lua54/lua53/lua52/luau"`
|
||||
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52", feature = "luau"))]
|
||||
pub is_vararg: bool,
|
||||
|
||||
+16
-26
@@ -32,41 +32,32 @@
|
||||
//! [`serde::Serialize`] or [`serde::Deserialize`] can be converted.
|
||||
//! For convenience, additional functionality to handle `NULL` values and arrays is provided.
|
||||
//!
|
||||
//! The [`Value`] enum implements [`serde::Serialize`] trait to support serializing Lua values
|
||||
//! (including [`UserData`]) into Rust values.
|
||||
//! The [`Value`] enum and other types implement [`serde::Serialize`] trait to support serializing
|
||||
//! Lua values into Rust values.
|
||||
//!
|
||||
//! Requires `feature = "serialize"`.
|
||||
//!
|
||||
//! # Async/await support
|
||||
//!
|
||||
//! The [`create_async_function`] allows creating non-blocking functions that returns [`Future`].
|
||||
//! Lua code with async capabilities can be executed by [`call_async`] family of functions or
|
||||
//! polling [`AsyncThread`] using any runtime (eg. Tokio).
|
||||
//! The [`Lua::create_async_function`] allows creating non-blocking functions that returns
|
||||
//! [`Future`]. Lua code with async capabilities can be executed by [`Function::call_async`] family
|
||||
//! of functions or polling [`AsyncThread`] using any runtime (eg. Tokio).
|
||||
//!
|
||||
//! Requires `feature = "async"`.
|
||||
//!
|
||||
//! # `Send` requirement
|
||||
//! # `Send` and `Sync` support
|
||||
//!
|
||||
//! By default `mlua` is `!Send`. This can be changed by enabling `feature = "send"` that adds
|
||||
//! `Send` requirement to [`Function`]s and [`UserData`].
|
||||
//! `Send` requirement to Rust functions and [`UserData`] types.
|
||||
//!
|
||||
//! In this case [`Lua`] object and their types can be send or used from other threads. Internally
|
||||
//! access to Lua VM is synchronized using a reentrant mutex that can be locked many times within
|
||||
//! the same thread.
|
||||
//!
|
||||
//! [Lua programming language]: https://www.lua.org/
|
||||
//! [`Lua`]: crate::Lua
|
||||
//! [executing]: crate::Chunk::exec
|
||||
//! [evaluating]: crate::Chunk::eval
|
||||
//! [globals]: crate::Lua::globals
|
||||
//! [`IntoLua`]: crate::IntoLua
|
||||
//! [`FromLua`]: crate::FromLua
|
||||
//! [`IntoLuaMulti`]: crate::IntoLuaMulti
|
||||
//! [`FromLuaMulti`]: crate::FromLuaMulti
|
||||
//! [`Function`]: crate::Function
|
||||
//! [`UserData`]: crate::UserData
|
||||
//! [`UserDataFields`]: crate::UserDataFields
|
||||
//! [`UserDataMethods`]: crate::UserDataMethods
|
||||
//! [`LuaSerdeExt`]: crate::LuaSerdeExt
|
||||
//! [`Value`]: crate::Value
|
||||
//! [`create_async_function`]: crate::Lua::create_async_function
|
||||
//! [`call_async`]: crate::Function::call_async
|
||||
//! [`AsyncThread`]: crate::AsyncThread
|
||||
//! [`Future`]: std::future::Future
|
||||
//! [`serde::Serialize`]: https://docs.serde.rs/serde/ser/trait.Serialize.html
|
||||
//! [`serde::Deserialize`]: https://docs.serde.rs/serde/de/trait.Deserialize.html
|
||||
@@ -74,6 +65,7 @@
|
||||
// Deny warnings inside doc tests / examples. When this isn't present, rustdoc doesn't show *any*
|
||||
// warnings at all.
|
||||
#![cfg_attr(docsrs, feature(doc_cfg))]
|
||||
#![cfg_attr(not(send), allow(clippy::arc_with_non_send_sync))]
|
||||
|
||||
#[macro_use]
|
||||
mod macros;
|
||||
@@ -99,6 +91,7 @@ mod types;
|
||||
mod userdata;
|
||||
mod util;
|
||||
mod value;
|
||||
mod vector;
|
||||
|
||||
pub mod prelude;
|
||||
|
||||
@@ -133,9 +126,10 @@ pub use crate::hook::HookTriggers;
|
||||
|
||||
#[cfg(any(feature = "luau", doc))]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
|
||||
pub use crate::{buffer::Buffer, chunk::Compiler, function::CoverageInfo, types::Vector};
|
||||
pub use crate::{buffer::Buffer, chunk::Compiler, function::CoverageInfo, vector::Vector};
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
|
||||
pub use crate::{thread::AsyncThread, traits::LuaNativeAsyncFn};
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
@@ -199,10 +193,6 @@ extern crate mlua_derive;
|
||||
/// - The `//` (floor division) operator is unusable, as its start a comment.
|
||||
///
|
||||
/// Everything else should work.
|
||||
///
|
||||
/// [`AsChunk`]: crate::AsChunk
|
||||
/// [`UserData`]: crate::UserData
|
||||
/// [`IntoLua`]: crate::IntoLua
|
||||
#[cfg(feature = "macros")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "macros")))]
|
||||
pub use mlua_derive::chunk;
|
||||
|
||||
+1
-17
@@ -1,5 +1,5 @@
|
||||
use std::ffi::CStr;
|
||||
use std::os::raw::{c_float, c_int};
|
||||
use std::os::raw::c_int;
|
||||
|
||||
use crate::error::Result;
|
||||
use crate::state::Lua;
|
||||
@@ -11,7 +11,6 @@ impl Lua {
|
||||
let globals = self.globals();
|
||||
|
||||
globals.raw_set("collectgarbage", self.create_c_function(lua_collectgarbage)?)?;
|
||||
globals.raw_set("vector", self.create_c_function(lua_vector)?)?;
|
||||
|
||||
// Set `_VERSION` global to include version number
|
||||
// The environment variable `LUAU_VERSION` set by the build script
|
||||
@@ -65,21 +64,6 @@ unsafe extern "C-unwind" fn lua_collectgarbage(state: *mut ffi::lua_State) -> c_
|
||||
}
|
||||
}
|
||||
|
||||
// Luau vector datatype constructor
|
||||
unsafe extern "C-unwind" fn lua_vector(state: *mut ffi::lua_State) -> c_int {
|
||||
let x = ffi::luaL_checknumber(state, 1) as c_float;
|
||||
let y = ffi::luaL_checknumber(state, 2) as c_float;
|
||||
let z = ffi::luaL_checknumber(state, 3) as c_float;
|
||||
#[cfg(feature = "luau-vector4")]
|
||||
let w = ffi::luaL_checknumber(state, 4) as c_float;
|
||||
|
||||
#[cfg(not(feature = "luau-vector4"))]
|
||||
ffi::lua_pushvector(state, x, y, z);
|
||||
#[cfg(feature = "luau-vector4")]
|
||||
ffi::lua_pushvector(state, x, y, z, w);
|
||||
1
|
||||
}
|
||||
|
||||
pub(crate) use package::register_package_module;
|
||||
|
||||
mod package;
|
||||
|
||||
+2
-2
@@ -20,7 +20,7 @@ use {libloading::Library, rustc_hash::FxHashMap};
|
||||
//
|
||||
|
||||
#[cfg(unix)]
|
||||
const TARGET_MLUA_LUAU_ABI_VERSION: u32 = 1;
|
||||
const TARGET_MLUA_LUAU_ABI_VERSION: u32 = 2;
|
||||
|
||||
#[cfg(all(unix, feature = "module"))]
|
||||
#[no_mangle]
|
||||
@@ -203,7 +203,7 @@ fn lua_loader(lua: &Lua, modname: StdString) -> Result<Value> {
|
||||
match fs::read(&file_path) {
|
||||
Ok(buf) => {
|
||||
return lua
|
||||
.load(&buf)
|
||||
.load(buf)
|
||||
.set_name(format!("={}", file_path.display()))
|
||||
.set_mode(ChunkMode::Text)
|
||||
.into_function()
|
||||
|
||||
+64
-26
@@ -11,7 +11,7 @@ use crate::traits::{FromLua, FromLuaMulti, IntoLua, IntoLuaMulti};
|
||||
use crate::util::check_stack;
|
||||
use crate::value::{Nil, Value};
|
||||
|
||||
/// Result is convertible to `MultiValue` following the common Lua idiom of returning the result
|
||||
/// Result is convertible to [`MultiValue`] following the common Lua idiom of returning the result
|
||||
/// on success, or in the case of an error, returning `nil` and an error message.
|
||||
impl<T: IntoLua, E: IntoLua> IntoLuaMulti for StdResult<T, E> {
|
||||
#[inline]
|
||||
@@ -124,6 +124,23 @@ impl MultiValue {
|
||||
MultiValue(VecDeque::with_capacity(capacity))
|
||||
}
|
||||
|
||||
/// Creates a `MultiValue` container from vector of values.
|
||||
///
|
||||
/// This methods needs *O*(*n*) data movement if the circular buffer doesn't happen to be at the
|
||||
/// beginning of the allocation.
|
||||
#[inline]
|
||||
pub fn from_vec(vec: Vec<Value>) -> MultiValue {
|
||||
vec.into()
|
||||
}
|
||||
|
||||
/// Consumes the `MultiValue` and returns a vector of values.
|
||||
///
|
||||
/// This methods works in *O*(1) time and does not allocate any additional memory.
|
||||
#[inline]
|
||||
pub fn into_vec(self) -> Vec<Value> {
|
||||
self.into()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn from_lua_iter<T: IntoLua>(lua: &Lua, iter: impl IntoIterator<Item = T>) -> Result<Self> {
|
||||
let iter = iter.into_iter();
|
||||
@@ -135,6 +152,20 @@ impl MultiValue {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Vec<Value>> for MultiValue {
|
||||
#[inline]
|
||||
fn from(value: Vec<Value>) -> Self {
|
||||
MultiValue(value.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<MultiValue> for Vec<Value> {
|
||||
#[inline]
|
||||
fn from(value: MultiValue) -> Self {
|
||||
value.0.into()
|
||||
}
|
||||
}
|
||||
|
||||
impl FromIterator<Value> for MultiValue {
|
||||
#[inline]
|
||||
fn from_iter<I: IntoIterator<Item = Value>>(iter: I) -> Self {
|
||||
@@ -203,10 +234,7 @@ impl FromLuaMulti for MultiValue {
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
///
|
||||
/// [`FromLua`]: crate::FromLua
|
||||
/// [`MultiValue`]: crate::MultiValue
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Default, Debug, Clone)]
|
||||
pub struct Variadic<T>(Vec<T>);
|
||||
|
||||
impl<T> Variadic<T> {
|
||||
@@ -214,11 +242,38 @@ impl<T> Variadic<T> {
|
||||
pub const fn new() -> Variadic<T> {
|
||||
Variadic(Vec::new())
|
||||
}
|
||||
|
||||
/// Creates an empty `Variadic` container with space for at least `capacity` elements.
|
||||
pub fn with_capacity(capacity: usize) -> Variadic<T> {
|
||||
Variadic(Vec::with_capacity(capacity))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Default for Variadic<T> {
|
||||
fn default() -> Variadic<T> {
|
||||
const { Variadic::new() }
|
||||
impl<T> Deref for Variadic<T> {
|
||||
type Target = Vec<T>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> DerefMut for Variadic<T> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> From<Vec<T>> for Variadic<T> {
|
||||
#[inline]
|
||||
fn from(vec: Vec<T>) -> Self {
|
||||
Variadic(vec)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> From<Variadic<T>> for Vec<T> {
|
||||
#[inline]
|
||||
fn from(value: Variadic<T>) -> Self {
|
||||
value.0
|
||||
}
|
||||
}
|
||||
|
||||
@@ -237,20 +292,6 @@ impl<T> IntoIterator for Variadic<T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Deref for Variadic<T> {
|
||||
type Target = Vec<T>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> DerefMut for Variadic<T> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: IntoLua> IntoLuaMulti for Variadic<T> {
|
||||
#[inline]
|
||||
fn into_lua_multi(self, lua: &Lua) -> Result<MultiValue> {
|
||||
@@ -290,10 +331,7 @@ macro_rules! impl_tuple {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn from_stack_multi(nvals: c_int, lua: &RawLua) -> Result<Self> {
|
||||
if nvals > 0 {
|
||||
ffi::lua_pop(lua.state(), nvals);
|
||||
}
|
||||
unsafe fn from_stack_multi(_nvals: c_int, _lua: &RawLua) -> Result<Self> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
+120
-57
@@ -1,7 +1,6 @@
|
||||
use std::cell::RefCell;
|
||||
use std::marker::PhantomData;
|
||||
use std::mem;
|
||||
use std::os::raw::c_void;
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
use crate::function::Function;
|
||||
@@ -9,7 +8,9 @@ use crate::state::{Lua, LuaGuard, RawLua};
|
||||
use crate::traits::{FromLuaMulti, IntoLuaMulti};
|
||||
use crate::types::{Callback, CallbackUpvalue, ScopedCallback, ValueRef};
|
||||
use crate::userdata::{AnyUserData, UserData, UserDataRegistry, UserDataStorage};
|
||||
use crate::util::{self, assert_stack, check_stack, get_userdata, take_userdata, StackGuard};
|
||||
use crate::util::{
|
||||
self, assert_stack, check_stack, get_metatable_ptr, get_userdata, take_userdata, StackGuard,
|
||||
};
|
||||
|
||||
/// Constructed by the [`Lua::scope`] method, allows temporarily creating Lua userdata and
|
||||
/// callbacks that are not required to be `Send` or `'static`.
|
||||
@@ -17,7 +18,9 @@ use crate::util::{self, assert_stack, check_stack, get_userdata, take_userdata,
|
||||
/// See [`Lua::scope`] for more details.
|
||||
pub struct Scope<'scope, 'env: 'scope> {
|
||||
lua: LuaGuard,
|
||||
// Internal destructors run first, then user destructors (based on the declaration order)
|
||||
destructors: Destructors<'env>,
|
||||
user_destructors: UserDestructors<'env>,
|
||||
_scope_invariant: PhantomData<&'scope mut &'scope ()>,
|
||||
_env_invariant: PhantomData<&'env mut &'env ()>,
|
||||
}
|
||||
@@ -27,11 +30,14 @@ type DestructorCallback<'a> = Box<dyn FnOnce(&RawLua, ValueRef) -> Vec<Box<dyn F
|
||||
// Implement Drop on Destructors instead of Scope to avoid compilation error
|
||||
struct Destructors<'a>(RefCell<Vec<(ValueRef, DestructorCallback<'a>)>>);
|
||||
|
||||
struct UserDestructors<'a>(RefCell<Vec<Box<dyn FnOnce() + 'a>>>);
|
||||
|
||||
impl<'scope, 'env: 'scope> Scope<'scope, 'env> {
|
||||
pub(crate) fn new(lua: LuaGuard) -> Self {
|
||||
Scope {
|
||||
lua,
|
||||
destructors: Destructors(RefCell::new(Vec::new())),
|
||||
user_destructors: UserDestructors(RefCell::new(Vec::new())),
|
||||
_scope_invariant: PhantomData,
|
||||
_env_invariant: PhantomData,
|
||||
}
|
||||
@@ -82,11 +88,9 @@ impl<'scope, 'env: 'scope> Scope<'scope, 'env> {
|
||||
where
|
||||
T: UserData + 'static,
|
||||
{
|
||||
unsafe {
|
||||
let ud = self.lua.make_userdata(UserDataStorage::new_ref(data))?;
|
||||
self.seal_userdata::<T>(&ud)?;
|
||||
Ok(ud)
|
||||
}
|
||||
let ud = unsafe { self.lua.make_userdata(UserDataStorage::new_ref(data)) }?;
|
||||
self.seal_userdata::<T>(&ud);
|
||||
Ok(ud)
|
||||
}
|
||||
|
||||
/// Creates a Lua userdata object from a mutable reference to custom userdata type.
|
||||
@@ -98,11 +102,9 @@ impl<'scope, 'env: 'scope> Scope<'scope, 'env> {
|
||||
where
|
||||
T: UserData + 'static,
|
||||
{
|
||||
unsafe {
|
||||
let ud = self.lua.make_userdata(UserDataStorage::new_ref_mut(data))?;
|
||||
self.seal_userdata::<T>(&ud)?;
|
||||
Ok(ud)
|
||||
}
|
||||
let ud = unsafe { self.lua.make_userdata(UserDataStorage::new_ref_mut(data)) }?;
|
||||
self.seal_userdata::<T>(&ud);
|
||||
Ok(ud)
|
||||
}
|
||||
|
||||
/// Creates a Lua userdata object from a reference to custom Rust type.
|
||||
@@ -116,11 +118,9 @@ impl<'scope, 'env: 'scope> Scope<'scope, 'env> {
|
||||
where
|
||||
T: 'static,
|
||||
{
|
||||
unsafe {
|
||||
let ud = self.lua.make_any_userdata(UserDataStorage::new_ref(data))?;
|
||||
self.seal_userdata::<T>(&ud)?;
|
||||
Ok(ud)
|
||||
}
|
||||
let ud = unsafe { self.lua.make_any_userdata(UserDataStorage::new_ref(data)) }?;
|
||||
self.seal_userdata::<T>(&ud);
|
||||
Ok(ud)
|
||||
}
|
||||
|
||||
/// Creates a Lua userdata object from a mutable reference to custom Rust type.
|
||||
@@ -132,11 +132,9 @@ impl<'scope, 'env: 'scope> Scope<'scope, 'env> {
|
||||
where
|
||||
T: 'static,
|
||||
{
|
||||
unsafe {
|
||||
let ud = self.lua.make_any_userdata(UserDataStorage::new_ref_mut(data))?;
|
||||
self.seal_userdata::<T>(&ud)?;
|
||||
Ok(ud)
|
||||
}
|
||||
let ud = unsafe { self.lua.make_any_userdata(UserDataStorage::new_ref_mut(data)) }?;
|
||||
self.seal_userdata::<T>(&ud);
|
||||
Ok(ud)
|
||||
}
|
||||
|
||||
/// Creates a Lua userdata object from a custom userdata type.
|
||||
@@ -146,14 +144,15 @@ impl<'scope, 'env: 'scope> Scope<'scope, 'env> {
|
||||
/// [`Lua::scope`] for more details.
|
||||
///
|
||||
/// The main limitation that comes from using non-'static userdata is that the produced userdata
|
||||
/// will no longer have a `TypeId` associated with it, because `TypeId` can only work for
|
||||
/// will no longer have a [`TypeId`] associated with it, because [`TypeId`] can only work for
|
||||
/// `'static` types. This means that it is impossible, once the userdata is created, to get a
|
||||
/// reference to it back *out* of an `AnyUserData` handle. This also implies that the
|
||||
/// reference to it back *out* of an [`AnyUserData`] handle. This also implies that the
|
||||
/// "function" type methods that can be added via [`UserDataMethods`] (the ones that accept
|
||||
/// `AnyUserData` as a first parameter) are vastly less useful. Also, there is no way to re-use
|
||||
/// a single metatable for multiple non-'static types, so there is a higher cost associated with
|
||||
/// creating the userdata metatable each time a new userdata is created.
|
||||
/// [`AnyUserData`] as a first parameter) are vastly less useful. Also, there is no way to
|
||||
/// re-use a single metatable for multiple non-'static types, so there is a higher cost
|
||||
/// associated with creating the userdata metatable each time a new userdata is created.
|
||||
///
|
||||
/// [`TypeId`]: std::any::TypeId
|
||||
/// [`UserDataMethods`]: crate::UserDataMethods
|
||||
pub fn create_userdata<T>(&'scope self, data: T) -> Result<AnyUserData>
|
||||
where
|
||||
@@ -164,7 +163,7 @@ impl<'scope, 'env: 'scope> Scope<'scope, 'env> {
|
||||
let _sg = StackGuard::new(state);
|
||||
check_stack(state, 3)?;
|
||||
|
||||
// // We don't write the data to the userdata until pushing the metatable
|
||||
// We don't write the data to the userdata until pushing the metatable
|
||||
let protect = !self.lua.unlikely_memory_error();
|
||||
#[cfg(feature = "luau")]
|
||||
let ud_ptr = {
|
||||
@@ -175,7 +174,7 @@ impl<'scope, 'env: 'scope> Scope<'scope, 'env> {
|
||||
let ud_ptr = util::push_uninit_userdata::<UserDataStorage<T>>(state, protect)?;
|
||||
|
||||
// Push the metatable and register it with no TypeId
|
||||
let mut registry = UserDataRegistry::new_unique(ud_ptr as *const c_void);
|
||||
let mut registry = UserDataRegistry::new_unique(ud_ptr as *mut _);
|
||||
T::register(&mut registry);
|
||||
self.lua.push_userdata_metatable(registry)?;
|
||||
let mt_ptr = ffi::lua_topointer(state, -1);
|
||||
@@ -187,33 +186,84 @@ impl<'scope, 'env: 'scope> Scope<'scope, 'env> {
|
||||
ffi::lua_setmetatable(state, -2);
|
||||
|
||||
let ud = AnyUserData(self.lua.pop_ref());
|
||||
|
||||
let destructor: DestructorCallback = Box::new(|rawlua, vref| {
|
||||
let state = rawlua.state();
|
||||
let _sg = StackGuard::new(state);
|
||||
assert_stack(state, 2);
|
||||
|
||||
// Check that userdata is valid (very likely)
|
||||
if rawlua.push_userdata_ref(&vref).is_err() {
|
||||
return vec![];
|
||||
}
|
||||
|
||||
// Deregister metatable
|
||||
ffi::lua_getmetatable(state, -1);
|
||||
let mt_ptr = ffi::lua_topointer(state, -1);
|
||||
ffi::lua_pop(state, 1);
|
||||
rawlua.deregister_userdata_metatable(mt_ptr);
|
||||
|
||||
let ud = take_userdata::<UserDataStorage<T>>(state);
|
||||
|
||||
vec![Box::new(move || drop(ud))]
|
||||
});
|
||||
self.destructors.0.borrow_mut().push((ud.0.clone(), destructor));
|
||||
self.seal_userdata::<T>(&ud);
|
||||
|
||||
Ok(ud)
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a Lua userdata object from a custom Rust type.
|
||||
///
|
||||
/// Since the Rust type is not required to be static and implement [`UserData`] trait,
|
||||
/// you need to provide a function to register fields or methods for the object.
|
||||
///
|
||||
/// See also [`Scope::create_userdata`] for more details about non-static limitations.
|
||||
pub fn create_any_userdata<T>(
|
||||
&'scope self,
|
||||
data: T,
|
||||
register: impl FnOnce(&mut UserDataRegistry<T>),
|
||||
) -> Result<AnyUserData>
|
||||
where
|
||||
T: 'env,
|
||||
{
|
||||
let state = self.lua.state();
|
||||
let ud = unsafe {
|
||||
let _sg = StackGuard::new(state);
|
||||
check_stack(state, 3)?;
|
||||
|
||||
// We don't write the data to the userdata until pushing the metatable
|
||||
let protect = !self.lua.unlikely_memory_error();
|
||||
#[cfg(feature = "luau")]
|
||||
let ud_ptr = {
|
||||
let data = UserDataStorage::new_scoped(data);
|
||||
util::push_userdata::<UserDataStorage<T>>(state, data, protect)?
|
||||
};
|
||||
#[cfg(not(feature = "luau"))]
|
||||
let ud_ptr = util::push_uninit_userdata::<UserDataStorage<T>>(state, protect)?;
|
||||
|
||||
// Push the metatable and register it with no TypeId
|
||||
let mut registry = UserDataRegistry::new_unique(ud_ptr as *mut _);
|
||||
register(&mut registry);
|
||||
self.lua.push_userdata_metatable(registry)?;
|
||||
let mt_ptr = ffi::lua_topointer(state, -1);
|
||||
self.lua.register_userdata_metatable(mt_ptr, None);
|
||||
|
||||
// Write data to the pointer and attach metatable
|
||||
#[cfg(not(feature = "luau"))]
|
||||
std::ptr::write(ud_ptr, UserDataStorage::new_scoped(data));
|
||||
ffi::lua_setmetatable(state, -2);
|
||||
|
||||
AnyUserData(self.lua.pop_ref())
|
||||
};
|
||||
self.seal_userdata::<T>(&ud);
|
||||
Ok(ud)
|
||||
}
|
||||
|
||||
/// Adds a destructor function to be run when the scope ends.
|
||||
///
|
||||
/// This functionality is useful for cleaning up any resources after the scope ends.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// # use mlua::{Error, Lua, Result};
|
||||
/// # fn main() -> Result<()> {
|
||||
/// let lua = Lua::new();
|
||||
/// let ud = lua.create_any_userdata(String::from("hello"))?;
|
||||
/// lua.scope(|scope| {
|
||||
/// scope.add_destructor(|| {
|
||||
/// _ = ud.take::<String>();
|
||||
/// });
|
||||
/// // Run the code that uses `ud` here
|
||||
/// Ok(())
|
||||
/// })?;
|
||||
/// assert!(matches!(ud.borrow::<String>(), Err(Error::UserDataDestructed)));
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
pub fn add_destructor(&'scope self, destructor: impl FnOnce() + 'env) {
|
||||
self.user_destructors.0.borrow_mut().push(Box::new(destructor));
|
||||
}
|
||||
|
||||
unsafe fn create_callback(&'scope self, f: ScopedCallback<'scope>) -> Result<Function> {
|
||||
let f = mem::transmute::<ScopedCallback, Callback>(f);
|
||||
let f = self.lua.create_callback(f)?;
|
||||
@@ -232,23 +282,27 @@ impl<'scope, 'env: 'scope> Scope<'scope, 'env> {
|
||||
}
|
||||
|
||||
/// Shortens the lifetime of the userdata to the lifetime of the scope.
|
||||
unsafe fn seal_userdata<T: 'static>(&self, ud: &AnyUserData) -> Result<()> {
|
||||
let destructor: DestructorCallback = Box::new(|rawlua, vref| {
|
||||
fn seal_userdata<T: 'env>(&self, ud: &AnyUserData) {
|
||||
let destructor: DestructorCallback = Box::new(|rawlua, vref| unsafe {
|
||||
let state = rawlua.state();
|
||||
let _sg = StackGuard::new(state);
|
||||
assert_stack(state, 2);
|
||||
|
||||
// Ensure that userdata is not destructed
|
||||
if rawlua.push_userdata_ref(&vref).is_err() {
|
||||
return vec![];
|
||||
match rawlua.push_userdata_ref(&vref) {
|
||||
Ok(Some(_)) => {}
|
||||
Ok(None) => {
|
||||
// Deregister metatable
|
||||
let mt_ptr = get_metatable_ptr(state, -1);
|
||||
rawlua.deregister_userdata_metatable(mt_ptr);
|
||||
}
|
||||
Err(_) => return vec![],
|
||||
}
|
||||
|
||||
let data = take_userdata::<UserDataStorage<T>>(state);
|
||||
vec![Box::new(move || drop(data))]
|
||||
});
|
||||
self.destructors.0.borrow_mut().push((ud.0.clone(), destructor));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -270,3 +324,12 @@ impl Drop for Destructors<'_> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for UserDestructors<'_> {
|
||||
fn drop(&mut self) {
|
||||
let destructors = mem::take(&mut *self.0.borrow_mut());
|
||||
for destructor in destructors {
|
||||
destructor();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+7
-5
@@ -1,3 +1,5 @@
|
||||
//! Deserialize Lua values to a Rust data structure.
|
||||
|
||||
use std::cell::RefCell;
|
||||
use std::os::raw::c_void;
|
||||
use std::rc::Rc;
|
||||
@@ -94,12 +96,12 @@ impl Options {
|
||||
}
|
||||
|
||||
impl Deserializer {
|
||||
/// Creates a new Lua Deserializer for the `Value`.
|
||||
/// Creates a new Lua Deserializer for the [`Value`].
|
||||
pub fn new(value: Value) -> Self {
|
||||
Self::new_with_options(value, Options::default())
|
||||
}
|
||||
|
||||
/// Creates a new Lua Deserializer for the `Value` with custom options.
|
||||
/// Creates a new Lua Deserializer for the [`Value`] with custom options.
|
||||
pub fn new_with_options(value: Value, options: Options) -> Self {
|
||||
Deserializer {
|
||||
value,
|
||||
@@ -418,7 +420,7 @@ impl<'de> de::SeqAccess<'de> for SeqDeserializer<'_> {
|
||||
|
||||
#[cfg(feature = "luau")]
|
||||
struct VecDeserializer {
|
||||
vec: crate::types::Vector,
|
||||
vec: crate::Vector,
|
||||
next: usize,
|
||||
options: Options,
|
||||
visited: Rc<RefCell<FxHashSet<*const c_void>>>,
|
||||
@@ -444,7 +446,7 @@ impl<'de> de::SeqAccess<'de> for VecDeserializer {
|
||||
}
|
||||
|
||||
fn size_hint(&self) -> Option<usize> {
|
||||
Some(crate::types::Vector::SIZE)
|
||||
Some(crate::Vector::SIZE)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -498,7 +500,7 @@ struct MapDeserializer<'a> {
|
||||
processed: usize,
|
||||
}
|
||||
|
||||
impl<'a> MapDeserializer<'a> {
|
||||
impl MapDeserializer<'_> {
|
||||
fn next_key_deserializer(&mut self) -> Result<Option<Deserializer>> {
|
||||
loop {
|
||||
match self.pairs.next() {
|
||||
|
||||
@@ -106,8 +106,6 @@ pub trait LuaSerdeExt: Sealed {
|
||||
///
|
||||
/// Requires `feature = "serialize"`
|
||||
///
|
||||
/// [`Value`]: crate::Value
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
@@ -133,8 +131,6 @@ pub trait LuaSerdeExt: Sealed {
|
||||
///
|
||||
/// Requires `feature = "serialize"`
|
||||
///
|
||||
/// [`Value`]: crate::Value
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
@@ -164,8 +160,6 @@ pub trait LuaSerdeExt: Sealed {
|
||||
///
|
||||
/// Requires `feature = "serialize"`
|
||||
///
|
||||
/// [`Value`]: crate::Value
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
|
||||
+5
-3
@@ -1,3 +1,5 @@
|
||||
//! Serialize a Rust data structure into Lua value.
|
||||
|
||||
use serde::{ser, Serialize};
|
||||
|
||||
use super::LuaSerdeExt;
|
||||
@@ -267,7 +269,7 @@ impl<'a> ser::Serializer for Serializer<'a> {
|
||||
#[inline]
|
||||
fn serialize_tuple_struct(self, name: &'static str, len: usize) -> Result<Self::SerializeTupleStruct> {
|
||||
#[cfg(feature = "luau")]
|
||||
if name == "Vector" && len == crate::types::Vector::SIZE {
|
||||
if name == "Vector" && len == crate::Vector::SIZE {
|
||||
return Ok(SerializeSeq::new_vector(self.lua, self.options));
|
||||
}
|
||||
_ = name;
|
||||
@@ -341,7 +343,7 @@ impl<'a> ser::Serializer for Serializer<'a> {
|
||||
pub struct SerializeSeq<'a> {
|
||||
lua: &'a Lua,
|
||||
#[cfg(feature = "luau")]
|
||||
vector: Option<crate::types::Vector>,
|
||||
vector: Option<crate::Vector>,
|
||||
table: Option<Table>,
|
||||
next: usize,
|
||||
options: Options,
|
||||
@@ -363,7 +365,7 @@ impl<'a> SerializeSeq<'a> {
|
||||
const fn new_vector(lua: &'a Lua, options: Options) -> Self {
|
||||
Self {
|
||||
lua,
|
||||
vector: Some(crate::types::Vector::zero()),
|
||||
vector: Some(crate::Vector::zero()),
|
||||
table: None,
|
||||
next: 0,
|
||||
options,
|
||||
|
||||
+156
-122
@@ -1,5 +1,5 @@
|
||||
use std::any::TypeId;
|
||||
use std::cell::RefCell;
|
||||
use std::cell::{BorrowError, BorrowMutError, RefCell};
|
||||
use std::marker::PhantomData;
|
||||
use std::ops::Deref;
|
||||
use std::os::raw::c_int;
|
||||
@@ -171,12 +171,10 @@ impl Lua {
|
||||
/// Creates a new Lua state and loads the **safe** subset of the standard libraries.
|
||||
///
|
||||
/// # Safety
|
||||
/// The created Lua state would have _some_ safety guarantees and would not allow to load unsafe
|
||||
/// The created Lua state will have _some_ safety guarantees and will not allow to load unsafe
|
||||
/// standard libraries or C modules.
|
||||
///
|
||||
/// See [`StdLib`] documentation for a list of unsafe modules that cannot be loaded.
|
||||
///
|
||||
/// [`StdLib`]: crate::StdLib
|
||||
pub fn new() -> Lua {
|
||||
mlua_expect!(
|
||||
Self::new_with(StdLib::ALL_SAFE, LuaOptions::default()),
|
||||
@@ -187,7 +185,7 @@ impl Lua {
|
||||
/// Creates a new Lua state and loads all the standard libraries.
|
||||
///
|
||||
/// # Safety
|
||||
/// The created Lua state would not have safety guarantees and would allow to load C modules.
|
||||
/// The created Lua state will not have safety guarantees and will allow to load C modules.
|
||||
pub unsafe fn unsafe_new() -> Lua {
|
||||
Self::unsafe_new_with(StdLib::ALL, LuaOptions::default())
|
||||
}
|
||||
@@ -197,12 +195,10 @@ impl Lua {
|
||||
/// Use the [`StdLib`] flags to specify the libraries you want to load.
|
||||
///
|
||||
/// # Safety
|
||||
/// The created Lua state would have _some_ safety guarantees and would not allow to load unsafe
|
||||
/// The created Lua state will have _some_ safety guarantees and will not allow to load unsafe
|
||||
/// standard libraries or C modules.
|
||||
///
|
||||
/// See [`StdLib`] documentation for a list of unsafe modules that cannot be loaded.
|
||||
///
|
||||
/// [`StdLib`]: crate::StdLib
|
||||
pub fn new_with(libs: StdLib, options: LuaOptions) -> Result<Lua> {
|
||||
#[cfg(not(feature = "luau"))]
|
||||
if libs.contains(StdLib::DEBUG) {
|
||||
@@ -222,7 +218,7 @@ impl Lua {
|
||||
if libs.contains(StdLib::PACKAGE) {
|
||||
mlua_expect!(lua.disable_c_modules(), "Error disabling C modules");
|
||||
}
|
||||
unsafe { lua.lock().set_safe() };
|
||||
lua.lock().mark_safe();
|
||||
|
||||
Ok(lua)
|
||||
}
|
||||
@@ -233,8 +229,6 @@ impl Lua {
|
||||
///
|
||||
/// # Safety
|
||||
/// The created Lua state will not have safety guarantees and allow to load C modules.
|
||||
///
|
||||
/// [`StdLib`]: crate::StdLib
|
||||
pub unsafe fn unsafe_new_with(libs: StdLib, options: LuaOptions) -> Lua {
|
||||
// Workaround to avoid stripping a few unused Lua symbols that could be imported
|
||||
// by C modules in unsafe mode
|
||||
@@ -289,6 +283,28 @@ impl Lua {
|
||||
///
|
||||
/// This method ensures that the Lua instance is locked while the function is called
|
||||
/// and restores Lua stack after the function returns.
|
||||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
/// # use mlua::{Lua, Result};
|
||||
/// # fn main() -> Result<()> {
|
||||
/// let lua = Lua::new();
|
||||
/// let n: i32 = unsafe {
|
||||
/// let nums = (3, 4, 5);
|
||||
/// lua.exec_raw(nums, |state| {
|
||||
/// let n = ffi::lua_gettop(state);
|
||||
/// let mut sum = 0;
|
||||
/// for i in 1..=n {
|
||||
/// sum += ffi::lua_tointeger(state, i);
|
||||
/// }
|
||||
/// ffi::lua_pop(state, n);
|
||||
/// ffi::lua_pushinteger(state, sum);
|
||||
/// })
|
||||
/// }?;
|
||||
/// assert_eq!(n, 12);
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
#[allow(clippy::missing_safety_doc)]
|
||||
pub unsafe fn exec_raw<R: FromLuaMulti>(
|
||||
&self,
|
||||
@@ -306,7 +322,11 @@ impl Lua {
|
||||
R::from_stack_multi(nresults, &lua)
|
||||
}
|
||||
|
||||
/// FIXME: Deprecated load_from_std_lib
|
||||
#[doc(hidden)]
|
||||
#[deprecated(since = "0.10.0", note = "please use `load_std_libs` instead")]
|
||||
pub fn load_from_std_lib(&self, libs: StdLib) -> Result<()> {
|
||||
self.load_std_libs(libs)
|
||||
}
|
||||
|
||||
/// Loads the specified subset of the standard libraries into an existing Lua state.
|
||||
///
|
||||
@@ -395,7 +415,7 @@ impl Lua {
|
||||
// Make sure that Lua is initialized
|
||||
let mut lua = Self::init_from_ptr(state);
|
||||
lua.collect_garbage = false;
|
||||
// `Lua` is no longer needed and must be dropped at this point to avoid possible memory leak
|
||||
// `Lua` is no longer needed and must be dropped at this point to avoid memory leak
|
||||
// in case of possible longjmp (lua_error) below
|
||||
drop(lua);
|
||||
|
||||
@@ -440,6 +460,7 @@ impl Lua {
|
||||
///
|
||||
/// ```
|
||||
/// # use mlua::{Lua, Result};
|
||||
/// # #[cfg(feature = "luau")]
|
||||
/// # fn main() -> Result<()> {
|
||||
/// let lua = Lua::new();
|
||||
///
|
||||
@@ -452,16 +473,19 @@ impl Lua {
|
||||
/// assert_eq!(lua.globals().get::<Option<u32>>("var")?, None);
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
///
|
||||
/// # #[cfg(not(feature = "luau"))]
|
||||
/// # fn main() {}
|
||||
/// ```
|
||||
///
|
||||
/// Requires `feature = "luau"`
|
||||
#[cfg(any(feature = "luau", docsrs))]
|
||||
#[cfg(any(feature = "luau", doc))]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
|
||||
pub fn sandbox(&self, enabled: bool) -> Result<()> {
|
||||
let lua = self.lock();
|
||||
unsafe {
|
||||
if (*lua.extra.get()).sandboxed != enabled {
|
||||
let state = lua.main_state;
|
||||
let state = lua.main_state();
|
||||
check_stack(state, 3)?;
|
||||
protect_lua!(state, 0, 0, |state| {
|
||||
if enabled {
|
||||
@@ -480,7 +504,7 @@ impl Lua {
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets a 'hook' function that will periodically be called as Lua code executes.
|
||||
/// Sets a hook function that will periodically be called as Lua code executes.
|
||||
///
|
||||
/// When exactly the hook function is called depends on the contents of the `triggers`
|
||||
/// parameter, see [`HookTriggers`] for more details.
|
||||
@@ -492,7 +516,7 @@ impl Lua {
|
||||
///
|
||||
/// This method sets a hook function for the current thread of this Lua instance.
|
||||
/// If you want to set a hook function for another thread (coroutine), use
|
||||
/// [`Thread::set_hook()`] instead.
|
||||
/// [`Thread::set_hook`] instead.
|
||||
///
|
||||
/// Please note you cannot have more than one hook function set at a time for this Lua instance.
|
||||
///
|
||||
@@ -517,7 +541,6 @@ impl Lua {
|
||||
/// # }
|
||||
/// ```
|
||||
///
|
||||
/// [`HookTriggers`]: crate::HookTriggers
|
||||
/// [`HookTriggers.every_nth_instruction`]: crate::HookTriggers::every_nth_instruction
|
||||
#[cfg(not(feature = "luau"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(not(feature = "luau"))))]
|
||||
@@ -529,7 +552,7 @@ impl Lua {
|
||||
unsafe { lua.set_thread_hook(lua.state(), triggers, callback) };
|
||||
}
|
||||
|
||||
/// Removes any hook previously set by [`Lua::set_hook()`] or [`Thread::set_hook()`].
|
||||
/// Removes any hook previously set by [`Lua::set_hook`] or [`Thread::set_hook`].
|
||||
///
|
||||
/// This function has no effect if a hook was not previously set.
|
||||
#[cfg(not(feature = "luau"))]
|
||||
@@ -539,10 +562,10 @@ impl Lua {
|
||||
unsafe {
|
||||
let state = lua.state();
|
||||
ffi::lua_sethook(state, None, 0, 0);
|
||||
match crate::util::get_main_state(lua.main_state) {
|
||||
Some(main_state) if !ptr::eq(state, main_state) => {
|
||||
match lua.main_state {
|
||||
Some(main_state) if state != main_state.as_ptr() => {
|
||||
// If main_state is different from state, remove hook from it too
|
||||
ffi::lua_sethook(main_state, None, 0, 0);
|
||||
ffi::lua_sethook(main_state.as_ptr(), None, 0, 0);
|
||||
}
|
||||
_ => {}
|
||||
};
|
||||
@@ -551,7 +574,7 @@ impl Lua {
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets an 'interrupt' function that will periodically be called by Luau VM.
|
||||
/// Sets an interrupt function that will periodically be called by Luau VM.
|
||||
///
|
||||
/// Any Luau code is guaranteed to call this handler "eventually"
|
||||
/// (in practice this can happen at any function call or at any loop iteration).
|
||||
@@ -570,6 +593,7 @@ impl Lua {
|
||||
/// ```
|
||||
/// # use std::sync::{Arc, atomic::{AtomicU64, Ordering}};
|
||||
/// # use mlua::{Lua, Result, ThreadStatus, VmState};
|
||||
/// # #[cfg(feature = "luau")]
|
||||
/// # fn main() -> Result<()> {
|
||||
/// let lua = Lua::new();
|
||||
/// let count = Arc::new(AtomicU64::new(0));
|
||||
@@ -592,8 +616,11 @@ impl Lua {
|
||||
/// }
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
///
|
||||
/// # #[cfg(not(feature = "luau"))]
|
||||
/// # fn main() {}
|
||||
/// ```
|
||||
#[cfg(any(feature = "luau", docsrs))]
|
||||
#[cfg(any(feature = "luau", doc))]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
|
||||
pub fn set_interrupt<F>(&self, callback: F)
|
||||
where
|
||||
@@ -627,20 +654,20 @@ impl Lua {
|
||||
let lua = self.lock();
|
||||
unsafe {
|
||||
(*lua.extra.get()).interrupt_callback = Some(Rc::new(callback));
|
||||
(*ffi::lua_callbacks(lua.main_state)).interrupt = Some(interrupt_proc);
|
||||
(*ffi::lua_callbacks(lua.main_state())).interrupt = Some(interrupt_proc);
|
||||
}
|
||||
}
|
||||
|
||||
/// Removes any 'interrupt' previously set by `set_interrupt`.
|
||||
/// Removes any interrupt function previously set by `set_interrupt`.
|
||||
///
|
||||
/// This function has no effect if an 'interrupt' was not previously set.
|
||||
#[cfg(any(feature = "luau", docsrs))]
|
||||
#[cfg(any(feature = "luau", doc))]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
|
||||
pub fn remove_interrupt(&self) {
|
||||
let lua = self.lock();
|
||||
unsafe {
|
||||
(*lua.extra.get()).interrupt_callback = None;
|
||||
(*ffi::lua_callbacks(lua.main_state)).interrupt = None;
|
||||
(*ffi::lua_callbacks(lua.main_state())).interrupt = None;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -670,10 +697,9 @@ impl Lua {
|
||||
}
|
||||
|
||||
let lua = self.lock();
|
||||
let state = lua.main_state;
|
||||
unsafe {
|
||||
(*lua.extra.get()).warn_callback = Some(Box::new(callback));
|
||||
ffi::lua_setwarnf(state, Some(warn_proc), lua.extra.get() as *mut c_void);
|
||||
ffi::lua_setwarnf(lua.state(), Some(warn_proc), lua.extra.get() as *mut c_void);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -688,7 +714,7 @@ impl Lua {
|
||||
let lua = self.lock();
|
||||
unsafe {
|
||||
(*lua.extra.get()).warn_callback = None;
|
||||
ffi::lua_setwarnf(lua.main_state, None, ptr::null_mut());
|
||||
ffi::lua_setwarnf(lua.state(), None, ptr::null_mut());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -740,13 +766,14 @@ impl Lua {
|
||||
/// Returns the amount of memory (in bytes) currently used inside this Lua state.
|
||||
pub fn used_memory(&self) -> usize {
|
||||
let lua = self.lock();
|
||||
let state = lua.main_state();
|
||||
unsafe {
|
||||
match MemoryState::get(lua.main_state) {
|
||||
match MemoryState::get(state) {
|
||||
mem_state if !mem_state.is_null() => (*mem_state).used_memory(),
|
||||
_ => {
|
||||
// Get data from the Lua GC
|
||||
let used_kbytes = ffi::lua_gc(lua.main_state, ffi::LUA_GCCOUNT, 0);
|
||||
let used_kbytes_rem = ffi::lua_gc(lua.main_state, ffi::LUA_GCCOUNTB, 0);
|
||||
let used_kbytes = ffi::lua_gc(state, ffi::LUA_GCCOUNT, 0);
|
||||
let used_kbytes_rem = ffi::lua_gc(state, ffi::LUA_GCCOUNTB, 0);
|
||||
(used_kbytes as usize) * 1024 + (used_kbytes_rem as usize)
|
||||
}
|
||||
}
|
||||
@@ -755,40 +782,40 @@ impl Lua {
|
||||
|
||||
/// Sets a memory limit (in bytes) on this Lua state.
|
||||
///
|
||||
/// Once an allocation occurs that would pass this memory limit,
|
||||
/// a `Error::MemoryError` is generated instead.
|
||||
/// Once an allocation occurs that would pass this memory limit, a `Error::MemoryError` is
|
||||
/// generated instead.
|
||||
/// Returns previous limit (zero means no limit).
|
||||
///
|
||||
/// Does not work in module mode where Lua state is managed externally.
|
||||
pub fn set_memory_limit(&self, limit: usize) -> Result<usize> {
|
||||
let lua = self.lock();
|
||||
unsafe {
|
||||
match MemoryState::get(lua.main_state) {
|
||||
match MemoryState::get(lua.state()) {
|
||||
mem_state if !mem_state.is_null() => Ok((*mem_state).set_memory_limit(limit)),
|
||||
_ => Err(Error::MemoryLimitNotAvailable),
|
||||
_ => Err(Error::MemoryControlNotAvailable),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns true if the garbage collector is currently running automatically.
|
||||
/// Returns `true` if the garbage collector is currently running automatically.
|
||||
///
|
||||
/// Requires `feature = "lua54/lua53/lua52/luau"`
|
||||
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52", feature = "luau"))]
|
||||
pub fn gc_is_running(&self) -> bool {
|
||||
let lua = self.lock();
|
||||
unsafe { ffi::lua_gc(lua.main_state, ffi::LUA_GCISRUNNING, 0) != 0 }
|
||||
unsafe { ffi::lua_gc(lua.main_state(), ffi::LUA_GCISRUNNING, 0) != 0 }
|
||||
}
|
||||
|
||||
/// Stop the Lua GC from running
|
||||
pub fn gc_stop(&self) {
|
||||
let lua = self.lock();
|
||||
unsafe { ffi::lua_gc(lua.main_state, ffi::LUA_GCSTOP, 0) };
|
||||
unsafe { ffi::lua_gc(lua.main_state(), ffi::LUA_GCSTOP, 0) };
|
||||
}
|
||||
|
||||
/// Restarts the Lua GC if it is not running
|
||||
pub fn gc_restart(&self) {
|
||||
let lua = self.lock();
|
||||
unsafe { ffi::lua_gc(lua.main_state, ffi::LUA_GCRESTART, 0) };
|
||||
unsafe { ffi::lua_gc(lua.main_state(), ffi::LUA_GCRESTART, 0) };
|
||||
}
|
||||
|
||||
/// Perform a full garbage-collection cycle.
|
||||
@@ -797,15 +824,16 @@ impl Lua {
|
||||
/// objects. Once to finish the current gc cycle, and once to start and finish the next cycle.
|
||||
pub fn gc_collect(&self) -> Result<()> {
|
||||
let lua = self.lock();
|
||||
let state = lua.main_state();
|
||||
unsafe {
|
||||
check_stack(lua.main_state, 2)?;
|
||||
protect_lua!(lua.main_state, 0, 0, fn(state) ffi::lua_gc(state, ffi::LUA_GCCOLLECT, 0))
|
||||
check_stack(state, 2)?;
|
||||
protect_lua!(state, 0, 0, fn(state) ffi::lua_gc(state, ffi::LUA_GCCOLLECT, 0))
|
||||
}
|
||||
}
|
||||
|
||||
/// Steps the garbage collector one indivisible step.
|
||||
///
|
||||
/// Returns true if this has finished a collection cycle.
|
||||
/// Returns `true` if this has finished a collection cycle.
|
||||
pub fn gc_step(&self) -> Result<bool> {
|
||||
self.gc_step_kbytes(0)
|
||||
}
|
||||
@@ -816,17 +844,18 @@ impl Lua {
|
||||
/// finished a collection cycle.
|
||||
pub fn gc_step_kbytes(&self, kbytes: c_int) -> Result<bool> {
|
||||
let lua = self.lock();
|
||||
let state = lua.main_state();
|
||||
unsafe {
|
||||
check_stack(lua.main_state, 3)?;
|
||||
protect_lua!(lua.main_state, 0, 0, |state| {
|
||||
check_stack(state, 3)?;
|
||||
protect_lua!(state, 0, 0, |state| {
|
||||
ffi::lua_gc(state, ffi::LUA_GCSTEP, kbytes) != 0
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the 'pause' value of the collector.
|
||||
/// Sets the `pause` value of the collector.
|
||||
///
|
||||
/// Returns the previous value of 'pause'. More information can be found in the Lua
|
||||
/// Returns the previous value of `pause`. More information can be found in the Lua
|
||||
/// [documentation].
|
||||
///
|
||||
/// For Luau this parameter sets GC goal
|
||||
@@ -834,23 +863,24 @@ impl Lua {
|
||||
/// [documentation]: https://www.lua.org/manual/5.4/manual.html#2.5
|
||||
pub fn gc_set_pause(&self, pause: c_int) -> c_int {
|
||||
let lua = self.lock();
|
||||
let state = lua.main_state();
|
||||
unsafe {
|
||||
#[cfg(not(feature = "luau"))]
|
||||
return ffi::lua_gc(lua.main_state, ffi::LUA_GCSETPAUSE, pause);
|
||||
return ffi::lua_gc(state, ffi::LUA_GCSETPAUSE, pause);
|
||||
#[cfg(feature = "luau")]
|
||||
return ffi::lua_gc(lua.main_state, ffi::LUA_GCSETGOAL, pause);
|
||||
return ffi::lua_gc(state, ffi::LUA_GCSETGOAL, pause);
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the 'step multiplier' value of the collector.
|
||||
/// Sets the `step multiplier` value of the collector.
|
||||
///
|
||||
/// Returns the previous value of the 'step multiplier'. More information can be found in the
|
||||
/// Returns the previous value of the `step multiplier`. More information can be found in the
|
||||
/// Lua [documentation].
|
||||
///
|
||||
/// [documentation]: https://www.lua.org/manual/5.4/manual.html#2.5
|
||||
pub fn gc_set_step_multiplier(&self, step_multiplier: c_int) -> c_int {
|
||||
let lua = self.lock();
|
||||
unsafe { ffi::lua_gc(lua.main_state, ffi::LUA_GCSETSTEPMUL, step_multiplier) }
|
||||
unsafe { ffi::lua_gc(lua.main_state(), ffi::LUA_GCSETSTEPMUL, step_multiplier) }
|
||||
}
|
||||
|
||||
/// Changes the collector to incremental mode with the given parameters.
|
||||
@@ -861,7 +891,7 @@ impl Lua {
|
||||
/// [documentation]: https://www.lua.org/manual/5.4/manual.html#2.5.1
|
||||
pub fn gc_inc(&self, pause: c_int, step_multiplier: c_int, step_size: c_int) -> GCMode {
|
||||
let lua = self.lock();
|
||||
let state = lua.main_state;
|
||||
let state = lua.main_state();
|
||||
|
||||
#[cfg(any(
|
||||
feature = "lua53",
|
||||
@@ -914,7 +944,7 @@ impl Lua {
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "lua54")))]
|
||||
pub fn gc_gen(&self, minor_multiplier: c_int, major_multiplier: c_int) -> GCMode {
|
||||
let lua = self.lock();
|
||||
let state = lua.main_state;
|
||||
let state = lua.main_state();
|
||||
let prev_mode = unsafe { ffi::lua_gc(state, ffi::LUA_GCGEN, minor_multiplier, major_multiplier) };
|
||||
match prev_mode {
|
||||
ffi::LUA_GCGEN => GCMode::Generational,
|
||||
@@ -985,9 +1015,10 @@ impl Lua {
|
||||
}
|
||||
}
|
||||
|
||||
/// Create and return an interned Lua string. Lua strings can be arbitrary `[u8]` data including
|
||||
/// embedded nulls, so in addition to `&str` and `&String`, you can also pass plain `&[u8]`
|
||||
/// here.
|
||||
/// Create and return an interned Lua string.
|
||||
///
|
||||
/// Lua strings can be arbitrary `[u8]` data including embedded nulls, so in addition to `&str`
|
||||
/// and `&String`, you can also pass plain `&[u8]` here.
|
||||
#[inline]
|
||||
pub fn create_string(&self, s: impl AsRef<[u8]>) -> Result<String> {
|
||||
unsafe { self.lock().create_string(s) }
|
||||
@@ -997,9 +1028,9 @@ impl Lua {
|
||||
///
|
||||
/// Requires `feature = "luau"`
|
||||
///
|
||||
/// [buffer]: https://luau-lang.org/library#buffer-library
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
|
||||
/// [buffer]: https://luau.org/library#buffer-library
|
||||
#[cfg(any(feature = "luau", doc))]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
|
||||
pub fn create_buffer(&self, buf: impl AsRef<[u8]>) -> Result<Buffer> {
|
||||
let lua = self.lock();
|
||||
let state = lua.state();
|
||||
@@ -1010,20 +1041,23 @@ impl Lua {
|
||||
}
|
||||
|
||||
let _sg = StackGuard::new(state);
|
||||
check_stack(state, 4)?;
|
||||
check_stack(state, 3)?;
|
||||
crate::util::push_buffer(state, buf.as_ref(), true)?;
|
||||
Ok(Buffer(lua.pop_ref()))
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates and returns a new empty table.
|
||||
#[inline]
|
||||
pub fn create_table(&self) -> Result<Table> {
|
||||
self.create_table_with_capacity(0, 0)
|
||||
}
|
||||
|
||||
/// Creates and returns a new empty table, with the specified capacity.
|
||||
/// `narr` is a hint for how many elements the table will have as a sequence;
|
||||
/// `nrec` is a hint for how many other elements the table will have.
|
||||
///
|
||||
/// - `narr` is a hint for how many elements the table will have as a sequence.
|
||||
/// - `nrec` is a hint for how many other elements the table will have.
|
||||
///
|
||||
/// Lua may use these hints to preallocate memory for the new table.
|
||||
pub fn create_table_with_capacity(&self, narr: usize, nrec: usize) -> Result<Table> {
|
||||
unsafe { self.lock().create_table_with_capacity(narr, nrec) }
|
||||
@@ -1109,9 +1143,6 @@ impl Lua {
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
///
|
||||
/// [`IntoLua`]: crate::IntoLua
|
||||
/// [`IntoLuaMulti`]: crate::IntoLuaMulti
|
||||
pub fn create_function<F, A, R>(&self, func: F) -> Result<Function>
|
||||
where
|
||||
F: Fn(&Lua, A) -> Result<R> + MaybeSend + 'static,
|
||||
@@ -1126,10 +1157,7 @@ impl Lua {
|
||||
|
||||
/// Wraps a Rust mutable closure, creating a callable Lua function handle to it.
|
||||
///
|
||||
/// This is a version of [`create_function`] that accepts a FnMut argument. Refer to
|
||||
/// [`create_function`] for more information about the implementation.
|
||||
///
|
||||
/// [`create_function`]: #method.create_function
|
||||
/// This is a version of [`Lua::create_function`] that accepts a `FnMut` argument.
|
||||
pub fn create_function_mut<F, A, R>(&self, func: F) -> Result<Function>
|
||||
where
|
||||
F: FnMut(&Lua, A) -> Result<R> + MaybeSend + 'static,
|
||||
@@ -1158,9 +1186,9 @@ impl Lua {
|
||||
/// call `yield()` passing internal representation of a `Poll::Pending` value.
|
||||
///
|
||||
/// The function must be called inside Lua coroutine ([`Thread`]) to be able to suspend its
|
||||
/// execution. An executor should be used to poll [`AsyncThread`] and mlua will take a
|
||||
/// provided Waker in that case. Otherwise noop waker will be used if try to call the
|
||||
/// function outside of Rust executors.
|
||||
/// execution. An executor should be used to poll [`AsyncThread`] and mlua will take a provided
|
||||
/// Waker in that case. Otherwise noop waker will be used if try to call the function outside of
|
||||
/// Rust executors.
|
||||
///
|
||||
/// The family of `call_async()` functions takes care about creating [`Thread`].
|
||||
///
|
||||
@@ -1189,7 +1217,6 @@ impl Lua {
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// [`Thread`]: crate::Thread
|
||||
/// [`AsyncThread`]: crate::AsyncThread
|
||||
#[cfg(feature = "async")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
|
||||
@@ -1246,7 +1273,7 @@ impl Lua {
|
||||
|
||||
/// Creates a Lua userdata object from a custom Rust type.
|
||||
///
|
||||
/// You can register the type using [`Lua::register_userdata_type()`] to add fields or methods
|
||||
/// You can register the type using [`Lua::register_userdata_type`] to add fields or methods
|
||||
/// _before_ calling this method.
|
||||
/// Otherwise, the userdata object will have an empty metatable.
|
||||
///
|
||||
@@ -1261,7 +1288,7 @@ impl Lua {
|
||||
|
||||
/// Creates a Lua userdata object from a custom serializable Rust type.
|
||||
///
|
||||
/// See [`Lua::create_any_userdata()`] for more details.
|
||||
/// See [`Lua::create_any_userdata`] for more details.
|
||||
///
|
||||
/// Requires `feature = "serialize"`
|
||||
#[cfg(feature = "serialize")]
|
||||
@@ -1419,9 +1446,10 @@ impl Lua {
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a handle to the active `Thread`. For calls to `Lua` this will be the main Lua
|
||||
/// thread, for parameters given to a callback, this will be whatever Lua thread called the
|
||||
/// callback.
|
||||
/// Returns a handle to the active `Thread`.
|
||||
///
|
||||
/// For calls to `Lua` this will be the main Lua thread, for parameters given to a callback,
|
||||
/// this will be whatever Lua thread called the callback.
|
||||
pub fn current_thread(&self) -> Thread {
|
||||
let lua = self.lock();
|
||||
let state = lua.state();
|
||||
@@ -1433,30 +1461,21 @@ impl Lua {
|
||||
}
|
||||
}
|
||||
|
||||
/// Calls the given function with a `Scope` parameter, giving the function the ability to create
|
||||
/// userdata and callbacks from rust types that are !Send or non-'static.
|
||||
/// Calls the given function with a [`Scope`] parameter, giving the function the ability to
|
||||
/// create userdata and callbacks from Rust types that are `!Send` or non-`'static`.
|
||||
///
|
||||
/// The lifetime of any function or userdata created through `Scope` lasts only until the
|
||||
/// The lifetime of any function or userdata created through [`Scope`] lasts only until the
|
||||
/// completion of this method call, on completion all such created values are automatically
|
||||
/// dropped and Lua references to them are invalidated. If a script accesses a value created
|
||||
/// through `Scope` outside of this method, a Lua error will result. Since we can ensure the
|
||||
/// lifetime of values created through `Scope`, and we know that `Lua` cannot be sent to another
|
||||
/// thread while `Scope` is live, it is safe to allow !Send datatypes and whose lifetimes only
|
||||
/// outlive the scope lifetime.
|
||||
///
|
||||
/// Inside the scope callback, all handles created through Scope will share the same unique 'lua
|
||||
/// lifetime of the parent `Lua`. This allows scoped and non-scoped values to be mixed in
|
||||
/// API calls, which is very useful (e.g. passing a scoped userdata to a non-scoped function).
|
||||
/// However, this also enables handles to scoped values to be trivially leaked from the given
|
||||
/// callback. This is not dangerous, though! After the callback returns, all scoped values are
|
||||
/// invalidated, which means that though references may exist, the Rust types backing them have
|
||||
/// dropped. `Function` types will error when called, and `AnyUserData` will be typeless. It
|
||||
/// would be impossible to prevent handles to scoped values from escaping anyway, since you
|
||||
/// would always be able to smuggle them through Lua state.
|
||||
/// through [`Scope`] outside of this method, a Lua error will result. Since we can ensure the
|
||||
/// lifetime of values created through [`Scope`], and we know that [`Lua`] cannot be sent to
|
||||
/// another thread while [`Scope`] is live, it is safe to allow `!Send` data types and whose
|
||||
/// lifetimes only outlive the scope lifetime.
|
||||
pub fn scope<'env, R>(
|
||||
&self,
|
||||
f: impl for<'scope> FnOnce(&'scope mut Scope<'scope, 'env>) -> Result<R>,
|
||||
) -> Result<R> {
|
||||
// TODO: Update to `&Scope` in next major release
|
||||
f(&mut Scope::new(self.lock_arc()))
|
||||
}
|
||||
|
||||
@@ -1544,41 +1563,41 @@ impl Lua {
|
||||
})
|
||||
}
|
||||
|
||||
/// Converts a value that implements `IntoLua` into a `Value` instance.
|
||||
/// Converts a value that implements [`IntoLua`] into a [`Value`] instance.
|
||||
#[inline]
|
||||
pub fn pack(&self, t: impl IntoLua) -> Result<Value> {
|
||||
t.into_lua(self)
|
||||
}
|
||||
|
||||
/// Converts a `Value` instance into a value that implements `FromLua`.
|
||||
/// Converts a [`Value`] instance into a value that implements [`FromLua`].
|
||||
#[inline]
|
||||
pub fn unpack<T: FromLua>(&self, value: Value) -> Result<T> {
|
||||
T::from_lua(value, self)
|
||||
}
|
||||
|
||||
/// Converts a value that implements `IntoLua` into a `FromLua` variant.
|
||||
/// Converts a value that implements [`IntoLua`] into a [`FromLua`] variant.
|
||||
#[inline]
|
||||
pub fn convert<U: FromLua>(&self, value: impl IntoLua) -> Result<U> {
|
||||
U::from_lua(value.into_lua(self)?, self)
|
||||
}
|
||||
|
||||
/// Converts a value that implements `IntoLuaMulti` into a `MultiValue` instance.
|
||||
/// Converts a value that implements [`IntoLuaMulti`] into a [`MultiValue`] instance.
|
||||
#[inline]
|
||||
pub fn pack_multi(&self, t: impl IntoLuaMulti) -> Result<MultiValue> {
|
||||
t.into_lua_multi(self)
|
||||
}
|
||||
|
||||
/// Converts a `MultiValue` instance into a value that implements `FromLuaMulti`.
|
||||
/// Converts a [`MultiValue`] instance into a value that implements [`FromLuaMulti`].
|
||||
#[inline]
|
||||
pub fn unpack_multi<T: FromLuaMulti>(&self, value: MultiValue) -> Result<T> {
|
||||
T::from_lua_multi(value, self)
|
||||
}
|
||||
|
||||
/// Set a value in the Lua registry based on a string name.
|
||||
/// Set a value in the Lua registry based on a string key.
|
||||
///
|
||||
/// This value will be available to rust from all `Lua` instances which share the same main
|
||||
/// This value will be available to Rust from all Lua instances which share the same main
|
||||
/// state.
|
||||
pub fn set_named_registry_value(&self, name: &str, t: impl IntoLua) -> Result<()> {
|
||||
pub fn set_named_registry_value(&self, key: &str, t: impl IntoLua) -> Result<()> {
|
||||
let lua = self.lock();
|
||||
let state = lua.state();
|
||||
unsafe {
|
||||
@@ -1586,15 +1605,15 @@ impl Lua {
|
||||
check_stack(state, 5)?;
|
||||
|
||||
lua.push(t)?;
|
||||
rawset_field(state, ffi::LUA_REGISTRYINDEX, name)
|
||||
rawset_field(state, ffi::LUA_REGISTRYINDEX, key)
|
||||
}
|
||||
}
|
||||
|
||||
/// Get a value from the Lua registry based on a string name.
|
||||
/// Get a value from the Lua registry based on a string key.
|
||||
///
|
||||
/// Any Lua instance which shares the underlying main state may call this method to
|
||||
/// get a value previously set by [`Lua::set_named_registry_value`].
|
||||
pub fn named_registry_value<T>(&self, name: &str) -> Result<T>
|
||||
pub fn named_registry_value<T>(&self, key: &str) -> Result<T>
|
||||
where
|
||||
T: FromLua,
|
||||
{
|
||||
@@ -1605,7 +1624,7 @@ impl Lua {
|
||||
check_stack(state, 3)?;
|
||||
|
||||
let protect = !lua.unlikely_memory_error();
|
||||
push_string(state, name.as_bytes(), protect)?;
|
||||
push_string(state, key.as_bytes(), protect)?;
|
||||
ffi::lua_rawget(state, ffi::LUA_REGISTRYINDEX);
|
||||
|
||||
T::from_stack(-1, &lua)
|
||||
@@ -1614,14 +1633,15 @@ impl Lua {
|
||||
|
||||
/// Removes a named value in the Lua registry.
|
||||
///
|
||||
/// Equivalent to calling [`Lua::set_named_registry_value`] with a value of Nil.
|
||||
pub fn unset_named_registry_value(&self, name: &str) -> Result<()> {
|
||||
self.set_named_registry_value(name, Nil)
|
||||
/// Equivalent to calling [`Lua::set_named_registry_value`] with a value of [`Nil`].
|
||||
#[inline]
|
||||
pub fn unset_named_registry_value(&self, key: &str) -> Result<()> {
|
||||
self.set_named_registry_value(key, Nil)
|
||||
}
|
||||
|
||||
/// Place a value in the Lua registry with an auto-generated key.
|
||||
///
|
||||
/// This value will be available to Rust from all `Lua` instances which share the same main
|
||||
/// This value will be available to Rust from all Lua instances which share the same main
|
||||
/// state.
|
||||
///
|
||||
/// Be warned, garbage collection of values held inside the registry is not automatic, see
|
||||
@@ -1663,7 +1683,7 @@ impl Lua {
|
||||
}
|
||||
}
|
||||
|
||||
/// Get a value from the Lua registry by its `RegistryKey`
|
||||
/// Get a value from the Lua registry by its [`RegistryKey`]
|
||||
///
|
||||
/// Any Lua instance which shares the underlying main state may call this method to get a value
|
||||
/// previously placed by [`Lua::create_registry_value`].
|
||||
@@ -1698,9 +1718,7 @@ impl Lua {
|
||||
return Err(Error::MismatchedRegistryKey);
|
||||
}
|
||||
|
||||
unsafe {
|
||||
ffi::luaL_unref(lua.state(), ffi::LUA_REGISTRYINDEX, key.take());
|
||||
}
|
||||
unsafe { ffi::luaL_unref(lua.state(), ffi::LUA_REGISTRYINDEX, key.take()) };
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1746,8 +1764,8 @@ impl Lua {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Returns true if the given [`RegistryKey`] was created by a [`Lua`] which shares the
|
||||
/// underlying main state with this [`Lua`] instance.
|
||||
/// Returns true if the given [`RegistryKey`] was created by a Lua which shares the
|
||||
/// underlying main state with this Lua instance.
|
||||
///
|
||||
/// Other than this, methods that accept a [`RegistryKey`] will return
|
||||
/// [`Error::MismatchedRegistryKey`] if passed a [`RegistryKey`] that was not created with a
|
||||
@@ -1819,14 +1837,14 @@ impl Lua {
|
||||
/// - `Err(data)` if the data object of type `T` was not inserted because the container is
|
||||
/// currently borrowed.
|
||||
///
|
||||
/// See [`Lua::set_app_data()`] for examples.
|
||||
/// See [`Lua::set_app_data`] for examples.
|
||||
pub fn try_set_app_data<T: MaybeSend + 'static>(&self, data: T) -> StdResult<Option<T>, T> {
|
||||
let lua = self.lock();
|
||||
let extra = unsafe { &*lua.extra.get() };
|
||||
extra.app_data.try_insert(data)
|
||||
}
|
||||
|
||||
/// Gets a reference to an application data object stored by [`Lua::set_app_data()`] of type
|
||||
/// Gets a reference to an application data object stored by [`Lua::set_app_data`] of type
|
||||
/// `T`.
|
||||
///
|
||||
/// # Panics
|
||||
@@ -1840,7 +1858,15 @@ impl Lua {
|
||||
extra.app_data.borrow(Some(guard))
|
||||
}
|
||||
|
||||
/// Gets a mutable reference to an application data object stored by [`Lua::set_app_data()`] of
|
||||
/// Tries to get a reference to an application data object stored by [`Lua::set_app_data`] of
|
||||
/// type `T`.
|
||||
pub fn try_app_data_ref<T: 'static>(&self) -> StdResult<Option<AppDataRef<T>>, BorrowError> {
|
||||
let guard = self.lock_arc();
|
||||
let extra = unsafe { &*guard.extra.get() };
|
||||
extra.app_data.try_borrow(Some(guard))
|
||||
}
|
||||
|
||||
/// Gets a mutable reference to an application data object stored by [`Lua::set_app_data`] of
|
||||
/// type `T`.
|
||||
///
|
||||
/// # Panics
|
||||
@@ -1853,6 +1879,14 @@ impl Lua {
|
||||
extra.app_data.borrow_mut(Some(guard))
|
||||
}
|
||||
|
||||
/// Tries to get a mutable reference to an application data object stored by
|
||||
/// [`Lua::set_app_data`] of type `T`.
|
||||
pub fn try_app_data_mut<T: 'static>(&self) -> StdResult<Option<AppDataRefMut<T>>, BorrowMutError> {
|
||||
let guard = self.lock_arc();
|
||||
let extra = unsafe { &*guard.extra.get() };
|
||||
extra.app_data.try_borrow_mut(Some(guard))
|
||||
}
|
||||
|
||||
/// Removes an application data of type `T`.
|
||||
///
|
||||
/// # Panics
|
||||
|
||||
+53
-47
@@ -1,11 +1,12 @@
|
||||
use std::any::TypeId;
|
||||
use std::cell::{Cell, UnsafeCell};
|
||||
use std::ffi::{CStr, CString};
|
||||
use std::mem;
|
||||
use std::os::raw::{c_char, c_int, c_void};
|
||||
use std::panic::resume_unwind;
|
||||
use std::ptr::{self, NonNull};
|
||||
use std::result::Result as StdResult;
|
||||
use std::sync::Arc;
|
||||
use std::{mem, ptr};
|
||||
|
||||
use crate::chunk::ChunkMode;
|
||||
use crate::error::{Error, Result};
|
||||
@@ -24,9 +25,9 @@ use crate::types::{
|
||||
use crate::userdata::{AnyUserData, MetaMethod, UserData, UserDataRegistry, UserDataStorage};
|
||||
use crate::util::{
|
||||
assert_stack, check_stack, get_destructed_userdata_metatable, get_internal_userdata, get_main_state,
|
||||
get_userdata, init_error_registry, init_internal_metatable, init_userdata_metatable, pop_error,
|
||||
push_internal_userdata, push_string, push_table, rawset_field, safe_pcall, safe_xpcall, short_type_name,
|
||||
StackGuard, WrappedFailure,
|
||||
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,
|
||||
};
|
||||
use crate::value::{Nil, Value};
|
||||
|
||||
@@ -41,7 +42,6 @@ use {
|
||||
crate::multi::MultiValue,
|
||||
crate::traits::FromLuaMulti,
|
||||
crate::types::{AsyncCallback, AsyncCallbackUpvalue, AsyncPollUpvalue},
|
||||
std::ptr::NonNull,
|
||||
std::task::{Context, Poll, Waker},
|
||||
};
|
||||
|
||||
@@ -50,7 +50,7 @@ use {
|
||||
pub struct RawLua {
|
||||
// The state is dynamic and depends on context
|
||||
pub(super) state: Cell<*mut ffi::lua_State>,
|
||||
pub(super) main_state: *mut ffi::lua_State,
|
||||
pub(super) main_state: Option<NonNull<ffi::lua_State>>,
|
||||
pub(super) extra: XRc<UnsafeCell<ExtraData>>,
|
||||
}
|
||||
|
||||
@@ -61,9 +61,9 @@ impl Drop for RawLua {
|
||||
return;
|
||||
}
|
||||
|
||||
let mem_state = MemoryState::get(self.main_state);
|
||||
let mem_state = MemoryState::get(self.main_state());
|
||||
|
||||
ffi::lua_close(self.main_state);
|
||||
ffi::lua_close(self.main_state());
|
||||
|
||||
// Deallocate `MemoryState`
|
||||
if !mem_state.is_null() {
|
||||
@@ -95,10 +95,11 @@ impl RawLua {
|
||||
self.state.get()
|
||||
}
|
||||
|
||||
#[cfg(feature = "luau")]
|
||||
#[inline(always)]
|
||||
pub(crate) fn main_state(&self) -> *mut ffi::lua_State {
|
||||
self.main_state
|
||||
.map(|state| state.as_ptr())
|
||||
.unwrap_or_else(|| self.state())
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
@@ -129,7 +130,7 @@ impl RawLua {
|
||||
let extra = rawlua.lock().extra.get();
|
||||
|
||||
mlua_expect!(
|
||||
load_from_std_lib(state, libs),
|
||||
load_std_libs(state, libs),
|
||||
"Error during loading standard libraries"
|
||||
);
|
||||
(*extra).libs |= libs;
|
||||
@@ -221,7 +222,8 @@ impl RawLua {
|
||||
#[allow(clippy::arc_with_non_send_sync)]
|
||||
let rawlua = XRc::new(ReentrantMutex::new(RawLua {
|
||||
state: Cell::new(state),
|
||||
main_state,
|
||||
// Make sure that we don't store current state as main state (if it's not available)
|
||||
main_state: get_main_state(state).and_then(NonNull::new),
|
||||
extra: XRc::clone(&extra),
|
||||
}));
|
||||
(*extra.get()).set_lua(&rawlua);
|
||||
@@ -238,8 +240,8 @@ impl RawLua {
|
||||
|
||||
/// Marks the Lua state as safe.
|
||||
#[inline(always)]
|
||||
pub(super) unsafe fn set_safe(&self) {
|
||||
(*self.extra.get()).safe = true;
|
||||
pub(super) fn mark_safe(&self) {
|
||||
unsafe { (*self.extra.get()).safe = true };
|
||||
}
|
||||
|
||||
/// Loads the specified subset of the standard libraries into an existing Lua state.
|
||||
@@ -263,7 +265,7 @@ impl RawLua {
|
||||
));
|
||||
}
|
||||
|
||||
let res = load_from_std_lib(self.main_state, libs);
|
||||
let res = load_std_libs(self.main_state(), libs);
|
||||
|
||||
// If `package` library loaded into a safe lua state then disable C modules
|
||||
let curr_libs = (*self.extra.get()).libs;
|
||||
@@ -613,9 +615,9 @@ impl RawLua {
|
||||
let v = ffi::lua_tovector(state, idx);
|
||||
mlua_debug_assert!(!v.is_null(), "vector is null");
|
||||
#[cfg(not(feature = "luau-vector4"))]
|
||||
return Value::Vector(crate::types::Vector([*v, *v.add(1), *v.add(2)]));
|
||||
return Value::Vector(crate::Vector([*v, *v.add(1), *v.add(2)]));
|
||||
#[cfg(feature = "luau-vector4")]
|
||||
return Value::Vector(crate::types::Vector([*v, *v.add(1), *v.add(2), *v.add(3)]));
|
||||
return Value::Vector(crate::Vector([*v, *v.add(1), *v.add(2), *v.add(3)]));
|
||||
}
|
||||
|
||||
ffi::LUA_TSTRING => {
|
||||
@@ -734,7 +736,7 @@ impl RawLua {
|
||||
}
|
||||
|
||||
// MemoryInfo is empty in module mode so we cannot predict memory limits
|
||||
match MemoryState::get(self.main_state) {
|
||||
match MemoryState::get(self.state()) {
|
||||
mem_state if !mem_state.is_null() => (*mem_state).memory_limit() == 0,
|
||||
_ => (*self.extra.get()).skip_memory_check, // Check the special flag (only for module mode)
|
||||
}
|
||||
@@ -832,7 +834,7 @@ impl RawLua {
|
||||
|
||||
pub(crate) unsafe fn push_userdata_metatable<T>(&self, mut registry: UserDataRegistry<T>) -> Result<()> {
|
||||
let state = self.state();
|
||||
let _sg = StackGuard::with_top(state, ffi::lua_gettop(state) + 1);
|
||||
let mut stack_guard = StackGuard::new(state);
|
||||
check_stack(state, 13)?;
|
||||
|
||||
// Prepare metatable, add meta methods first and then meta fields
|
||||
@@ -863,8 +865,6 @@ impl RawLua {
|
||||
}
|
||||
let metatable_index = ffi::lua_absindex(state, -1);
|
||||
|
||||
let mut extra_tables_count = 0;
|
||||
|
||||
let fields_nrec = registry.fields.len();
|
||||
if fields_nrec > 0 {
|
||||
// If `__index` is a table then update it in-place
|
||||
@@ -909,7 +909,6 @@ impl RawLua {
|
||||
rawset_field(state, -2, &k)?;
|
||||
}
|
||||
field_getters_index = Some(ffi::lua_absindex(state, -1));
|
||||
extra_tables_count += 1;
|
||||
}
|
||||
|
||||
let mut field_setters_index = None;
|
||||
@@ -921,7 +920,6 @@ impl RawLua {
|
||||
rawset_field(state, -2, &k)?;
|
||||
}
|
||||
field_setters_index = Some(ffi::lua_absindex(state, -1));
|
||||
extra_tables_count += 1;
|
||||
}
|
||||
|
||||
let mut methods_index = None;
|
||||
@@ -958,18 +956,23 @@ impl RawLua {
|
||||
}
|
||||
_ => {
|
||||
methods_index = Some(ffi::lua_absindex(state, -1));
|
||||
extra_tables_count += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "luau")]
|
||||
let extra_init = None;
|
||||
#[cfg(not(feature = "luau"))]
|
||||
let extra_init: Option<fn(*mut ffi::lua_State) -> Result<()>> = Some(|state| {
|
||||
ffi::lua_pushcfunction(state, crate::util::userdata_destructor::<UserDataStorage<T>>);
|
||||
rawset_field(state, -2, "__gc")
|
||||
});
|
||||
unsafe extern "C-unwind" fn userdata_destructor<T>(state: *mut ffi::lua_State) -> c_int {
|
||||
let ud = get_userdata::<UserDataStorage<T>>(state, -1);
|
||||
if !(*ud).is_borrowed() {
|
||||
take_userdata::<UserDataStorage<T>>(state);
|
||||
ffi::lua_pushboolean(state, 1);
|
||||
} else {
|
||||
ffi::lua_pushboolean(state, 0);
|
||||
}
|
||||
1
|
||||
}
|
||||
|
||||
ffi::lua_pushcfunction(state, userdata_destructor::<T>);
|
||||
rawset_field(state, metatable_index, "__gc")?;
|
||||
|
||||
init_userdata_metatable(
|
||||
state,
|
||||
@@ -977,11 +980,10 @@ impl RawLua {
|
||||
field_getters_index,
|
||||
field_setters_index,
|
||||
methods_index,
|
||||
extra_init,
|
||||
)?;
|
||||
|
||||
// Pop extra tables to get metatable on top of the stack
|
||||
ffi::lua_pop(state, extra_tables_count);
|
||||
// Update stack guard to keep metatable after return
|
||||
stack_guard.keep(1);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -1026,11 +1028,10 @@ impl RawLua {
|
||||
state: *mut ffi::lua_State,
|
||||
idx: c_int,
|
||||
) -> Result<Option<TypeId>> {
|
||||
if ffi::lua_getmetatable(state, idx) == 0 {
|
||||
let mt_ptr = get_metatable_ptr(state, idx);
|
||||
if mt_ptr.is_null() {
|
||||
return Err(Error::UserDataTypeMismatch);
|
||||
}
|
||||
let mt_ptr = ffi::lua_topointer(state, -1);
|
||||
ffi::lua_pop(state, 1);
|
||||
|
||||
// Fast path to skip looking up the metatable in the map
|
||||
let (last_mt, last_type_id) = (*self.extra.get()).last_checked_userdata_mt;
|
||||
@@ -1101,7 +1102,7 @@ impl RawLua {
|
||||
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52", feature = "luau"))]
|
||||
unsafe {
|
||||
if !(*self.extra.get()).libs.contains(StdLib::COROUTINE) {
|
||||
load_from_std_lib(self.main_state, StdLib::COROUTINE)?;
|
||||
load_std_libs(self.main_state(), StdLib::COROUTINE)?;
|
||||
(*self.extra.get()).libs |= StdLib::COROUTINE;
|
||||
}
|
||||
}
|
||||
@@ -1250,7 +1251,7 @@ impl RawLua {
|
||||
}
|
||||
|
||||
// Uses 3 stack spaces
|
||||
unsafe fn load_from_std_lib(state: *mut ffi::lua_State, libs: StdLib) -> Result<()> {
|
||||
unsafe fn load_std_libs(state: *mut ffi::lua_State, libs: StdLib) -> Result<()> {
|
||||
#[inline(always)]
|
||||
pub unsafe fn requiref(
|
||||
state: *mut ffi::lua_State,
|
||||
@@ -1346,6 +1347,12 @@ unsafe fn load_from_std_lib(state: *mut ffi::lua_State, libs: StdLib) -> Result<
|
||||
ffi::lua_pop(state, 1);
|
||||
}
|
||||
|
||||
#[cfg(feature = "luau")]
|
||||
if libs.contains(StdLib::VECTOR) {
|
||||
requiref(state, ffi::LUA_VECLIBNAME, ffi::luaopen_vector, 1)?;
|
||||
ffi::lua_pop(state, 1);
|
||||
}
|
||||
|
||||
if libs.contains(StdLib::MATH) {
|
||||
requiref(state, ffi::LUA_MATHLIBNAME, ffi::luaopen_math, 1)?;
|
||||
ffi::lua_pop(state, 1);
|
||||
@@ -1368,16 +1375,15 @@ unsafe fn load_from_std_lib(state: *mut ffi::lua_State, libs: StdLib) -> Result<
|
||||
}
|
||||
|
||||
#[cfg(feature = "luajit")]
|
||||
{
|
||||
if libs.contains(StdLib::JIT) {
|
||||
requiref(state, ffi::LUA_JITLIBNAME, ffi::luaopen_jit, 1)?;
|
||||
ffi::lua_pop(state, 1);
|
||||
}
|
||||
if libs.contains(StdLib::JIT) {
|
||||
requiref(state, ffi::LUA_JITLIBNAME, ffi::luaopen_jit, 1)?;
|
||||
ffi::lua_pop(state, 1);
|
||||
}
|
||||
|
||||
if libs.contains(StdLib::FFI) {
|
||||
requiref(state, ffi::LUA_FFILIBNAME, ffi::luaopen_ffi, 1)?;
|
||||
ffi::lua_pop(state, 1);
|
||||
}
|
||||
#[cfg(feature = "luajit")]
|
||||
if libs.contains(StdLib::FFI) {
|
||||
requiref(state, ffi::LUA_FFILIBNAME, ffi::luaopen_ffi, 1)?;
|
||||
ffi::lua_pop(state, 1);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
||||
+1
-1
@@ -18,7 +18,7 @@ impl<'a> StateGuard<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Drop for StateGuard<'a> {
|
||||
impl Drop for StateGuard<'_> {
|
||||
fn drop(&mut self) {
|
||||
self.0.state.set(self.1);
|
||||
}
|
||||
|
||||
+7
-2
@@ -43,17 +43,22 @@ impl StdLib {
|
||||
/// [`package`](https://www.lua.org/manual/5.4/manual.html#6.3) library
|
||||
pub const PACKAGE: StdLib = StdLib(1 << 8);
|
||||
|
||||
/// [`buffer`](https://luau-lang.org/library#buffer-library) library
|
||||
/// [`buffer`](https://luau.org/library#buffer-library) library
|
||||
#[cfg(any(feature = "luau", doc))]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
|
||||
pub const BUFFER: StdLib = StdLib(1 << 9);
|
||||
|
||||
/// [`vector`](https://luau.org/library#vector-library) library
|
||||
#[cfg(any(feature = "luau", doc))]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
|
||||
pub const VECTOR: StdLib = StdLib(1 << 10);
|
||||
|
||||
/// [`jit`](http://luajit.org/ext_jit.html) library
|
||||
///
|
||||
/// Requires `feature = "luajit"`
|
||||
#[cfg(any(feature = "luajit", doc))]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "luajit")))]
|
||||
pub const JIT: StdLib = StdLib(1 << 9);
|
||||
pub const JIT: StdLib = StdLib(1 << 11);
|
||||
|
||||
/// (**unsafe**) [`ffi`](http://luajit.org/ext_ffi.html) library
|
||||
///
|
||||
|
||||
+37
-5
@@ -5,16 +5,16 @@ use std::os::raw::{c_int, c_void};
|
||||
use std::string::String as StdString;
|
||||
use std::{cmp, fmt, slice, str};
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
use crate::state::Lua;
|
||||
use crate::types::{LuaType, ValueRef};
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
use {
|
||||
serde::ser::{Serialize, Serializer},
|
||||
std::result::Result as StdResult,
|
||||
};
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
use crate::state::Lua;
|
||||
use crate::types::{LuaType, ValueRef};
|
||||
|
||||
/// Handle to an internal Lua string.
|
||||
///
|
||||
/// Unlike Rust strings, Lua strings may not be valid UTF-8.
|
||||
@@ -55,7 +55,11 @@ impl String {
|
||||
///
|
||||
/// Any non-Unicode sequences are replaced with [`U+FFFD REPLACEMENT CHARACTER`][U+FFFD].
|
||||
///
|
||||
/// This method returns [`StdString`] instead of [`Cow<'_, str>`] because lifetime cannot be
|
||||
/// bound to a weak Lua object.
|
||||
///
|
||||
/// [U+FFFD]: std::char::REPLACEMENT_CHARACTER
|
||||
/// [`Cow<'_, str>`]: std::borrow::Cow
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
@@ -74,6 +78,16 @@ impl String {
|
||||
StdString::from_utf8_lossy(&self.as_bytes()).into_owned()
|
||||
}
|
||||
|
||||
/// Returns an object that implements [`Display`] for safely printing a Lua [`String`] that may
|
||||
/// contain non-Unicode data.
|
||||
///
|
||||
/// This may perform lossy conversion.
|
||||
///
|
||||
/// [`Display`]: fmt::Display
|
||||
pub fn display(&self) -> impl fmt::Display + '_ {
|
||||
Display(self)
|
||||
}
|
||||
|
||||
/// Get the bytes that make up this string.
|
||||
///
|
||||
/// The returned slice will not contain the terminating nul byte, but will contain any nul
|
||||
@@ -148,7 +162,7 @@ impl fmt::Debug for String {
|
||||
}
|
||||
}
|
||||
|
||||
// Lua strings are basically &[u8] slices, so implement PartialEq for anything resembling that.
|
||||
// Lua strings are basically `&[u8]` slices, so implement `PartialEq` for anything resembling that.
|
||||
//
|
||||
// This makes our `String` comparable with `Vec<u8>`, `[u8]`, `&str` and `String`.
|
||||
//
|
||||
@@ -172,6 +186,15 @@ impl PartialEq for String {
|
||||
|
||||
impl Eq for String {}
|
||||
|
||||
impl<T> PartialOrd<T> for String
|
||||
where
|
||||
T: AsRef<[u8]> + ?Sized,
|
||||
{
|
||||
fn partial_cmp(&self, other: &T) -> Option<cmp::Ordering> {
|
||||
self.as_bytes().partial_cmp(&other.as_ref())
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd for String {
|
||||
fn partial_cmp(&self, other: &String) -> Option<cmp::Ordering> {
|
||||
Some(self.cmp(other))
|
||||
@@ -203,6 +226,15 @@ impl Serialize for String {
|
||||
}
|
||||
}
|
||||
|
||||
struct Display<'a>(&'a String);
|
||||
|
||||
impl fmt::Display for Display<'_> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
let bytes = self.0.as_bytes();
|
||||
<bstr::BStr as fmt::Display>::fmt(bstr::BStr::new(&bytes), f)
|
||||
}
|
||||
}
|
||||
|
||||
/// A borrowed string (`&str`) that holds a strong reference to the Lua state.
|
||||
pub struct BorrowedStr<'a>(&'a str, #[allow(unused)] Lua);
|
||||
|
||||
|
||||
+85
-76
@@ -4,6 +4,17 @@ use std::marker::PhantomData;
|
||||
use std::os::raw::{c_int, c_void};
|
||||
use std::string::String as StdString;
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
use crate::function::Function;
|
||||
use crate::state::{LuaGuard, RawLua};
|
||||
use crate::traits::{FromLua, FromLuaMulti, IntoLua, IntoLuaMulti, ObjectLike};
|
||||
use crate::types::{Integer, LuaType, ValueRef};
|
||||
use crate::util::{assert_stack, check_stack, get_metatable_ptr, StackGuard};
|
||||
use crate::value::{Nil, Value};
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
use futures_util::future::{self, Either, Future};
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
use {
|
||||
rustc_hash::FxHashSet,
|
||||
@@ -11,17 +22,6 @@ use {
|
||||
std::{cell::RefCell, rc::Rc, result::Result as StdResult},
|
||||
};
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
use crate::function::Function;
|
||||
use crate::state::{LuaGuard, RawLua};
|
||||
use crate::traits::{FromLua, FromLuaMulti, IntoLua, IntoLuaMulti, ObjectLike};
|
||||
use crate::types::{Integer, LuaType, ValueRef};
|
||||
use crate::util::{assert_stack, check_stack, StackGuard};
|
||||
use crate::value::{Nil, Value};
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
use futures_util::future::{self, Either, Future};
|
||||
|
||||
/// Handle to an internal Lua table.
|
||||
#[derive(Clone, PartialEq)]
|
||||
pub struct Table(pub(crate) ValueRef);
|
||||
@@ -59,7 +59,7 @@ impl Table {
|
||||
/// # }
|
||||
/// ```
|
||||
///
|
||||
/// [`raw_set`]: #method.raw_set
|
||||
/// [`raw_set`]: Table::raw_set
|
||||
pub fn set(&self, key: impl IntoLua, value: impl IntoLua) -> Result<()> {
|
||||
// Fast track (skip protected call)
|
||||
if !self.has_metatable() {
|
||||
@@ -106,7 +106,7 @@ impl Table {
|
||||
/// # }
|
||||
/// ```
|
||||
///
|
||||
/// [`raw_get`]: #method.raw_get
|
||||
/// [`raw_get`]: Table::raw_get
|
||||
pub fn get<V: FromLua>(&self, key: impl IntoLua) -> Result<V> {
|
||||
// Fast track (skip protected call)
|
||||
if !self.has_metatable() {
|
||||
@@ -218,13 +218,12 @@ impl Table {
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn equals<T: AsRef<Self>>(&self, other: T) -> Result<bool> {
|
||||
let other = other.as_ref();
|
||||
pub fn equals(&self, other: &Self) -> Result<bool> {
|
||||
if self == other {
|
||||
return Ok(true);
|
||||
}
|
||||
|
||||
// Compare using __eq metamethod if exists
|
||||
// Compare using `__eq` metamethod if exists
|
||||
// First, check the self for the metamethod.
|
||||
// If self does not define it, then check the other table.
|
||||
if let Some(mt) = self.metatable() {
|
||||
@@ -243,12 +242,12 @@ impl Table {
|
||||
|
||||
/// Sets a key-value pair without invoking metamethods.
|
||||
pub fn raw_set(&self, key: impl IntoLua, value: impl IntoLua) -> Result<()> {
|
||||
#[cfg(feature = "luau")]
|
||||
self.check_readonly_write()?;
|
||||
|
||||
let lua = self.0.lua.lock();
|
||||
let state = lua.state();
|
||||
unsafe {
|
||||
#[cfg(feature = "luau")]
|
||||
self.check_readonly_write(&lua)?;
|
||||
|
||||
let _sg = StackGuard::new(state);
|
||||
check_stack(state, 5)?;
|
||||
|
||||
@@ -283,7 +282,9 @@ impl Table {
|
||||
}
|
||||
|
||||
/// Inserts element value at position `idx` to the table, shifting up the elements from
|
||||
/// `table[idx]`. The worst case complexity is O(n), where n is the table length.
|
||||
/// `table[idx]`.
|
||||
///
|
||||
/// The worst case complexity is O(n), where n is the table length.
|
||||
pub fn raw_insert(&self, idx: Integer, value: impl IntoLua) -> Result<()> {
|
||||
let size = self.raw_len() as Integer;
|
||||
if idx < 1 || idx > size + 1 {
|
||||
@@ -311,12 +312,12 @@ impl Table {
|
||||
|
||||
/// Appends a value to the back of the table without invoking metamethods.
|
||||
pub fn raw_push(&self, value: impl IntoLua) -> Result<()> {
|
||||
#[cfg(feature = "luau")]
|
||||
self.check_readonly_write()?;
|
||||
|
||||
let lua = self.0.lua.lock();
|
||||
let state = lua.state();
|
||||
unsafe {
|
||||
#[cfg(feature = "luau")]
|
||||
self.check_readonly_write(&lua)?;
|
||||
|
||||
let _sg = StackGuard::new(state);
|
||||
check_stack(state, 4)?;
|
||||
|
||||
@@ -339,12 +340,12 @@ impl Table {
|
||||
|
||||
/// Removes the last element from the table and returns it, without invoking metamethods.
|
||||
pub fn raw_pop<V: FromLua>(&self) -> Result<V> {
|
||||
#[cfg(feature = "luau")]
|
||||
self.check_readonly_write()?;
|
||||
|
||||
let lua = self.0.lua.lock();
|
||||
let state = lua.state();
|
||||
unsafe {
|
||||
#[cfg(feature = "luau")]
|
||||
self.check_readonly_write(&lua)?;
|
||||
|
||||
let _sg = StackGuard::new(state);
|
||||
check_stack(state, 3)?;
|
||||
|
||||
@@ -362,8 +363,8 @@ impl Table {
|
||||
/// Removes a key from the table.
|
||||
///
|
||||
/// If `key` is an integer, mlua shifts down the elements from `table[key+1]`,
|
||||
/// and erases element `table[key]`. The complexity is O(n) in the worst case,
|
||||
/// where n is the table length.
|
||||
/// and erases element `table[key]`. The complexity is `O(n)` in the worst case,
|
||||
/// where `n` is the table length.
|
||||
///
|
||||
/// For other key types this is equivalent to setting `table[key] = nil`.
|
||||
pub fn raw_remove(&self, key: impl IntoLua) -> Result<()> {
|
||||
@@ -400,13 +401,13 @@ impl Table {
|
||||
///
|
||||
/// This method is useful to clear the table while keeping its capacity.
|
||||
pub fn clear(&self) -> Result<()> {
|
||||
#[cfg(feature = "luau")]
|
||||
self.check_readonly_write()?;
|
||||
|
||||
let lua = self.0.lua.lock();
|
||||
unsafe {
|
||||
#[cfg(feature = "luau")]
|
||||
ffi::lua_cleartable(lua.ref_thread(), self.0.index);
|
||||
{
|
||||
self.check_readonly_write(&lua)?;
|
||||
ffi::lua_cleartable(lua.ref_thread(), self.0.index);
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "luau"))]
|
||||
{
|
||||
@@ -438,9 +439,8 @@ impl Table {
|
||||
|
||||
/// Returns the result of the Lua `#` operator.
|
||||
///
|
||||
/// This might invoke the `__len` metamethod. Use the [`raw_len`] method if that is not desired.
|
||||
///
|
||||
/// [`raw_len`]: #method.raw_len
|
||||
/// This might invoke the `__len` metamethod. Use the [`Table::raw_len`] method if that is not
|
||||
/// desired.
|
||||
pub fn len(&self) -> Result<Integer> {
|
||||
// Fast track (skip protected call)
|
||||
if !self.has_metatable() {
|
||||
@@ -492,7 +492,9 @@ impl Table {
|
||||
|
||||
/// Returns a reference to the metatable of this table, or `None` if no metatable is set.
|
||||
///
|
||||
/// Unlike the `getmetatable` Lua function, this method ignores the `__metatable` field.
|
||||
/// Unlike the [`getmetatable`] Lua function, this method ignores the `__metatable` field.
|
||||
///
|
||||
/// [`getmetatable`]: https://www.lua.org/manual/5.4/manual.html#pdf-getmetatable
|
||||
pub fn metatable(&self) -> Option<Table> {
|
||||
let lua = self.0.lua.lock();
|
||||
let state = lua.state();
|
||||
@@ -511,6 +513,7 @@ impl Table {
|
||||
|
||||
#[doc(hidden)]
|
||||
#[deprecated(since = "0.10.0", note = "please use `metatable` instead")]
|
||||
#[cfg(not(tarpaulin_include))]
|
||||
pub fn get_metatable(&self) -> Option<Table> {
|
||||
self.metatable()
|
||||
}
|
||||
@@ -547,14 +550,7 @@ impl Table {
|
||||
#[inline]
|
||||
pub fn has_metatable(&self) -> bool {
|
||||
let lua = self.0.lua.lock();
|
||||
let ref_thread = lua.ref_thread();
|
||||
unsafe {
|
||||
if ffi::lua_getmetatable(ref_thread, self.0.index) != 0 {
|
||||
ffi::lua_pop(ref_thread, 1);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
false
|
||||
unsafe { !get_metatable_ptr(lua.ref_thread(), self.0.index).is_null() }
|
||||
}
|
||||
|
||||
/// Sets `readonly` attribute on the table.
|
||||
@@ -621,7 +617,6 @@ impl Table {
|
||||
/// # }
|
||||
/// ```
|
||||
///
|
||||
/// [`Result`]: crate::Result
|
||||
/// [Lua manual]: http://www.lua.org/manual/5.4/manual.html#pdf-next
|
||||
pub fn pairs<K: FromLua, V: FromLua>(&self) -> TablePairs<K, V> {
|
||||
TablePairs {
|
||||
@@ -688,10 +683,6 @@ impl Table {
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
///
|
||||
/// [`pairs`]: #method.pairs
|
||||
/// [`Result`]: crate::Result
|
||||
/// [Lua manual]: http://www.lua.org/manual/5.4/manual.html#pdf-next
|
||||
pub fn sequence_values<V: FromLua>(&self) -> TableSequence<V> {
|
||||
TableSequence {
|
||||
guard: self.0.lua.lock(),
|
||||
@@ -701,8 +692,9 @@ impl Table {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
pub(crate) fn for_each_value<V>(&self, mut f: impl FnMut(V) -> Result<()>) -> Result<()>
|
||||
/// Iterates over the sequence part of the table, invoking the given closure on each value.
|
||||
#[doc(hidden)]
|
||||
pub fn for_each_value<V>(&self, mut f: impl FnMut(V) -> Result<()>) -> Result<()>
|
||||
where
|
||||
V: FromLua,
|
||||
{
|
||||
@@ -726,12 +718,12 @@ impl Table {
|
||||
/// Sets element value at position `idx` without invoking metamethods.
|
||||
#[doc(hidden)]
|
||||
pub fn raw_seti(&self, idx: usize, value: impl IntoLua) -> Result<()> {
|
||||
#[cfg(feature = "luau")]
|
||||
self.check_readonly_write()?;
|
||||
|
||||
let lua = self.0.lua.lock();
|
||||
let state = lua.state();
|
||||
unsafe {
|
||||
#[cfg(feature = "luau")]
|
||||
self.check_readonly_write(&lua)?;
|
||||
|
||||
let _sg = StackGuard::new(state);
|
||||
check_stack(state, 5)?;
|
||||
|
||||
@@ -767,8 +759,8 @@ impl Table {
|
||||
|
||||
#[cfg(feature = "luau")]
|
||||
#[inline(always)]
|
||||
pub(crate) fn check_readonly_write(&self) -> Result<()> {
|
||||
if self.is_readonly() {
|
||||
fn check_readonly_write(&self, lua: &RawLua) -> Result<()> {
|
||||
if unsafe { ffi::lua_getreadonly(lua.ref_thread(), self.0.index) != 0 } {
|
||||
return Err(Error::runtime("attempt to modify a readonly table"));
|
||||
}
|
||||
Ok(())
|
||||
@@ -786,16 +778,40 @@ impl Table {
|
||||
let mut pairs = self.pairs::<Value, Value>().flatten().collect::<Vec<_>>();
|
||||
// Sort keys
|
||||
pairs.sort_by(|(a, _), (b, _)| a.sort_cmp(b));
|
||||
let is_sequence = (pairs.iter().enumerate())
|
||||
.all(|(i, (k, _))| matches!(k, Value::Integer(n) if *n == (i + 1) as Integer));
|
||||
if pairs.is_empty() {
|
||||
return write!(fmt, "{{}}");
|
||||
}
|
||||
writeln!(fmt, "{{")?;
|
||||
for (key, value) in pairs {
|
||||
write!(fmt, "{}[", " ".repeat(ident + 2))?;
|
||||
key.fmt_pretty(fmt, false, ident + 2, visited)?;
|
||||
write!(fmt, "] = ")?;
|
||||
value.fmt_pretty(fmt, true, ident + 2, visited)?;
|
||||
writeln!(fmt, ",")?;
|
||||
if is_sequence {
|
||||
// Format as list
|
||||
for (_, value) in pairs {
|
||||
write!(fmt, "{}", " ".repeat(ident + 2))?;
|
||||
value.fmt_pretty(fmt, true, ident + 2, visited)?;
|
||||
writeln!(fmt, ",")?;
|
||||
}
|
||||
} else {
|
||||
fn is_simple_key(key: &[u8]) -> bool {
|
||||
key.iter().take(1).all(|c| c.is_ascii_alphabetic() || *c == b'_')
|
||||
&& key.iter().all(|c| c.is_ascii_alphanumeric() || *c == b'_')
|
||||
}
|
||||
|
||||
for (key, value) in pairs {
|
||||
match key {
|
||||
Value::String(key) if is_simple_key(&key.as_bytes()) => {
|
||||
write!(fmt, "{}{}", " ".repeat(ident + 2), key.display())?;
|
||||
write!(fmt, " = ")?;
|
||||
}
|
||||
_ => {
|
||||
write!(fmt, "{}[", " ".repeat(ident + 2))?;
|
||||
key.fmt_pretty(fmt, false, ident + 2, visited)?;
|
||||
write!(fmt, "] = ")?;
|
||||
}
|
||||
}
|
||||
value.fmt_pretty(fmt, true, ident + 2, visited)?;
|
||||
writeln!(fmt, ",")?;
|
||||
}
|
||||
}
|
||||
write!(fmt, "{}}}", " ".repeat(ident))
|
||||
}
|
||||
@@ -810,13 +826,6 @@ impl fmt::Debug for Table {
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<Table> for Table {
|
||||
#[inline]
|
||||
fn as_ref(&self) -> &Self {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> PartialEq<[T]> for Table
|
||||
where
|
||||
T: IntoLua + Clone,
|
||||
@@ -867,6 +876,10 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl LuaType for Table {
|
||||
const TYPE_ID: c_int = ffi::LUA_TTABLE;
|
||||
}
|
||||
|
||||
impl ObjectLike for Table {
|
||||
#[inline]
|
||||
fn get<V: FromLua>(&self, key: impl IntoLua) -> Result<V> {
|
||||
@@ -961,10 +974,6 @@ impl Serialize for Table {
|
||||
}
|
||||
}
|
||||
|
||||
impl LuaType for Table {
|
||||
const TYPE_ID: c_int = ffi::LUA_TTABLE;
|
||||
}
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
impl<'a> SerializableTable<'a> {
|
||||
#[inline]
|
||||
@@ -982,7 +991,7 @@ impl<'a> SerializableTable<'a> {
|
||||
}
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
impl<'a> Serialize for SerializableTable<'a> {
|
||||
impl Serialize for SerializableTable<'_> {
|
||||
fn serialize<S>(&self, serializer: S) -> StdResult<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
@@ -1073,7 +1082,7 @@ pub struct TablePairs<'a, K, V> {
|
||||
_phantom: PhantomData<(K, V)>,
|
||||
}
|
||||
|
||||
impl<'a, K, V> Iterator for TablePairs<'a, K, V>
|
||||
impl<K, V> Iterator for TablePairs<'_, K, V>
|
||||
where
|
||||
K: FromLua,
|
||||
V: FromLua,
|
||||
@@ -1133,7 +1142,7 @@ pub struct TableSequence<'a, V> {
|
||||
_phantom: PhantomData<V>,
|
||||
}
|
||||
|
||||
impl<'a, V> Iterator for TableSequence<'a, V>
|
||||
impl<V> Iterator for TableSequence<'_, V>
|
||||
where
|
||||
V: FromLua,
|
||||
{
|
||||
|
||||
+28
-20
@@ -69,23 +69,23 @@ pub struct AsyncThread<A, R> {
|
||||
|
||||
impl Thread {
|
||||
#[inline(always)]
|
||||
const fn state(&self) -> *mut ffi::lua_State {
|
||||
fn state(&self) -> *mut ffi::lua_State {
|
||||
self.1
|
||||
}
|
||||
|
||||
/// Resumes execution of this thread.
|
||||
///
|
||||
/// Equivalent to `coroutine.resume`.
|
||||
/// Equivalent to [`coroutine.resume`].
|
||||
///
|
||||
/// Passes `args` as arguments to the thread. If the coroutine has called `coroutine.yield`, it
|
||||
/// will return these arguments. Otherwise, the coroutine wasn't yet started, so the arguments
|
||||
/// are passed to its main function.
|
||||
/// Passes `args` as arguments to the thread. If the coroutine has called [`coroutine.yield`],
|
||||
/// it will return these arguments. Otherwise, the coroutine wasn't yet started, so the
|
||||
/// arguments are passed to its main function.
|
||||
///
|
||||
/// If the thread is no longer in `Active` state (meaning it has finished execution or
|
||||
/// encountered an error), this will return `Err(CoroutineInactive)`, otherwise will return `Ok`
|
||||
/// as follows:
|
||||
/// If the thread is no longer resumable (meaning it has finished execution or encountered an
|
||||
/// error), this will return [`Error::CoroutineUnresumable`], otherwise will return `Ok` as
|
||||
/// follows:
|
||||
///
|
||||
/// If the thread calls `coroutine.yield`, returns the values passed to `yield`. If the thread
|
||||
/// If the thread calls [`coroutine.yield`], returns the values passed to `yield`. If the thread
|
||||
/// `return`s values from its main function, returns those.
|
||||
///
|
||||
/// # Examples
|
||||
@@ -114,6 +114,9 @@ impl Thread {
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
///
|
||||
/// [`coroutine.resume`]: https://www.lua.org/manual/5.4/manual.html#pdf-coroutine.resume
|
||||
/// [`coroutine.yield`]: https://www.lua.org/manual/5.4/manual.html#pdf-coroutine.yield
|
||||
pub fn resume<R>(&self, args: impl IntoLuaMulti) -> Result<R>
|
||||
where
|
||||
R: FromLuaMulti,
|
||||
@@ -187,10 +190,10 @@ impl Thread {
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets a 'hook' function that will periodically be called as Lua code executes.
|
||||
/// Sets a hook function that will periodically be called as Lua code executes.
|
||||
///
|
||||
/// This function is similar or [`Lua::set_hook()`] except that it sets for the thread.
|
||||
/// To remove a hook call [`Lua::remove_hook()`].
|
||||
/// This function is similar or [`Lua::set_hook`] except that it sets for the thread.
|
||||
/// To remove a hook call [`Lua::remove_hook`].
|
||||
#[cfg(not(feature = "luau"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(not(feature = "luau"))))]
|
||||
pub fn set_hook<F>(&self, triggers: HookTriggers, callback: F)
|
||||
@@ -252,21 +255,22 @@ impl Thread {
|
||||
}
|
||||
}
|
||||
|
||||
/// Converts Thread to an AsyncThread which implements [`Future`] and [`Stream`] traits.
|
||||
/// Converts [`Thread`] to an [`AsyncThread`] which implements [`Future`] and [`Stream`] traits.
|
||||
///
|
||||
/// `args` are passed as arguments to the thread function for first call.
|
||||
/// The object calls [`resume()`] while polling and also allows to run rust futures
|
||||
/// The object calls [`resume`] while polling and also allow to run Rust futures
|
||||
/// to completion using an executor.
|
||||
///
|
||||
/// Using AsyncThread as a Stream allows to iterate through `coroutine.yield()`
|
||||
/// values whereas Future version discards that values and poll until the final
|
||||
/// Using [`AsyncThread`] as a [`Stream`] allow to iterate through [`coroutine.yield`]
|
||||
/// values whereas [`Future`] version discards that values and poll until the final
|
||||
/// one (returned from the thread function).
|
||||
///
|
||||
/// Requires `feature = "async"`
|
||||
///
|
||||
/// [`Future`]: std::future::Future
|
||||
/// [`Stream`]: futures_util::stream::Stream
|
||||
/// [`resume()`]: https://www.lua.org/manual/5.4/manual.html#lua_resume
|
||||
/// [`resume`]: https://www.lua.org/manual/5.4/manual.html#lua_resume
|
||||
/// [`coroutine.yield`]: https://www.lua.org/manual/5.4/manual.html#pdf-coroutine.yield
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
@@ -316,7 +320,7 @@ impl Thread {
|
||||
/// Under the hood replaces the global environment table with a new table,
|
||||
/// that performs writes locally and proxies reads to caller's global environment.
|
||||
///
|
||||
/// This mode ideally should be used together with the global sandbox mode [`Lua::sandbox()`].
|
||||
/// This mode ideally should be used together with the global sandbox mode [`Lua::sandbox`].
|
||||
///
|
||||
/// Please note that Luau links environment table with chunk when loading it into Lua state.
|
||||
/// Therefore you need to load chunks into a thread to link with the thread environment.
|
||||
@@ -325,6 +329,7 @@ impl Thread {
|
||||
///
|
||||
/// ```
|
||||
/// # use mlua::{Lua, Result};
|
||||
/// # #[cfg(feature = "luau")]
|
||||
/// # fn main() -> Result<()> {
|
||||
/// let lua = Lua::new();
|
||||
/// let thread = lua.create_thread(lua.create_function(|lua2, ()| {
|
||||
@@ -339,10 +344,13 @@ impl Thread {
|
||||
/// assert_eq!(lua.globals().get::<Option<u32>>("var")?, None);
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
///
|
||||
/// # #[cfg(not(feature = "luau"))]
|
||||
/// # fn main() { }
|
||||
/// ```
|
||||
///
|
||||
/// Requires `feature = "luau"`
|
||||
#[cfg(any(feature = "luau", docsrs))]
|
||||
#[cfg(any(feature = "luau", doc))]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
|
||||
#[doc(hidden)]
|
||||
pub fn sandbox(&self) -> Result<()> {
|
||||
@@ -522,7 +530,7 @@ impl<'lua, 'a> WakerGuard<'lua, 'a> {
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
impl<'lua, 'a> Drop for WakerGuard<'lua, 'a> {
|
||||
impl Drop for WakerGuard<'_, '_> {
|
||||
fn drop(&mut self) {
|
||||
unsafe { self.lua.set_waker(self.prev) };
|
||||
}
|
||||
|
||||
+7
-10
@@ -13,7 +13,7 @@ use crate::value::Value;
|
||||
#[cfg(feature = "async")]
|
||||
use std::future::Future;
|
||||
|
||||
/// Trait for types convertible to `Value`.
|
||||
/// Trait for types convertible to [`Value`].
|
||||
pub trait IntoLua: Sized {
|
||||
/// Performs the conversion.
|
||||
fn into_lua(self, lua: &Lua) -> Result<Value>;
|
||||
@@ -29,7 +29,7 @@ pub trait IntoLua: Sized {
|
||||
}
|
||||
}
|
||||
|
||||
/// Trait for types convertible from `Value`.
|
||||
/// Trait for types convertible from [`Value`].
|
||||
pub trait FromLua: Sized {
|
||||
/// Performs the conversion.
|
||||
fn from_lua(value: Value, lua: &Lua) -> Result<Self>;
|
||||
@@ -71,8 +71,8 @@ pub trait FromLua: Sized {
|
||||
|
||||
/// Trait for types convertible to any number of Lua values.
|
||||
///
|
||||
/// This is a generalization of `IntoLua`, allowing any number of resulting Lua values instead of
|
||||
/// just one. Any type that implements `IntoLua` will automatically implement this trait.
|
||||
/// This is a generalization of [`IntoLua`], allowing any number of resulting Lua values instead of
|
||||
/// just one. Any type that implements [`IntoLua`] will automatically implement this trait.
|
||||
pub trait IntoLuaMulti: Sized {
|
||||
/// Performs the conversion.
|
||||
fn into_lua_multi(self, lua: &Lua) -> Result<MultiValue>;
|
||||
@@ -97,8 +97,9 @@ pub trait IntoLuaMulti: Sized {
|
||||
|
||||
/// Trait for types that can be created from an arbitrary number of Lua values.
|
||||
///
|
||||
/// This is a generalization of `FromLua`, allowing an arbitrary number of Lua values to participate
|
||||
/// in the conversion. Any type that implements `FromLua` will automatically implement this trait.
|
||||
/// This is a generalization of [`FromLua`], allowing an arbitrary number of Lua values to
|
||||
/// participate in the conversion. Any type that implements [`FromLua`] will automatically
|
||||
/// implement this trait.
|
||||
pub trait FromLuaMulti: Sized {
|
||||
/// Performs the conversion.
|
||||
///
|
||||
@@ -127,10 +128,6 @@ pub trait FromLuaMulti: Sized {
|
||||
for idx in 0..nvals {
|
||||
values.push_back(lua.stack_value(-nvals + idx, None));
|
||||
}
|
||||
if nvals > 0 {
|
||||
// It's safe to clear the stack as all references moved to ref thread
|
||||
ffi::lua_pop(lua.state(), nvals);
|
||||
}
|
||||
Self::from_lua_multi(values, lua.lua())
|
||||
}
|
||||
|
||||
|
||||
+1
-7
@@ -20,8 +20,6 @@ pub use app_data::{AppData, AppDataRef, AppDataRefMut};
|
||||
pub use either::Either;
|
||||
pub use registry_key::RegistryKey;
|
||||
pub(crate) use value_ref::ValueRef;
|
||||
#[cfg(any(feature = "luau", doc))]
|
||||
pub use vector::Vector;
|
||||
|
||||
/// Type of Lua integer numbers.
|
||||
pub type Integer = ffi::lua_Integer;
|
||||
@@ -66,7 +64,7 @@ pub(crate) type AsyncCallbackUpvalue = Upvalue<AsyncCallback>;
|
||||
#[cfg(feature = "async")]
|
||||
pub(crate) type AsyncPollUpvalue = Upvalue<BoxFuture<'static, Result<c_int>>>;
|
||||
|
||||
/// Type to set next Luau VM action after executing interrupt function.
|
||||
/// Type to set next Lua VM action after executing interrupt or hook function.
|
||||
pub enum VmState {
|
||||
Continue,
|
||||
/// Yield the current thread.
|
||||
@@ -123,14 +121,10 @@ impl LuaType for LightUserData {
|
||||
}
|
||||
|
||||
mod app_data;
|
||||
mod either;
|
||||
mod registry_key;
|
||||
mod sync;
|
||||
mod value_ref;
|
||||
|
||||
#[cfg(any(feature = "luau", doc))]
|
||||
mod vector;
|
||||
|
||||
#[cfg(test)]
|
||||
mod assertions {
|
||||
use super::*;
|
||||
|
||||
+54
-18
@@ -1,5 +1,5 @@
|
||||
use std::any::{Any, TypeId};
|
||||
use std::cell::{Cell, Ref, RefCell, RefMut, UnsafeCell};
|
||||
use std::cell::{BorrowError, BorrowMutError, Cell, Ref, RefCell, RefMut, UnsafeCell};
|
||||
use std::fmt;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use std::result::Result as StdResult;
|
||||
@@ -41,30 +41,66 @@ impl AppData {
|
||||
.and_then(|data| data.into_inner().downcast::<T>().ok().map(|data| *data)))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[track_caller]
|
||||
pub(crate) fn borrow<T: 'static>(&self, guard: Option<LuaGuard>) -> Option<AppDataRef<T>> {
|
||||
let data = unsafe { &*self.container.get() }
|
||||
.get(&TypeId::of::<T>())?
|
||||
.borrow();
|
||||
self.borrow.set(self.borrow.get() + 1);
|
||||
Some(AppDataRef {
|
||||
data: Ref::filter_map(data, |data| data.downcast_ref()).ok()?,
|
||||
borrow: &self.borrow,
|
||||
_guard: guard,
|
||||
})
|
||||
match self.try_borrow(guard) {
|
||||
Ok(data) => data,
|
||||
Err(err) => panic!("already mutably borrowed: {err:?}"),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn try_borrow<T: 'static>(
|
||||
&self,
|
||||
guard: Option<LuaGuard>,
|
||||
) -> Result<Option<AppDataRef<T>>, BorrowError> {
|
||||
let data = unsafe { &*self.container.get() }
|
||||
.get(&TypeId::of::<T>())
|
||||
.map(|c| c.try_borrow())
|
||||
.transpose()?
|
||||
.and_then(|data| Ref::filter_map(data, |data| data.downcast_ref()).ok());
|
||||
match data {
|
||||
Some(data) => {
|
||||
self.borrow.set(self.borrow.get() + 1);
|
||||
Ok(Some(AppDataRef {
|
||||
data,
|
||||
borrow: &self.borrow,
|
||||
_guard: guard,
|
||||
}))
|
||||
}
|
||||
None => Ok(None),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[track_caller]
|
||||
pub(crate) fn borrow_mut<T: 'static>(&self, guard: Option<LuaGuard>) -> Option<AppDataRefMut<T>> {
|
||||
match self.try_borrow_mut(guard) {
|
||||
Ok(data) => data,
|
||||
Err(err) => panic!("already borrowed: {err:?}"),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn try_borrow_mut<T: 'static>(
|
||||
&self,
|
||||
guard: Option<LuaGuard>,
|
||||
) -> Result<Option<AppDataRefMut<T>>, BorrowMutError> {
|
||||
let data = unsafe { &*self.container.get() }
|
||||
.get(&TypeId::of::<T>())?
|
||||
.borrow_mut();
|
||||
self.borrow.set(self.borrow.get() + 1);
|
||||
Some(AppDataRefMut {
|
||||
data: RefMut::filter_map(data, |data| data.downcast_mut()).ok()?,
|
||||
borrow: &self.borrow,
|
||||
_guard: guard,
|
||||
})
|
||||
.get(&TypeId::of::<T>())
|
||||
.map(|c| c.try_borrow_mut())
|
||||
.transpose()?
|
||||
.and_then(|data| RefMut::filter_map(data, |data| data.downcast_mut()).ok());
|
||||
match data {
|
||||
Some(data) => {
|
||||
self.borrow.set(self.borrow.get() + 1);
|
||||
Ok(Some(AppDataRefMut {
|
||||
data,
|
||||
borrow: &self.borrow,
|
||||
_guard: guard,
|
||||
}))
|
||||
}
|
||||
None => Ok(None),
|
||||
}
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
|
||||
@@ -1,135 +0,0 @@
|
||||
use std::ffi::CStr;
|
||||
use std::fmt;
|
||||
use std::hash::Hash;
|
||||
use std::os::raw::c_int;
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
use crate::state::{Lua, RawLua};
|
||||
use crate::traits::{FromLua, IntoLua, ShortTypeName as _};
|
||||
use crate::value::Value;
|
||||
|
||||
/// Combination of two types into a single one.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub enum Either<L, R> {
|
||||
Left(L),
|
||||
Right(R),
|
||||
}
|
||||
|
||||
impl<L, R> Either<L, R> {
|
||||
/// Return true if the value is the Left variant.
|
||||
#[inline]
|
||||
pub fn is_left(&self) -> bool {
|
||||
matches!(self, Either::Left(_))
|
||||
}
|
||||
|
||||
/// Return true if the value is the Right variant.
|
||||
#[inline]
|
||||
pub fn is_right(&self) -> bool {
|
||||
matches!(self, Either::Right(_))
|
||||
}
|
||||
|
||||
/// Convert the left side of `Either<L, R>` to an `Option<L>`.
|
||||
#[inline]
|
||||
pub fn left(self) -> Option<L> {
|
||||
match self {
|
||||
Either::Left(l) => Some(l),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert the right side of `Either<L, R>` to an `Option<R>`.
|
||||
#[inline]
|
||||
pub fn right(self) -> Option<R> {
|
||||
match self {
|
||||
Either::Right(r) => Some(r),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert `&Either<L, R>` to `Either<&L, &R>`.
|
||||
#[inline]
|
||||
pub fn as_ref(&self) -> Either<&L, &R> {
|
||||
match self {
|
||||
Either::Left(l) => Either::Left(l),
|
||||
Either::Right(r) => Either::Right(r),
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert `&mut Either<L, R>` to `Either<&mut L, &mut R>`.
|
||||
#[inline]
|
||||
pub fn as_mut(&mut self) -> Either<&mut L, &mut R> {
|
||||
match self {
|
||||
Either::Left(l) => Either::Left(l),
|
||||
Either::Right(r) => Either::Right(r),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<L, R> fmt::Display for Either<L, R>
|
||||
where
|
||||
L: fmt::Display,
|
||||
R: fmt::Display,
|
||||
{
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Either::Left(a) => a.fmt(f),
|
||||
Either::Right(b) => b.fmt(f),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<L: IntoLua, R: IntoLua> IntoLua for Either<L, R> {
|
||||
#[inline]
|
||||
fn into_lua(self, lua: &Lua) -> Result<Value> {
|
||||
match self {
|
||||
Either::Left(l) => l.into_lua(lua),
|
||||
Either::Right(r) => r.into_lua(lua),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn push_into_stack(self, lua: &RawLua) -> Result<()> {
|
||||
match self {
|
||||
Either::Left(l) => l.push_into_stack(lua),
|
||||
Either::Right(r) => r.push_into_stack(lua),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<L: FromLua, R: FromLua> FromLua for Either<L, R> {
|
||||
#[inline]
|
||||
fn from_lua(value: Value, lua: &Lua) -> Result<Self> {
|
||||
let value_type_name = value.type_name();
|
||||
// Try the left type first
|
||||
match L::from_lua(value.clone(), lua) {
|
||||
Ok(l) => Ok(Either::Left(l)),
|
||||
// Try the right type
|
||||
Err(_) => match R::from_lua(value, lua).map(Either::Right) {
|
||||
Ok(r) => Ok(r),
|
||||
Err(_) => Err(Error::FromLuaConversionError {
|
||||
from: value_type_name,
|
||||
to: Self::type_name(),
|
||||
message: None,
|
||||
}),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn from_stack(idx: c_int, lua: &RawLua) -> Result<Self> {
|
||||
match L::from_stack(idx, lua) {
|
||||
Ok(l) => Ok(Either::Left(l)),
|
||||
Err(_) => match R::from_stack(idx, lua).map(Either::Right) {
|
||||
Ok(r) => Ok(r),
|
||||
Err(_) => {
|
||||
let value_type_name = CStr::from_ptr(ffi::luaL_typename(lua.state(), idx));
|
||||
Err(Error::FromLuaConversionError {
|
||||
from: value_type_name.to_str().unwrap(),
|
||||
to: Self::type_name(),
|
||||
message: None,
|
||||
})
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -12,17 +12,16 @@ use parking_lot::Mutex;
|
||||
/// and instances not manually removed can be garbage collected with
|
||||
/// [`Lua::expire_registry_values`].
|
||||
///
|
||||
/// Be warned, If you place this into Lua via a [`UserData`] type or a rust callback, it is *very
|
||||
/// easy* to accidentally cause reference cycles that the Lua garbage collector cannot resolve.
|
||||
/// Instead of placing a [`RegistryKey`] into a [`UserData`] type, prefer instead to use
|
||||
/// [`AnyUserData::set_user_value`] / [`AnyUserData::user_value`].
|
||||
/// Be warned, If you place this into Lua via a [`UserData`] type or a Rust callback, it is *easy*
|
||||
/// to accidentally cause reference cycles that the Lua garbage collector cannot resolve. Instead of
|
||||
/// placing a [`RegistryKey`] into a [`UserData`] type, consider to use
|
||||
/// [`AnyUserData::set_user_value`].
|
||||
///
|
||||
/// [`UserData`]: crate::UserData
|
||||
/// [`RegistryKey`]: crate::RegistryKey
|
||||
/// [`Lua::remove_registry_value`]: crate::Lua::remove_registry_value
|
||||
/// [`Lua::expire_registry_values`]: crate::Lua::expire_registry_values
|
||||
/// [`AnyUserData::set_user_value`]: crate::AnyUserData::set_user_value
|
||||
/// [`AnyUserData::user_value`]: crate::AnyUserData::user_value
|
||||
pub struct RegistryKey {
|
||||
pub(crate) registry_id: i32,
|
||||
pub(crate) unref_list: Arc<Mutex<Option<Vec<c_int>>>>,
|
||||
|
||||
+1
-1
@@ -53,7 +53,7 @@ mod inner {
|
||||
|
||||
pub(crate) struct ReentrantMutexGuard<'a, T>(&'a T);
|
||||
|
||||
impl<'a, T> Deref for ReentrantMutexGuard<'a, T> {
|
||||
impl<T> Deref for ReentrantMutexGuard<'_, T> {
|
||||
type Target = T;
|
||||
|
||||
#[inline(always)]
|
||||
|
||||
+117
-151
@@ -5,6 +5,16 @@ use std::hash::Hash;
|
||||
use std::os::raw::{c_char, c_void};
|
||||
use std::string::String as StdString;
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
use crate::function::Function;
|
||||
use crate::state::Lua;
|
||||
use crate::string::String;
|
||||
use crate::table::{Table, TablePairs};
|
||||
use crate::traits::{FromLua, FromLuaMulti, IntoLua, IntoLuaMulti};
|
||||
use crate::types::{MaybeSend, ValueRef};
|
||||
use crate::util::{check_stack, get_userdata, push_string, take_userdata, StackGuard};
|
||||
use crate::value::Value;
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
use std::future::Future;
|
||||
|
||||
@@ -14,16 +24,6 @@ use {
|
||||
std::result::Result as StdResult,
|
||||
};
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
use crate::function::Function;
|
||||
use crate::state::Lua;
|
||||
use crate::string::String;
|
||||
use crate::table::{Table, TablePairs};
|
||||
use crate::traits::{FromLua, FromLuaMulti, IntoLua, IntoLuaMulti};
|
||||
use crate::types::{MaybeSend, ValueRef};
|
||||
use crate::util::{check_stack, get_userdata, take_userdata, StackGuard};
|
||||
use crate::value::Value;
|
||||
|
||||
// Re-export for convenience
|
||||
pub(crate) use cell::UserDataStorage;
|
||||
pub use cell::{UserDataRef, UserDataRefMut};
|
||||
@@ -34,8 +34,6 @@ pub use registry::UserDataRegistry;
|
||||
///
|
||||
/// Currently, this mechanism does not allow overriding the `__gc` metamethod, since there is
|
||||
/// generally no need to do so: [`UserData`] implementors can instead just implement `Drop`.
|
||||
///
|
||||
/// [`UserData`]: crate::UserData
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
#[non_exhaustive]
|
||||
pub enum MetaMethod {
|
||||
@@ -243,8 +241,6 @@ impl AsRef<str> for MetaMethod {
|
||||
}
|
||||
|
||||
/// Method registry for [`UserData`] implementors.
|
||||
///
|
||||
/// [`UserData`]: crate::UserData
|
||||
pub trait UserDataMethods<T> {
|
||||
/// Add a regular method which accepts a `&T` as the first parameter.
|
||||
///
|
||||
@@ -263,20 +259,20 @@ pub trait UserDataMethods<T> {
|
||||
///
|
||||
/// Refer to [`add_method`] for more information about the implementation.
|
||||
///
|
||||
/// [`add_method`]: #method.add_method
|
||||
/// [`add_method`]: UserDataMethods::add_method
|
||||
fn add_method_mut<M, A, R>(&mut self, name: impl ToString, method: M)
|
||||
where
|
||||
M: FnMut(&Lua, &mut T, A) -> Result<R> + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
R: IntoLuaMulti;
|
||||
|
||||
/// Add an async method which accepts a `&T` as the first parameter and returns Future.
|
||||
/// Add an async method which accepts a `&T` as the first parameter and returns [`Future`].
|
||||
///
|
||||
/// Refer to [`add_method`] for more information about the implementation.
|
||||
///
|
||||
/// Requires `feature = "async"`
|
||||
///
|
||||
/// [`add_method`]: #method.add_method
|
||||
/// [`add_method`]: UserDataMethods::add_method
|
||||
#[cfg(feature = "async")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
|
||||
fn add_async_method<M, A, MR, R>(&mut self, name: impl ToString, method: M)
|
||||
@@ -287,13 +283,13 @@ pub trait UserDataMethods<T> {
|
||||
MR: Future<Output = Result<R>> + MaybeSend + 'static,
|
||||
R: IntoLuaMulti;
|
||||
|
||||
/// Add an async method which accepts a `&mut T` as the first parameter and returns Future.
|
||||
/// Add an async method which accepts a `&mut T` as the first parameter and returns [`Future`].
|
||||
///
|
||||
/// Refer to [`add_method`] for more information about the implementation.
|
||||
///
|
||||
/// Requires `feature = "async"`
|
||||
///
|
||||
/// [`add_method`]: #method.add_method
|
||||
/// [`add_method`]: UserDataMethods::add_method
|
||||
#[cfg(feature = "async")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
|
||||
fn add_async_method_mut<M, A, MR, R>(&mut self, name: impl ToString, method: M)
|
||||
@@ -304,16 +300,11 @@ pub trait UserDataMethods<T> {
|
||||
MR: Future<Output = Result<R>> + MaybeSend + 'static,
|
||||
R: IntoLuaMulti;
|
||||
|
||||
/// Add a regular method as a function which accepts generic arguments, the first argument will
|
||||
/// be a [`AnyUserData`] of type `T` if the method is called with Lua method syntax:
|
||||
/// `my_userdata:my_method(arg1, arg2)`, or it is passed in as the first argument:
|
||||
/// `my_userdata.my_method(my_userdata, arg1, arg2)`.
|
||||
/// Add a regular method as a function which accepts generic arguments.
|
||||
///
|
||||
/// Prefer to use [`add_method`] or [`add_method_mut`] as they are easier to use.
|
||||
///
|
||||
/// [`AnyUserData`]: crate::AnyUserData
|
||||
/// [`add_method`]: #method.add_method
|
||||
/// [`add_method_mut`]: #method.add_method_mut
|
||||
/// The first argument will be a [`AnyUserData`] of type `T` if the method is called with Lua
|
||||
/// method syntax: `my_userdata:my_method(arg1, arg2)`, or it is passed in as the first
|
||||
/// argument: `my_userdata.my_method(my_userdata, arg1, arg2)`.
|
||||
fn add_function<F, A, R>(&mut self, name: impl ToString, function: F)
|
||||
where
|
||||
F: Fn(&Lua, A) -> Result<R> + MaybeSend + 'static,
|
||||
@@ -322,23 +313,23 @@ pub trait UserDataMethods<T> {
|
||||
|
||||
/// Add a regular method as a mutable function which accepts generic arguments.
|
||||
///
|
||||
/// This is a version of [`add_function`] that accepts a FnMut argument.
|
||||
/// This is a version of [`add_function`] that accepts a `FnMut` argument.
|
||||
///
|
||||
/// [`add_function`]: #method.add_function
|
||||
/// [`add_function`]: UserDataMethods::add_function
|
||||
fn add_function_mut<F, A, R>(&mut self, name: impl ToString, function: F)
|
||||
where
|
||||
F: FnMut(&Lua, A) -> Result<R> + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
R: IntoLuaMulti;
|
||||
|
||||
/// Add a regular method as an async function which accepts generic arguments
|
||||
/// and returns Future.
|
||||
/// Add a regular method as an async function which accepts generic arguments and returns
|
||||
/// [`Future`].
|
||||
///
|
||||
/// This is an async version of [`add_function`].
|
||||
///
|
||||
/// Requires `feature = "async"`
|
||||
///
|
||||
/// [`add_function`]: #method.add_function
|
||||
/// [`add_function`]: UserDataMethods::add_function
|
||||
#[cfg(feature = "async")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
|
||||
fn add_async_function<F, A, FR, R>(&mut self, name: impl ToString, function: F)
|
||||
@@ -355,7 +346,7 @@ pub trait UserDataMethods<T> {
|
||||
/// This can cause an error with certain binary metamethods that can trigger if only the right
|
||||
/// side has a metatable. To prevent this, use [`add_meta_function`].
|
||||
///
|
||||
/// [`add_meta_function`]: #method.add_meta_function
|
||||
/// [`add_meta_function`]: UserDataMethods::add_meta_function
|
||||
fn add_meta_method<M, A, R>(&mut self, name: impl ToString, method: M)
|
||||
where
|
||||
M: Fn(&Lua, &T, A) -> Result<R> + MaybeSend + 'static,
|
||||
@@ -369,20 +360,20 @@ pub trait UserDataMethods<T> {
|
||||
/// This can cause an error with certain binary metamethods that can trigger if only the right
|
||||
/// side has a metatable. To prevent this, use [`add_meta_function`].
|
||||
///
|
||||
/// [`add_meta_function`]: #method.add_meta_function
|
||||
/// [`add_meta_function`]: UserDataMethods::add_meta_function
|
||||
fn add_meta_method_mut<M, A, R>(&mut self, name: impl ToString, method: M)
|
||||
where
|
||||
M: FnMut(&Lua, &mut T, A) -> Result<R> + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
R: IntoLuaMulti;
|
||||
|
||||
/// Add an async metamethod which accepts a `&T` as the first parameter and returns Future.
|
||||
/// Add an async metamethod which accepts a `&T` as the first parameter and returns [`Future`].
|
||||
///
|
||||
/// This is an async version of [`add_meta_method`].
|
||||
///
|
||||
/// Requires `feature = "async"`
|
||||
///
|
||||
/// [`add_meta_method`]: #method.add_meta_method
|
||||
/// [`add_meta_method`]: UserDataMethods::add_meta_method
|
||||
#[cfg(all(feature = "async", not(any(feature = "lua51", feature = "luau"))))]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
|
||||
fn add_async_meta_method<M, A, MR, R>(&mut self, name: impl ToString, method: M)
|
||||
@@ -393,13 +384,14 @@ pub trait UserDataMethods<T> {
|
||||
MR: Future<Output = Result<R>> + MaybeSend + 'static,
|
||||
R: IntoLuaMulti;
|
||||
|
||||
/// Add an async metamethod which accepts a `&mut T` as the first parameter and returns Future.
|
||||
/// Add an async metamethod which accepts a `&mut T` as the first parameter and returns
|
||||
/// [`Future`].
|
||||
///
|
||||
/// This is an async version of [`add_meta_method_mut`].
|
||||
///
|
||||
/// Requires `feature = "async"`
|
||||
///
|
||||
/// [`add_meta_method_mut`]: #method.add_meta_method_mut
|
||||
/// [`add_meta_method_mut`]: UserDataMethods::add_meta_method_mut
|
||||
#[cfg(all(feature = "async", not(any(feature = "lua51", feature = "luau"))))]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
|
||||
fn add_async_meta_method_mut<M, A, MR, R>(&mut self, name: impl ToString, method: M)
|
||||
@@ -423,22 +415,22 @@ pub trait UserDataMethods<T> {
|
||||
|
||||
/// Add a metamethod as a mutable function which accepts generic arguments.
|
||||
///
|
||||
/// This is a version of [`add_meta_function`] that accepts a FnMut argument.
|
||||
/// This is a version of [`add_meta_function`] that accepts a `FnMut` argument.
|
||||
///
|
||||
/// [`add_meta_function`]: #method.add_meta_function
|
||||
/// [`add_meta_function`]: UserDataMethods::add_meta_function
|
||||
fn add_meta_function_mut<F, A, R>(&mut self, name: impl ToString, function: F)
|
||||
where
|
||||
F: FnMut(&Lua, A) -> Result<R> + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
R: IntoLuaMulti;
|
||||
|
||||
/// Add a metamethod which accepts generic arguments and returns Future.
|
||||
/// Add a metamethod which accepts generic arguments and returns [`Future`].
|
||||
///
|
||||
/// This is an async version of [`add_meta_function`].
|
||||
///
|
||||
/// Requires `feature = "async"`
|
||||
///
|
||||
/// [`add_meta_function`]: #method.add_meta_function
|
||||
/// [`add_meta_function`]: UserDataMethods::add_meta_function
|
||||
#[cfg(all(feature = "async", not(any(feature = "lua51", feature = "luau"))))]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
|
||||
fn add_async_meta_function<F, A, FR, R>(&mut self, name: impl ToString, function: F)
|
||||
@@ -450,15 +442,13 @@ pub trait UserDataMethods<T> {
|
||||
}
|
||||
|
||||
/// Field registry for [`UserData`] implementors.
|
||||
///
|
||||
/// [`UserData`]: crate::UserData
|
||||
pub trait UserDataFields<T> {
|
||||
/// Add a static field to the `UserData`.
|
||||
/// Add a static field to the [`UserData`].
|
||||
///
|
||||
/// Static fields are implemented by updating the `__index` metamethod and returning the
|
||||
/// accessed field. This allows them to be used with the expected `userdata.field` syntax.
|
||||
///
|
||||
/// Static fields are usually shared between all instances of the `UserData` of the same type.
|
||||
/// Static fields are usually shared between all instances of the [`UserData`] of the same type.
|
||||
///
|
||||
/// If `add_meta_method` is used to set the `__index` metamethod, it will
|
||||
/// be used as a fall-back if no regular field or method are found.
|
||||
@@ -493,11 +483,6 @@ pub trait UserDataFields<T> {
|
||||
|
||||
/// Add a regular field getter as a function which accepts a generic [`AnyUserData`] of type `T`
|
||||
/// argument.
|
||||
///
|
||||
/// Prefer to use [`add_field_method_get`] as it is easier to use.
|
||||
///
|
||||
/// [`AnyUserData`]: crate::AnyUserData
|
||||
/// [`add_field_method_get`]: #method.add_field_method_get
|
||||
fn add_field_function_get<F, R>(&mut self, name: impl ToString, function: F)
|
||||
where
|
||||
F: Fn(&Lua, AnyUserData) -> Result<R> + MaybeSend + 'static,
|
||||
@@ -505,11 +490,6 @@ pub trait UserDataFields<T> {
|
||||
|
||||
/// Add a regular field setter as a function which accepts a generic [`AnyUserData`] of type `T`
|
||||
/// first argument.
|
||||
///
|
||||
/// Prefer to use [`add_field_method_set`] as it is easier to use.
|
||||
///
|
||||
/// [`AnyUserData`]: crate::AnyUserData
|
||||
/// [`add_field_method_set`]: #method.add_field_method_set
|
||||
fn add_field_function_set<F, A>(&mut self, name: impl ToString, function: F)
|
||||
where
|
||||
F: FnMut(&Lua, AnyUserData, A) -> Result<()> + MaybeSend + 'static,
|
||||
@@ -517,7 +497,7 @@ pub trait UserDataFields<T> {
|
||||
|
||||
/// Add a metatable field.
|
||||
///
|
||||
/// This will initialize the metatable field with `value` on `UserData` creation.
|
||||
/// This will initialize the metatable field with `value` on [`UserData`] creation.
|
||||
///
|
||||
/// # Note
|
||||
///
|
||||
@@ -529,7 +509,7 @@ pub trait UserDataFields<T> {
|
||||
|
||||
/// Add a metatable field computed from `f`.
|
||||
///
|
||||
/// This will initialize the metatable field from `f` on `UserData` creation.
|
||||
/// This will initialize the metatable field from `f` on [`UserData`] creation.
|
||||
///
|
||||
/// # Note
|
||||
///
|
||||
@@ -544,6 +524,7 @@ pub trait UserDataFields<T> {
|
||||
/// Trait for custom userdata types.
|
||||
///
|
||||
/// By implementing this trait, a struct becomes eligible for use inside Lua code.
|
||||
///
|
||||
/// Implementation of [`IntoLua`] is automatically provided, [`FromLua`] needs to be implemented
|
||||
/// manually.
|
||||
///
|
||||
@@ -603,11 +584,6 @@ pub trait UserDataFields<T> {
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
///
|
||||
/// [`IntoLua`]: crate::IntoLua
|
||||
/// [`FromLua`]: crate::FromLua
|
||||
/// [`UserDataFields`]: crate::UserDataFields
|
||||
/// [`UserDataMethods`]: crate::UserDataMethods
|
||||
pub trait UserData: Sized {
|
||||
/// Adds custom fields specific to this userdata.
|
||||
#[allow(unused_variables)]
|
||||
@@ -629,18 +605,14 @@ pub trait UserData: Sized {
|
||||
|
||||
/// Handle to an internal Lua userdata for any type that implements [`UserData`].
|
||||
///
|
||||
/// Similar to `std::any::Any`, this provides an interface for dynamic type checking via the [`is`]
|
||||
/// and [`borrow`] methods.
|
||||
///
|
||||
/// Internally, instances are stored in a `RefCell`, to best match the mutable semantics of the Lua
|
||||
/// language.
|
||||
/// Similar to [`std::any::Any`], this provides an interface for dynamic type checking via the
|
||||
/// [`is`] and [`borrow`] methods.
|
||||
///
|
||||
/// # Note
|
||||
///
|
||||
/// This API should only be used when necessary. Implementing [`UserData`] already allows defining
|
||||
/// methods which check the type and acquire a borrow behind the scenes.
|
||||
///
|
||||
/// [`UserData`]: crate::UserData
|
||||
/// [`is`]: crate::AnyUserData::is
|
||||
/// [`borrow`]: crate::AnyUserData::borrow
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
@@ -657,8 +629,12 @@ impl AnyUserData {
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns a `UserDataBorrowError` if the userdata is already mutably borrowed. Returns a
|
||||
/// `UserDataTypeMismatch` if the userdata is not of type `T` or if it's scoped.
|
||||
/// Returns a [`UserDataBorrowError`] if the userdata is already mutably borrowed.
|
||||
/// Returns a [`DataTypeMismatch`] if the userdata is not of type `T` or if it's
|
||||
/// scoped.
|
||||
///
|
||||
/// [`UserDataBorrowError`]: crate::Error::UserDataBorrowError
|
||||
/// [`DataTypeMismatch`]: crate::Error::UserDataTypeMismatch
|
||||
#[inline]
|
||||
pub fn borrow<T: 'static>(&self) -> Result<UserDataRef<T>> {
|
||||
self.inspect(|ud| ud.try_borrow_owned())
|
||||
@@ -676,8 +652,12 @@ impl AnyUserData {
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns a `UserDataBorrowMutError` if the userdata cannot be mutably borrowed.
|
||||
/// Returns a `UserDataTypeMismatch` if the userdata is not of type `T` or if it's scoped.
|
||||
/// Returns a [`UserDataBorrowMutError`] if the userdata cannot be mutably borrowed.
|
||||
/// Returns a [`UserDataTypeMismatch`] if the userdata is not of type `T` or if it's
|
||||
/// scoped.
|
||||
///
|
||||
/// [`UserDataBorrowMutError`]: crate::Error::UserDataBorrowMutError
|
||||
/// [`UserDataTypeMismatch`]: crate::Error::UserDataTypeMismatch
|
||||
#[inline]
|
||||
pub fn borrow_mut<T: 'static>(&self) -> Result<UserDataRefMut<T>> {
|
||||
self.inspect(|ud| ud.try_borrow_owned_mut())
|
||||
@@ -692,6 +672,7 @@ impl AnyUserData {
|
||||
}
|
||||
|
||||
/// Takes the value out of this userdata.
|
||||
///
|
||||
/// Sets the special "destructed" metatable that prevents any further operations with this
|
||||
/// userdata.
|
||||
///
|
||||
@@ -715,14 +696,39 @@ impl AnyUserData {
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets an associated value to this `AnyUserData`.
|
||||
/// Destroys this userdata.
|
||||
///
|
||||
/// This is similar to [`AnyUserData::take`], but it doesn't require a type.
|
||||
///
|
||||
/// This method works for non-scoped userdata only.
|
||||
pub fn destroy(&self) -> Result<()> {
|
||||
let lua = self.0.lua.lock();
|
||||
let state = lua.state();
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(state);
|
||||
check_stack(state, 3)?;
|
||||
|
||||
lua.push_userdata_ref(&self.0)?;
|
||||
protect_lua!(state, 1, 1, fn(state) {
|
||||
if ffi::luaL_callmeta(state, -1, cstr!("__gc")) == 0 {
|
||||
ffi::lua_pushboolean(state, 0);
|
||||
}
|
||||
})?;
|
||||
if ffi::lua_isboolean(state, -1) != 0 && ffi::lua_toboolean(state, -1) != 0 {
|
||||
return Ok(());
|
||||
}
|
||||
Err(Error::UserDataBorrowMutError)
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets an associated value to this [`AnyUserData`].
|
||||
///
|
||||
/// The value may be any Lua value whatsoever, and can be retrieved with [`user_value`].
|
||||
///
|
||||
/// This is the same as calling [`set_nth_user_value`] with `n` set to 1.
|
||||
///
|
||||
/// [`user_value`]: #method.user_value
|
||||
/// [`set_nth_user_value`]: #method.set_nth_user_value
|
||||
/// [`user_value`]: AnyUserData::user_value
|
||||
/// [`set_nth_user_value`]: AnyUserData::set_nth_user_value
|
||||
#[inline]
|
||||
pub fn set_user_value(&self, v: impl IntoLua) -> Result<()> {
|
||||
self.set_nth_user_value(1, v)
|
||||
@@ -732,29 +738,21 @@ impl AnyUserData {
|
||||
///
|
||||
/// This is the same as calling [`nth_user_value`] with `n` set to 1.
|
||||
///
|
||||
/// [`set_user_value`]: #method.set_user_value
|
||||
/// [`nth_user_value`]: #method.nth_user_value
|
||||
/// [`set_user_value`]: AnyUserData::set_user_value
|
||||
/// [`nth_user_value`]: AnyUserData::nth_user_value
|
||||
#[inline]
|
||||
pub fn user_value<V: FromLua>(&self) -> Result<V> {
|
||||
self.nth_user_value(1)
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
#[deprecated(since = "0.9.0", note = "please use `user_value` instead")]
|
||||
pub fn get_user_value<V: FromLua>(&self) -> Result<V> {
|
||||
self.nth_user_value(1)
|
||||
}
|
||||
|
||||
/// Sets an associated `n`th value to this `AnyUserData`.
|
||||
/// Sets an associated `n`th value to this [`AnyUserData`].
|
||||
///
|
||||
/// The value may be any Lua value whatsoever, and can be retrieved with [`nth_user_value`].
|
||||
/// `n` starts from 1 and can be up to 65535.
|
||||
///
|
||||
/// This is supported for all Lua versions.
|
||||
/// In Lua 5.4 first 7 elements are stored in a most efficient way.
|
||||
/// For other Lua versions this functionality is provided using a wrapping table.
|
||||
/// This is supported for all Lua versions using a wrapping table.
|
||||
///
|
||||
/// [`nth_user_value`]: #method.nth_user_value
|
||||
/// [`nth_user_value`]: AnyUserData::nth_user_value
|
||||
pub fn set_nth_user_value(&self, n: usize, v: impl IntoLua) -> Result<()> {
|
||||
if n < 1 || n > u16::MAX as usize {
|
||||
return Err(Error::runtime("user value index out of bounds"));
|
||||
@@ -790,11 +788,9 @@ impl AnyUserData {
|
||||
///
|
||||
/// `n` starts from 1 and can be up to 65535.
|
||||
///
|
||||
/// This is supported for all Lua versions.
|
||||
/// In Lua 5.4 first 7 elements are stored in a most efficient way.
|
||||
/// For other Lua versions this functionality is provided using a wrapping table.
|
||||
/// This is supported for all Lua versions using a wrapping table.
|
||||
///
|
||||
/// [`set_nth_user_value`]: #method.set_nth_user_value
|
||||
/// [`set_nth_user_value`]: AnyUserData::set_nth_user_value
|
||||
pub fn nth_user_value<V: FromLua>(&self, n: usize) -> Result<V> {
|
||||
if n < 1 || n > u16::MAX as usize {
|
||||
return Err(Error::runtime("user value index out of bounds"));
|
||||
@@ -809,29 +805,20 @@ impl AnyUserData {
|
||||
lua.push_userdata_ref(&self.0)?;
|
||||
|
||||
// Multiple (extra) user values are emulated by storing them in a table
|
||||
protect_lua!(state, 1, 1, |state| {
|
||||
if ffi::lua_getuservalue(state, -1) != ffi::LUA_TTABLE {
|
||||
ffi::lua_pushnil(state);
|
||||
return;
|
||||
}
|
||||
ffi::lua_rawgeti(state, -1, n as ffi::lua_Integer);
|
||||
})?;
|
||||
if ffi::lua_getuservalue(state, -1) != ffi::LUA_TTABLE {
|
||||
return V::from_lua(Value::Nil, lua.lua());
|
||||
}
|
||||
ffi::lua_rawgeti(state, -1, n as ffi::lua_Integer);
|
||||
|
||||
V::from_lua(lua.pop_value(), lua.lua())
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
#[deprecated(since = "0.9.0", note = "please use `nth_user_value` instead")]
|
||||
pub fn get_nth_user_value<V: FromLua>(&self, n: usize) -> Result<V> {
|
||||
self.nth_user_value(n)
|
||||
}
|
||||
|
||||
/// Sets an associated value to this `AnyUserData` by name.
|
||||
/// Sets an associated value to this [`AnyUserData`] by name.
|
||||
///
|
||||
/// The value can be retrieved with [`named_user_value`].
|
||||
///
|
||||
/// [`named_user_value`]: #method.named_user_value
|
||||
/// [`named_user_value`]: AnyUserData::named_user_value
|
||||
pub fn set_named_user_value(&self, name: &str, v: impl IntoLua) -> Result<()> {
|
||||
let lua = self.0.lua.lock();
|
||||
let state = lua.state();
|
||||
@@ -862,7 +849,7 @@ impl AnyUserData {
|
||||
|
||||
/// Returns an associated value by name set by [`set_named_user_value`].
|
||||
///
|
||||
/// [`set_named_user_value`]: #method.set_named_user_value
|
||||
/// [`set_named_user_value`]: AnyUserData::set_named_user_value
|
||||
pub fn named_user_value<V: FromLua>(&self, name: &str) -> Result<V> {
|
||||
let lua = self.0.lua.lock();
|
||||
let state = lua.state();
|
||||
@@ -873,36 +860,25 @@ impl AnyUserData {
|
||||
lua.push_userdata_ref(&self.0)?;
|
||||
|
||||
// Multiple (extra) user values are emulated by storing them in a table
|
||||
protect_lua!(state, 1, 1, |state| {
|
||||
if ffi::lua_getuservalue(state, -1) != ffi::LUA_TTABLE {
|
||||
ffi::lua_pushnil(state);
|
||||
return;
|
||||
}
|
||||
ffi::lua_pushlstring(state, name.as_ptr() as *const c_char, name.len());
|
||||
ffi::lua_rawget(state, -2);
|
||||
})?;
|
||||
if ffi::lua_getuservalue(state, -1) != ffi::LUA_TTABLE {
|
||||
return V::from_lua(Value::Nil, lua.lua());
|
||||
}
|
||||
push_string(state, name.as_bytes(), !lua.unlikely_memory_error())?;
|
||||
ffi::lua_rawget(state, -2);
|
||||
|
||||
V::from_lua(lua.pop_value(), lua.lua())
|
||||
V::from_stack(-1, &lua)
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
#[deprecated(since = "0.9.0", note = "please use `named_user_value` instead")]
|
||||
pub fn get_named_user_value<V: FromLua>(&self, name: &str) -> Result<V> {
|
||||
self.named_user_value(name)
|
||||
}
|
||||
|
||||
/// Returns a metatable of this `UserData`.
|
||||
/// Returns a metatable of this [`AnyUserData`].
|
||||
///
|
||||
/// Returned [`UserDataMetatable`] object wraps the original metatable and
|
||||
/// provides safe access to its methods.
|
||||
///
|
||||
/// For `T: 'static` returned metatable is shared among all instances of type `T`.
|
||||
///
|
||||
/// [`UserDataMetatable`]: crate::UserDataMetatable
|
||||
#[inline]
|
||||
pub fn metatable(&self) -> Result<UserDataMetatable> {
|
||||
self.get_raw_metatable().map(UserDataMetatable)
|
||||
self.raw_metatable().map(UserDataMetatable)
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
@@ -911,7 +887,7 @@ impl AnyUserData {
|
||||
self.metatable()
|
||||
}
|
||||
|
||||
fn get_raw_metatable(&self) -> Result<Table> {
|
||||
fn raw_metatable(&self) -> Result<Table> {
|
||||
let lua = self.0.lua.lock();
|
||||
let state = lua.state();
|
||||
unsafe {
|
||||
@@ -958,15 +934,14 @@ impl AnyUserData {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn equals<T: AsRef<Self>>(&self, other: T) -> Result<bool> {
|
||||
let other = other.as_ref();
|
||||
pub(crate) fn equals(&self, other: &Self) -> Result<bool> {
|
||||
// Uses lua_rawequal() under the hood
|
||||
if self == other {
|
||||
return Ok(true);
|
||||
}
|
||||
|
||||
let mt = self.get_raw_metatable()?;
|
||||
if mt != other.get_raw_metatable()? {
|
||||
let mt = self.raw_metatable()?;
|
||||
if mt != other.raw_metatable()? {
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
@@ -977,8 +952,8 @@ impl AnyUserData {
|
||||
Ok(false)
|
||||
}
|
||||
|
||||
/// Returns `true` if this `AnyUserData` is serializable (eg. was created using
|
||||
/// `create_ser_userdata`).
|
||||
/// Returns `true` if this [`AnyUserData`] is serializable (e.g. was created using
|
||||
/// [`Lua::create_ser_userdata`]).
|
||||
#[cfg(feature = "serialize")]
|
||||
pub(crate) fn is_serializable(&self) -> bool {
|
||||
let lua = self.0.lua.lock();
|
||||
@@ -1010,14 +985,7 @@ impl AnyUserData {
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<AnyUserData> for AnyUserData {
|
||||
#[inline]
|
||||
fn as_ref(&self) -> &Self {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
/// Handle to a `UserData` metatable.
|
||||
/// Handle to a [`AnyUserData`] metatable.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct UserDataMetatable(pub(crate) Table);
|
||||
|
||||
@@ -1060,17 +1028,14 @@ impl UserDataMetatable {
|
||||
}
|
||||
}
|
||||
|
||||
/// An iterator over the pairs of a [`UserData`] metatable.
|
||||
/// An iterator over the pairs of a [`AnyUserData`] metatable.
|
||||
///
|
||||
/// It skips restricted metamethods, such as `__gc` or `__metatable`.
|
||||
///
|
||||
/// This struct is created by the [`UserDataMetatable::pairs`] method.
|
||||
///
|
||||
/// [`UserData`]: crate::UserData
|
||||
/// [`UserDataMetatable::pairs`]: crate::UserDataMetatable::method.pairs
|
||||
pub struct UserDataMetatablePairs<'a, V>(TablePairs<'a, StdString, V>);
|
||||
|
||||
impl<'a, V> Iterator for UserDataMetatablePairs<'a, V>
|
||||
impl<V> Iterator for UserDataMetatablePairs<'_, V>
|
||||
where
|
||||
V: FromLua,
|
||||
{
|
||||
@@ -1113,7 +1078,7 @@ pub(crate) struct WrappedUserdata<F: FnOnce(&Lua) -> Result<AnyUserData>>(F);
|
||||
impl AnyUserData {
|
||||
/// Wraps any Rust type, returning an opaque type that implements [`IntoLua`] trait.
|
||||
///
|
||||
/// This function uses [`Lua::create_any_userdata()`] under the hood.
|
||||
/// This function uses [`Lua::create_any_userdata`] under the hood.
|
||||
pub fn wrap<T: MaybeSend + 'static>(data: T) -> impl IntoLua {
|
||||
WrappedUserdata(move |lua| lua.create_any_userdata(data))
|
||||
}
|
||||
@@ -1121,7 +1086,7 @@ impl AnyUserData {
|
||||
/// Wraps any Rust type that implements [`Serialize`], returning an opaque type that implements
|
||||
/// [`IntoLua`] trait.
|
||||
///
|
||||
/// This function uses [`Lua::create_ser_any_userdata()`] under the hood.
|
||||
/// This function uses [`Lua::create_ser_any_userdata`] under the hood.
|
||||
#[cfg(feature = "serialize")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "serialize")))]
|
||||
pub fn wrap_ser<T: Serialize + MaybeSend + 'static>(data: T) -> impl IntoLua {
|
||||
@@ -1142,6 +1107,7 @@ mod cell;
|
||||
mod lock;
|
||||
mod object;
|
||||
mod registry;
|
||||
mod util;
|
||||
|
||||
#[cfg(test)]
|
||||
mod assertions {
|
||||
|
||||
+54
-12
@@ -1,5 +1,5 @@
|
||||
use std::any::{type_name, TypeId};
|
||||
use std::cell::{RefCell, UnsafeCell};
|
||||
use std::cell::{Cell, RefCell, UnsafeCell};
|
||||
use std::fmt;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use std::os::raw::c_int;
|
||||
@@ -16,6 +16,7 @@ use crate::util::get_userdata;
|
||||
use crate::value::Value;
|
||||
|
||||
use super::lock::{RawLock, UserDataLock};
|
||||
use super::util::is_sync;
|
||||
|
||||
#[cfg(all(feature = "serialize", not(feature = "send")))]
|
||||
type DynSerialize = dyn erased_serde::Serialize;
|
||||
@@ -98,6 +99,15 @@ impl<T> UserDataVariant<T> {
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn borrow_count(&self) -> &Cell<usize> {
|
||||
match self {
|
||||
Self::Default(inner) => &inner.borrow_count,
|
||||
#[cfg(feature = "serialize")]
|
||||
Self::Serializable(inner) => &inner.borrow_count,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn as_ptr(&self) -> *mut T {
|
||||
match self {
|
||||
@@ -129,10 +139,13 @@ impl Serialize for UserDataStorage<()> {
|
||||
/// A type that provides interior mutability for a userdata value (thread-safe).
|
||||
pub(crate) struct UserDataCell<T> {
|
||||
raw_lock: RawLock,
|
||||
borrow_count: Cell<usize>,
|
||||
value: UnsafeCell<T>,
|
||||
}
|
||||
|
||||
#[cfg(feature = "send")]
|
||||
unsafe impl<T: Send> Send for UserDataCell<T> {}
|
||||
#[cfg(feature = "send")]
|
||||
unsafe impl<T: Send> Sync for UserDataCell<T> {}
|
||||
|
||||
impl<T> UserDataCell<T> {
|
||||
@@ -140,12 +153,13 @@ 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.
|
||||
/// 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>);
|
||||
@@ -162,7 +176,11 @@ impl<T> Deref for UserDataRef<T> {
|
||||
impl<T> Drop for UserDataRef<T> {
|
||||
#[inline]
|
||||
fn drop(&mut self) {
|
||||
unsafe { self.0.raw_lock().unlock_shared() };
|
||||
if !cfg!(feature = "send") || is_sync::<T>() {
|
||||
unsafe { self.0.raw_lock().unlock_shared() };
|
||||
} else {
|
||||
unsafe { self.0.raw_lock().unlock_exclusive() };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -183,7 +201,11 @@ impl<T> TryFrom<UserDataVariant<T>> for UserDataRef<T> {
|
||||
|
||||
#[inline]
|
||||
fn try_from(variant: UserDataVariant<T>) -> Result<Self> {
|
||||
if !variant.raw_lock().try_lock_shared() {
|
||||
if !cfg!(feature = "send") || is_sync::<T>() {
|
||||
if !variant.raw_lock().try_lock_shared() {
|
||||
return Err(Error::UserDataBorrowError);
|
||||
}
|
||||
} else if !variant.raw_lock().try_lock_exclusive() {
|
||||
return Err(Error::UserDataBorrowError);
|
||||
}
|
||||
Ok(UserDataRef(variant))
|
||||
@@ -206,7 +228,7 @@ impl<T: 'static> FromLua for UserDataRef<T> {
|
||||
}
|
||||
}
|
||||
|
||||
/// A wrapper type for a mutably borrowed value from a `AnyUserData`.
|
||||
/// 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>);
|
||||
@@ -277,14 +299,17 @@ impl<T: 'static> FromLua for UserDataRefMut<T> {
|
||||
/// A type that provides read access to a userdata value (borrowing the value).
|
||||
pub(crate) struct UserDataBorrowRef<'a, T>(&'a UserDataVariant<T>);
|
||||
|
||||
impl<'a, T> Drop for UserDataBorrowRef<'a, T> {
|
||||
impl<T> Drop for UserDataBorrowRef<'_, T> {
|
||||
#[inline]
|
||||
fn drop(&mut self) {
|
||||
unsafe { self.0.raw_lock().unlock_shared() };
|
||||
unsafe {
|
||||
self.0.borrow_count().set(self.0.borrow_count().get() - 1);
|
||||
self.0.raw_lock().unlock_shared();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> Deref for UserDataBorrowRef<'a, T> {
|
||||
impl<T> Deref for UserDataBorrowRef<'_, T> {
|
||||
type Target = T;
|
||||
|
||||
#[inline]
|
||||
@@ -299,23 +324,31 @@ impl<'a, T> TryFrom<&'a UserDataVariant<T>> for UserDataBorrowRef<'a, T> {
|
||||
|
||||
#[inline(always)]
|
||||
fn try_from(variant: &'a UserDataVariant<T>) -> Result<Self> {
|
||||
// We don't need to check for `T: Sync` because when this method is used (internally),
|
||||
// Lua mutex is already locked.
|
||||
// If non-`Sync` userdata is already borrowed by another thread (via `UserDataRef`), it will be
|
||||
// exclusively locked.
|
||||
if !variant.raw_lock().try_lock_shared() {
|
||||
return Err(Error::UserDataBorrowError);
|
||||
}
|
||||
variant.borrow_count().set(variant.borrow_count().get() + 1);
|
||||
Ok(UserDataBorrowRef(variant))
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct UserDataBorrowMut<'a, T>(&'a UserDataVariant<T>);
|
||||
|
||||
impl<'a, T> Drop for UserDataBorrowMut<'a, T> {
|
||||
impl<T> Drop for UserDataBorrowMut<'_, T> {
|
||||
#[inline]
|
||||
fn drop(&mut self) {
|
||||
unsafe { self.0.raw_lock().unlock_exclusive() };
|
||||
unsafe {
|
||||
self.0.borrow_count().set(self.0.borrow_count().get() - 1);
|
||||
self.0.raw_lock().unlock_exclusive();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> Deref for UserDataBorrowMut<'a, T> {
|
||||
impl<T> Deref for UserDataBorrowMut<'_, T> {
|
||||
type Target = T;
|
||||
|
||||
#[inline]
|
||||
@@ -324,7 +357,7 @@ impl<'a, T> Deref for UserDataBorrowMut<'a, T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> DerefMut for UserDataBorrowMut<'a, T> {
|
||||
impl<T> DerefMut for UserDataBorrowMut<'_, T> {
|
||||
#[inline]
|
||||
fn deref_mut(&mut self) -> &mut T {
|
||||
unsafe { &mut *self.0.as_ptr() }
|
||||
@@ -339,6 +372,7 @@ impl<'a, T> TryFrom<&'a UserDataVariant<T>> for UserDataBorrowMut<'a, T> {
|
||||
if !variant.raw_lock().try_lock_exclusive() {
|
||||
return Err(Error::UserDataBorrowMutError);
|
||||
}
|
||||
variant.borrow_count().set(variant.borrow_count().get() + 1);
|
||||
Ok(UserDataBorrowMut(variant))
|
||||
}
|
||||
}
|
||||
@@ -455,6 +489,14 @@ impl<T> UserDataStorage<T> {
|
||||
Self::Scoped(ScopedUserDataVariant::Boxed(RefCell::new(data)))
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub(crate) fn is_borrowed(&self) -> bool {
|
||||
match self {
|
||||
Self::Owned(variant) => variant.borrow_count().get() > 0,
|
||||
Self::Scoped(_) => true,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn try_borrow_scoped<R>(&self, f: impl FnOnce(&T) -> R) -> Result<R> {
|
||||
match self {
|
||||
|
||||
@@ -11,6 +11,7 @@ pub(crate) trait UserDataLock {
|
||||
pub(crate) use lock_impl::RawLock;
|
||||
|
||||
#[cfg(not(feature = "send"))]
|
||||
#[cfg(not(tarpaulin_include))]
|
||||
mod lock_impl {
|
||||
use std::cell::Cell;
|
||||
|
||||
@@ -62,32 +63,32 @@ mod lock_impl {
|
||||
|
||||
#[cfg(feature = "send")]
|
||||
mod lock_impl {
|
||||
use parking_lot::lock_api::RawMutex;
|
||||
use parking_lot::lock_api::RawRwLock;
|
||||
|
||||
pub(crate) type RawLock = parking_lot::RawMutex;
|
||||
pub(crate) type RawLock = parking_lot::RawRwLock;
|
||||
|
||||
impl super::UserDataLock for RawLock {
|
||||
#[allow(clippy::declare_interior_mutable_const)]
|
||||
const INIT: Self = <Self as parking_lot::lock_api::RawMutex>::INIT;
|
||||
const INIT: Self = <Self as parking_lot::lock_api::RawRwLock>::INIT;
|
||||
|
||||
#[inline(always)]
|
||||
fn try_lock_shared(&self) -> bool {
|
||||
RawLock::try_lock(self)
|
||||
RawRwLock::try_lock_shared(self)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn try_lock_exclusive(&self) -> bool {
|
||||
RawLock::try_lock(self)
|
||||
RawRwLock::try_lock_exclusive(self)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
unsafe fn unlock_shared(&self) {
|
||||
RawLock::unlock(self)
|
||||
RawRwLock::unlock_shared(self)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
unsafe fn unlock_exclusive(&self) {
|
||||
RawLock::unlock(self)
|
||||
RawRwLock::unlock_exclusive(self)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+250
-64
@@ -21,12 +21,32 @@ use {
|
||||
std::future::{self, Future},
|
||||
};
|
||||
|
||||
#[cfg(all(feature = "userdata-wrappers", not(feature = "send")))]
|
||||
use std::rc::Rc;
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
use std::sync::{Arc, Mutex, RwLock};
|
||||
|
||||
type StaticFieldCallback = Box<dyn FnOnce(&RawLua) -> Result<()> + 'static>;
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub(crate) enum UserDataTypeId {
|
||||
enum UserDataTypeId {
|
||||
Shared(TypeId),
|
||||
Unique(usize),
|
||||
|
||||
#[cfg(all(feature = "userdata-wrappers", not(feature = "send")))]
|
||||
Rc(TypeId),
|
||||
#[cfg(all(feature = "userdata-wrappers", not(feature = "send")))]
|
||||
RcRefCell(TypeId),
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
Arc(TypeId),
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
ArcMutex(TypeId),
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
ArcRwLock(TypeId),
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
ArcParkingLotMutex(TypeId),
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
ArcParkingLotRwLock(TypeId),
|
||||
}
|
||||
|
||||
/// Handle to registry for userdata methods and metamethods.
|
||||
@@ -45,31 +65,23 @@ pub struct UserDataRegistry<T> {
|
||||
#[cfg(feature = "async")]
|
||||
pub(crate) async_meta_methods: Vec<(String, AsyncCallback)>,
|
||||
|
||||
pub(crate) type_id: UserDataTypeId,
|
||||
type_id: UserDataTypeId,
|
||||
_type: PhantomData<T>,
|
||||
}
|
||||
|
||||
impl<T> UserDataRegistry<T> {
|
||||
#[inline]
|
||||
#[inline(always)]
|
||||
pub(crate) fn new(type_id: TypeId) -> Self {
|
||||
UserDataRegistry {
|
||||
fields: Vec::new(),
|
||||
field_getters: Vec::new(),
|
||||
field_setters: Vec::new(),
|
||||
meta_fields: Vec::new(),
|
||||
methods: Vec::new(),
|
||||
#[cfg(feature = "async")]
|
||||
async_methods: Vec::new(),
|
||||
meta_methods: Vec::new(),
|
||||
#[cfg(feature = "async")]
|
||||
async_meta_methods: Vec::new(),
|
||||
type_id: UserDataTypeId::Shared(type_id),
|
||||
_type: PhantomData,
|
||||
}
|
||||
Self::with_type_id(UserDataTypeId::Shared(type_id))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn new_unique(ud_ptr: *const c_void) -> Self {
|
||||
#[inline(always)]
|
||||
pub(crate) fn new_unique(ud_ptr: *mut c_void) -> Self {
|
||||
Self::with_type_id(UserDataTypeId::Unique(ud_ptr as usize))
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn with_type_id(type_id: UserDataTypeId) -> Self {
|
||||
UserDataRegistry {
|
||||
fields: Vec::new(),
|
||||
field_getters: Vec::new(),
|
||||
@@ -81,7 +93,7 @@ impl<T> UserDataRegistry<T> {
|
||||
meta_methods: Vec::new(),
|
||||
#[cfg(feature = "async")]
|
||||
async_meta_methods: Vec::new(),
|
||||
type_id: UserDataTypeId::Unique(ud_ptr as usize),
|
||||
type_id,
|
||||
_type: PhantomData,
|
||||
}
|
||||
}
|
||||
@@ -91,6 +103,20 @@ impl<T> UserDataRegistry<T> {
|
||||
match self.type_id {
|
||||
UserDataTypeId::Shared(type_id) => Some(type_id),
|
||||
UserDataTypeId::Unique(_) => None,
|
||||
#[cfg(all(feature = "userdata-wrappers", not(feature = "send")))]
|
||||
UserDataTypeId::Rc(type_id) => Some(type_id),
|
||||
#[cfg(all(feature = "userdata-wrappers", not(feature = "send")))]
|
||||
UserDataTypeId::RcRefCell(type_id) => Some(type_id),
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
UserDataTypeId::Arc(type_id) => Some(type_id),
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
UserDataTypeId::ArcMutex(type_id) => Some(type_id),
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
UserDataTypeId::ArcRwLock(type_id) => Some(type_id),
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
UserDataTypeId::ArcParkingLotMutex(type_id) => Some(type_id),
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
UserDataTypeId::ArcParkingLotRwLock(type_id) => Some(type_id),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -120,28 +146,102 @@ impl<T> UserDataRegistry<T> {
|
||||
let args = A::from_stack_args(nargs - 1, 2, Some(&name), rawlua);
|
||||
|
||||
match target_type_id {
|
||||
// This branch is for `'static` userdata that share type metatable
|
||||
UserDataTypeId::Shared(target_type_id) => {
|
||||
match try_self_arg!(rawlua.get_userdata_type_id::<T>(self_index)) {
|
||||
Some(self_type_id) if self_type_id == target_type_id => {
|
||||
let ud = get_userdata::<UserDataStorage<T>>(state, self_index);
|
||||
try_self_arg!((*ud).try_borrow_scoped(|ud| {
|
||||
method(rawlua.lua(), ud, args?)?.push_into_stack_multi(rawlua)
|
||||
}))
|
||||
}
|
||||
_ => Err(Error::bad_self_argument(&name, Error::UserDataTypeMismatch)),
|
||||
}
|
||||
#[rustfmt::skip]
|
||||
UserDataTypeId::Shared(target_type_id)
|
||||
if try_self_arg!(rawlua.get_userdata_type_id::<T>(self_index)) == Some(target_type_id) =>
|
||||
{
|
||||
let ud = get_userdata::<UserDataStorage<T>>(state, self_index);
|
||||
try_self_arg!((*ud).try_borrow_scoped(|ud| {
|
||||
method(rawlua.lua(), ud, args?)?.push_into_stack_multi(rawlua)
|
||||
}))
|
||||
}
|
||||
UserDataTypeId::Unique(target_ptr) => {
|
||||
match get_userdata::<UserDataStorage<T>>(state, self_index) {
|
||||
ud if ud as usize == target_ptr => {
|
||||
try_self_arg!((*ud).try_borrow_scoped(|ud| {
|
||||
method(rawlua.lua(), ud, args?)?.push_into_stack_multi(rawlua)
|
||||
}))
|
||||
}
|
||||
_ => Err(Error::bad_self_argument(&name, Error::UserDataTypeMismatch)),
|
||||
}
|
||||
#[rustfmt::skip]
|
||||
UserDataTypeId::Unique(target_ptr)
|
||||
if get_userdata::<UserDataStorage<T>>(state, self_index) as usize == target_ptr =>
|
||||
{
|
||||
let ud = target_ptr as *mut UserDataStorage<T>;
|
||||
try_self_arg!((*ud).try_borrow_scoped(|ud| {
|
||||
method(rawlua.lua(), ud, args?)?.push_into_stack_multi(rawlua)
|
||||
}))
|
||||
}
|
||||
#[cfg(all(feature = "userdata-wrappers", not(feature = "send")))]
|
||||
#[rustfmt::skip]
|
||||
UserDataTypeId::Rc(target_type_id)
|
||||
if try_self_arg!(rawlua.get_userdata_type_id::<Rc<T>>(self_index)) == Some(target_type_id) =>
|
||||
{
|
||||
let ud = get_userdata::<UserDataStorage<Rc<T>>>(state, self_index);
|
||||
try_self_arg!((*ud).try_borrow_scoped(|ud| {
|
||||
method(rawlua.lua(), ud, args?)?.push_into_stack_multi(rawlua)
|
||||
}))
|
||||
}
|
||||
#[cfg(all(feature = "userdata-wrappers", not(feature = "send")))]
|
||||
#[rustfmt::skip]
|
||||
UserDataTypeId::RcRefCell(target_type_id)
|
||||
if try_self_arg!(rawlua.get_userdata_type_id::<Rc<RefCell<T>>>(self_index)) == Some(target_type_id) =>
|
||||
{
|
||||
let ud = get_userdata::<UserDataStorage<Rc<RefCell<T>>>>(state, self_index);
|
||||
try_self_arg!((*ud).try_borrow_scoped(|ud| {
|
||||
let ud = ud.try_borrow().map_err(|_| Error::UserDataBorrowError)?;
|
||||
method(rawlua.lua(), &ud, args?)?.push_into_stack_multi(rawlua)
|
||||
}))
|
||||
}
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
#[rustfmt::skip]
|
||||
UserDataTypeId::Arc(target_type_id)
|
||||
if try_self_arg!(rawlua.get_userdata_type_id::<Arc<T>>(self_index)) == Some(target_type_id) =>
|
||||
{
|
||||
let ud = get_userdata::<UserDataStorage<Arc<T>>>(state, self_index);
|
||||
try_self_arg!((*ud).try_borrow_scoped(|ud| {
|
||||
method(rawlua.lua(), ud, args?)?.push_into_stack_multi(rawlua)
|
||||
}))
|
||||
}
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
#[rustfmt::skip]
|
||||
UserDataTypeId::ArcMutex(target_type_id)
|
||||
if try_self_arg!(rawlua.get_userdata_type_id::<Arc<Mutex<T>>>(self_index)) == Some(target_type_id) =>
|
||||
{
|
||||
let ud = get_userdata::<UserDataStorage<Arc<Mutex<T>>>>(state, self_index);
|
||||
try_self_arg!((*ud).try_borrow_scoped(|ud| {
|
||||
let ud = ud.try_lock().map_err(|_| Error::UserDataBorrowError)?;
|
||||
method(rawlua.lua(), &ud, args?)?.push_into_stack_multi(rawlua)
|
||||
}))
|
||||
}
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
#[rustfmt::skip]
|
||||
UserDataTypeId::ArcRwLock(target_type_id)
|
||||
if try_self_arg!(rawlua.get_userdata_type_id::<Arc<RwLock<T>>>(self_index)) == Some(target_type_id) =>
|
||||
{
|
||||
let ud = get_userdata::<UserDataStorage<Arc<RwLock<T>>>>(state, self_index);
|
||||
try_self_arg!((*ud).try_borrow_scoped(|ud| {
|
||||
let ud = ud.try_read().map_err(|_| Error::UserDataBorrowError)?;
|
||||
method(rawlua.lua(), &ud, args?)?.push_into_stack_multi(rawlua)
|
||||
}))
|
||||
}
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
#[rustfmt::skip]
|
||||
UserDataTypeId::ArcParkingLotMutex(target_type_id)
|
||||
if try_self_arg!(rawlua.get_userdata_type_id::<Arc<parking_lot::Mutex<T>>>(self_index))
|
||||
== Some(target_type_id) =>
|
||||
{
|
||||
let ud = get_userdata::<UserDataStorage<Arc<parking_lot::Mutex<T>>>>(state, self_index);
|
||||
try_self_arg!((*ud).try_borrow_scoped(|ud| {
|
||||
let ud = ud.try_lock().ok_or(Error::UserDataBorrowError)?;
|
||||
method(rawlua.lua(), &ud, args?)?.push_into_stack_multi(rawlua)
|
||||
}))
|
||||
}
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
#[rustfmt::skip]
|
||||
UserDataTypeId::ArcParkingLotRwLock(target_type_id)
|
||||
if try_self_arg!(rawlua.get_userdata_type_id::<Arc<parking_lot::RwLock<T>>>(self_index))
|
||||
== Some(target_type_id) =>
|
||||
{
|
||||
let ud = get_userdata::<UserDataStorage<Arc<parking_lot::RwLock<T>>>>(state, self_index);
|
||||
try_self_arg!((*ud).try_borrow_scoped(|ud| {
|
||||
let ud = ud.try_read().ok_or(Error::UserDataBorrowError)?;
|
||||
method(rawlua.lua(), &ud, args?)?.push_into_stack_multi(rawlua)
|
||||
}))
|
||||
}
|
||||
_ => Err(Error::bad_self_argument(&name, Error::UserDataTypeMismatch)),
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -174,28 +274,96 @@ impl<T> UserDataRegistry<T> {
|
||||
let args = A::from_stack_args(nargs - 1, 2, Some(&name), rawlua);
|
||||
|
||||
match target_type_id {
|
||||
// This branch is for `'static` userdata that share type metatable
|
||||
UserDataTypeId::Shared(target_type_id) => {
|
||||
match try_self_arg!(rawlua.get_userdata_type_id::<T>(self_index)) {
|
||||
Some(self_type_id) if self_type_id == target_type_id => {
|
||||
let ud = get_userdata::<UserDataStorage<T>>(state, self_index);
|
||||
try_self_arg!((*ud).try_borrow_scoped_mut(|ud| {
|
||||
method(rawlua.lua(), ud, args?)?.push_into_stack_multi(rawlua)
|
||||
}))
|
||||
}
|
||||
_ => Err(Error::bad_self_argument(&name, Error::UserDataTypeMismatch)),
|
||||
}
|
||||
#[rustfmt::skip]
|
||||
UserDataTypeId::Shared(target_type_id)
|
||||
if try_self_arg!(rawlua.get_userdata_type_id::<T>(self_index)) == Some(target_type_id) =>
|
||||
{
|
||||
let ud = get_userdata::<UserDataStorage<T>>(state, self_index);
|
||||
try_self_arg!((*ud).try_borrow_scoped_mut(|ud| {
|
||||
method(rawlua.lua(), ud, args?)?.push_into_stack_multi(rawlua)
|
||||
}))
|
||||
}
|
||||
UserDataTypeId::Unique(target_ptr) => {
|
||||
match get_userdata::<UserDataStorage<T>>(state, self_index) {
|
||||
ud if ud as usize == target_ptr => {
|
||||
try_self_arg!((*ud).try_borrow_scoped_mut(|ud| {
|
||||
method(rawlua.lua(), ud, args?)?.push_into_stack_multi(rawlua)
|
||||
}))
|
||||
}
|
||||
_ => Err(Error::bad_self_argument(&name, Error::UserDataTypeMismatch)),
|
||||
}
|
||||
#[rustfmt::skip]
|
||||
UserDataTypeId::Unique(target_ptr)
|
||||
if get_userdata::<UserDataStorage<T>>(state, self_index) as usize == target_ptr =>
|
||||
{
|
||||
let ud = target_ptr as *mut UserDataStorage<T>;
|
||||
try_self_arg!((*ud).try_borrow_scoped_mut(|ud| {
|
||||
method(rawlua.lua(), ud, args?)?.push_into_stack_multi(rawlua)
|
||||
}))
|
||||
}
|
||||
#[cfg(all(feature = "userdata-wrappers", not(feature = "send")))]
|
||||
#[rustfmt::skip]
|
||||
UserDataTypeId::Rc(target_type_id)
|
||||
if try_self_arg!(rawlua.get_userdata_type_id::<Rc<T>>(self_index)) == Some(target_type_id) =>
|
||||
{
|
||||
Err(Error::UserDataBorrowMutError)
|
||||
},
|
||||
#[cfg(all(feature = "userdata-wrappers", not(feature = "send")))]
|
||||
#[rustfmt::skip]
|
||||
UserDataTypeId::RcRefCell(target_type_id)
|
||||
if try_self_arg!(rawlua.get_userdata_type_id::<Rc<RefCell<T>>>(self_index)) == Some(target_type_id) =>
|
||||
{
|
||||
let ud = get_userdata::<UserDataStorage<Rc<RefCell<T>>>>(state, self_index);
|
||||
try_self_arg!((*ud).try_borrow_scoped(|ud| {
|
||||
let mut ud = ud.try_borrow_mut().map_err(|_| Error::UserDataBorrowMutError)?;
|
||||
method(rawlua.lua(), &mut ud, args?)?.push_into_stack_multi(rawlua)
|
||||
}))
|
||||
}
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
#[rustfmt::skip]
|
||||
UserDataTypeId::Arc(target_type_id)
|
||||
if try_self_arg!(rawlua.get_userdata_type_id::<Arc<T>>(self_index)) == Some(target_type_id) =>
|
||||
{
|
||||
Err(Error::UserDataBorrowMutError)
|
||||
},
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
#[rustfmt::skip]
|
||||
UserDataTypeId::ArcMutex(target_type_id)
|
||||
if try_self_arg!(rawlua.get_userdata_type_id::<Arc<Mutex<T>>>(self_index)) == Some(target_type_id) =>
|
||||
{
|
||||
let ud = get_userdata::<UserDataStorage<Arc<Mutex<T>>>>(state, self_index);
|
||||
try_self_arg!((*ud).try_borrow_scoped(|ud| {
|
||||
let mut ud = ud.try_lock().map_err(|_| Error::UserDataBorrowMutError)?;
|
||||
method(rawlua.lua(), &mut ud, args?)?.push_into_stack_multi(rawlua)
|
||||
}))
|
||||
}
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
#[rustfmt::skip]
|
||||
UserDataTypeId::ArcRwLock(target_type_id)
|
||||
if try_self_arg!(rawlua.get_userdata_type_id::<Arc<RwLock<T>>>(self_index)) == Some(target_type_id) =>
|
||||
{
|
||||
let ud = get_userdata::<UserDataStorage<Arc<RwLock<T>>>>(state, self_index);
|
||||
try_self_arg!((*ud).try_borrow_scoped(|ud| {
|
||||
let mut ud = ud.try_write().map_err(|_| Error::UserDataBorrowMutError)?;
|
||||
method(rawlua.lua(), &mut ud, args?)?.push_into_stack_multi(rawlua)
|
||||
}))
|
||||
}
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
#[rustfmt::skip]
|
||||
UserDataTypeId::ArcParkingLotMutex(target_type_id)
|
||||
if try_self_arg!(rawlua.get_userdata_type_id::<Arc<parking_lot::Mutex<T>>>(self_index))
|
||||
== Some(target_type_id) =>
|
||||
{
|
||||
let ud = get_userdata::<UserDataStorage<Arc<parking_lot::Mutex<T>>>>(state, self_index);
|
||||
try_self_arg!((*ud).try_borrow_scoped(|ud| {
|
||||
let mut ud = ud.try_lock().ok_or(Error::UserDataBorrowMutError)?;
|
||||
method(rawlua.lua(), &mut ud, args?)?.push_into_stack_multi(rawlua)
|
||||
}))
|
||||
}
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
#[rustfmt::skip]
|
||||
UserDataTypeId::ArcParkingLotRwLock(target_type_id)
|
||||
if try_self_arg!(rawlua.get_userdata_type_id::<Arc<parking_lot::RwLock<T>>>(self_index))
|
||||
== Some(target_type_id) =>
|
||||
{
|
||||
let ud = get_userdata::<UserDataStorage<Arc<parking_lot::RwLock<T>>>>(state, self_index);
|
||||
try_self_arg!((*ud).try_borrow_scoped(|ud| {
|
||||
let mut ud = ud.try_write().ok_or(Error::UserDataBorrowMutError)?;
|
||||
method(rawlua.lua(), &mut ud, args?)?.push_into_stack_multi(rawlua)
|
||||
}))
|
||||
}
|
||||
_ => Err(Error::bad_self_argument(&name, Error::UserDataTypeMismatch)),
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -607,11 +775,14 @@ impl<T> UserDataMethods<T> for UserDataRegistry<T> {
|
||||
}
|
||||
|
||||
macro_rules! lua_userdata_impl {
|
||||
($type:ty) => {
|
||||
($type:ty => $type_variant:tt) => {
|
||||
lua_userdata_impl!($type, UserDataTypeId::$type_variant(TypeId::of::<$type>()));
|
||||
};
|
||||
|
||||
($type:ty, $type_id:expr) => {
|
||||
impl<T: UserData + 'static> UserData for $type {
|
||||
fn register(registry: &mut UserDataRegistry<Self>) {
|
||||
let type_id = TypeId::of::<T>();
|
||||
let mut orig_registry = UserDataRegistry::new(type_id);
|
||||
let mut orig_registry = UserDataRegistry::with_type_id($type_id);
|
||||
T::register(&mut orig_registry);
|
||||
|
||||
// Copy all fields, methods, etc. from the original registry
|
||||
@@ -635,4 +806,19 @@ macro_rules! lua_userdata_impl {
|
||||
// A special proxy object for UserData
|
||||
pub(crate) struct UserDataProxy<T>(pub(crate) PhantomData<T>);
|
||||
|
||||
lua_userdata_impl!(UserDataProxy<T>);
|
||||
lua_userdata_impl!(UserDataProxy<T>, UserDataTypeId::Shared(TypeId::of::<T>()));
|
||||
|
||||
#[cfg(all(feature = "userdata-wrappers", not(feature = "send")))]
|
||||
lua_userdata_impl!(Rc<T> => Rc);
|
||||
#[cfg(all(feature = "userdata-wrappers", not(feature = "send")))]
|
||||
lua_userdata_impl!(Rc<RefCell<T>> => RcRefCell);
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
lua_userdata_impl!(Arc<T> => Arc);
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
lua_userdata_impl!(Arc<Mutex<T>> => ArcMutex);
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
lua_userdata_impl!(Arc<RwLock<T>> => ArcRwLock);
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
lua_userdata_impl!(Arc<parking_lot::Mutex<T>> => ArcParkingLotMutex);
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
lua_userdata_impl!(Arc<parking_lot::RwLock<T>> => ArcParkingLotRwLock);
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
use std::cell::Cell;
|
||||
use std::marker::PhantomData;
|
||||
|
||||
// This is a trick to check if a type is `Sync` or not.
|
||||
// It uses leaked specialization feature from stdlib.
|
||||
struct IsSync<'a, T> {
|
||||
is_sync: &'a Cell<bool>,
|
||||
_marker: PhantomData<T>,
|
||||
}
|
||||
|
||||
impl<T> Clone for IsSync<'_, T> {
|
||||
fn clone(&self) -> Self {
|
||||
self.is_sync.set(false);
|
||||
IsSync {
|
||||
is_sync: self.is_sync,
|
||||
_marker: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Sync> Copy for IsSync<'_, T> {}
|
||||
|
||||
pub(crate) fn is_sync<T>() -> bool {
|
||||
let is_sync = Cell::new(true);
|
||||
let _ = [IsSync::<T> {
|
||||
is_sync: &is_sync,
|
||||
_marker: PhantomData,
|
||||
}]
|
||||
.clone();
|
||||
is_sync.get()
|
||||
}
|
||||
+30
-4
@@ -1,6 +1,6 @@
|
||||
use std::borrow::Cow;
|
||||
use std::ffi::CStr;
|
||||
use std::os::raw::{c_char, c_int};
|
||||
use std::os::raw::{c_char, c_int, c_void};
|
||||
use std::{ptr, slice, str};
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
@@ -21,9 +21,6 @@ pub(crate) use userdata::{
|
||||
pub(crate) use userdata::push_uninit_userdata;
|
||||
pub(crate) use userdata::push_userdata;
|
||||
|
||||
#[cfg(not(feature = "luau"))]
|
||||
pub(crate) use userdata::userdata_destructor;
|
||||
|
||||
// Checks that Lua has enough free stack space for future stack operations. On failure, this will
|
||||
// panic with an internal error message.
|
||||
#[inline]
|
||||
@@ -66,6 +63,11 @@ impl StackGuard {
|
||||
pub(crate) fn with_top(state: *mut ffi::lua_State, top: c_int) -> StackGuard {
|
||||
StackGuard { state, top }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn keep(&mut self, n: c_int) {
|
||||
self.top += n;
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for StackGuard {
|
||||
@@ -129,6 +131,15 @@ pub(crate) unsafe fn push_table(
|
||||
}
|
||||
}
|
||||
|
||||
// Uses 4 stack spaces, does not call checkstack.
|
||||
pub(crate) unsafe fn rawget_field(state: *mut ffi::lua_State, table: c_int, field: &str) -> Result<c_int> {
|
||||
ffi::lua_pushvalue(state, table);
|
||||
protect_lua!(state, 1, 1, |state| {
|
||||
ffi::lua_pushlstring(state, field.as_ptr() as *const c_char, field.len());
|
||||
ffi::lua_rawget(state, -2)
|
||||
})
|
||||
}
|
||||
|
||||
// Uses 4 stack spaces, does not call checkstack.
|
||||
pub(crate) unsafe fn rawset_field(state: *mut ffi::lua_State, table: c_int, field: &str) -> Result<()> {
|
||||
ffi::lua_pushvalue(state, table);
|
||||
@@ -282,6 +293,21 @@ pub(crate) unsafe fn to_string(state: *mut ffi::lua_State, index: c_int) -> Stri
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub(crate) unsafe fn get_metatable_ptr(state: *mut ffi::lua_State, index: c_int) -> *const c_void {
|
||||
#[cfg(feature = "luau")]
|
||||
return ffi::lua_getmetatablepointer(state, index);
|
||||
|
||||
#[cfg(not(feature = "luau"))]
|
||||
if ffi::lua_getmetatable(state, index) == 0 {
|
||||
ptr::null()
|
||||
} else {
|
||||
let p = ffi::lua_topointer(state, -1);
|
||||
ffi::lua_pop(state, 1);
|
||||
p
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn ptr_to_str<'a>(input: *const c_char) -> Option<&'a str> {
|
||||
if input.is_null() {
|
||||
return None;
|
||||
|
||||
+18
-35
@@ -3,7 +3,7 @@ use std::os::raw::{c_int, c_void};
|
||||
use std::{ptr, str};
|
||||
|
||||
use crate::error::Result;
|
||||
use crate::util::{check_stack, push_string, push_table, rawset_field, TypeKey};
|
||||
use crate::util::{check_stack, get_metatable_ptr, push_table, rawget_field, rawset_field, TypeKey};
|
||||
|
||||
// Pushes the userdata and attaches a metatable with __gc method.
|
||||
// Internally uses 3 stack spaces, does not call checkstack.
|
||||
@@ -58,25 +58,20 @@ pub(crate) unsafe fn init_internal_metatable<T: TypeKey>(
|
||||
pub(crate) unsafe fn get_internal_userdata<T: TypeKey>(
|
||||
state: *mut ffi::lua_State,
|
||||
index: c_int,
|
||||
type_mt_ptr: *const c_void,
|
||||
mut type_mt_ptr: *const c_void,
|
||||
) -> *mut T {
|
||||
let ud = ffi::lua_touserdata(state, index) as *mut T;
|
||||
if ud.is_null() || ffi::lua_getmetatable(state, index) == 0 {
|
||||
if ud.is_null() {
|
||||
return ptr::null_mut();
|
||||
}
|
||||
if !type_mt_ptr.is_null() {
|
||||
let ud_mt_ptr = ffi::lua_topointer(state, -1);
|
||||
ffi::lua_pop(state, 1);
|
||||
if ud_mt_ptr != type_mt_ptr {
|
||||
return ptr::null_mut();
|
||||
}
|
||||
} else {
|
||||
let mt_ptr = get_metatable_ptr(state, index);
|
||||
if type_mt_ptr.is_null() {
|
||||
get_internal_metatable::<T>(state);
|
||||
let res = ffi::lua_rawequal(state, -1, -2);
|
||||
ffi::lua_pop(state, 2);
|
||||
if res == 0 {
|
||||
return ptr::null_mut();
|
||||
}
|
||||
type_mt_ptr = ffi::lua_topointer(state, -1);
|
||||
ffi::lua_pop(state, 1);
|
||||
}
|
||||
if mt_ptr != type_mt_ptr {
|
||||
return ptr::null_mut();
|
||||
}
|
||||
ud
|
||||
}
|
||||
@@ -157,16 +152,12 @@ pub(crate) unsafe fn init_userdata_metatable(
|
||||
field_getters: Option<c_int>,
|
||||
field_setters: Option<c_int>,
|
||||
methods: Option<c_int>,
|
||||
extra_init: Option<fn(*mut ffi::lua_State) -> Result<()>>,
|
||||
) -> Result<()> {
|
||||
ffi::lua_pushvalue(state, metatable);
|
||||
|
||||
if field_getters.is_some() || methods.is_some() {
|
||||
// Push `__index` generator function
|
||||
init_userdata_metatable_index(state)?;
|
||||
|
||||
push_string(state, b"__index", true)?;
|
||||
let index_type = ffi::lua_rawget(state, -3);
|
||||
let index_type = rawget_field(state, metatable, "__index")?;
|
||||
match index_type {
|
||||
ffi::LUA_TNIL | ffi::LUA_TTABLE | ffi::LUA_TFUNCTION => {
|
||||
for &idx in &[field_getters, methods] {
|
||||
@@ -180,39 +171,31 @@ pub(crate) unsafe fn init_userdata_metatable(
|
||||
// Generate `__index`
|
||||
protect_lua!(state, 4, 1, fn(state) ffi::lua_call(state, 3, 1))?;
|
||||
}
|
||||
_ => mlua_panic!("improper __index type {}", index_type),
|
||||
_ => mlua_panic!("improper `__index` type: {}", index_type),
|
||||
}
|
||||
|
||||
rawset_field(state, -2, "__index")?;
|
||||
rawset_field(state, metatable, "__index")?;
|
||||
}
|
||||
|
||||
if let Some(field_setters) = field_setters {
|
||||
// Push `__newindex` generator function
|
||||
init_userdata_metatable_newindex(state)?;
|
||||
|
||||
push_string(state, b"__newindex", true)?;
|
||||
let newindex_type = ffi::lua_rawget(state, -3);
|
||||
let newindex_type = rawget_field(state, metatable, "__newindex")?;
|
||||
match newindex_type {
|
||||
ffi::LUA_TNIL | ffi::LUA_TTABLE | ffi::LUA_TFUNCTION => {
|
||||
ffi::lua_pushvalue(state, field_setters);
|
||||
// Generate `__newindex`
|
||||
protect_lua!(state, 3, 1, fn(state) ffi::lua_call(state, 2, 1))?;
|
||||
}
|
||||
_ => mlua_panic!("improper __newindex type {}", newindex_type),
|
||||
_ => mlua_panic!("improper `__newindex` type: {}", newindex_type),
|
||||
}
|
||||
|
||||
rawset_field(state, -2, "__newindex")?;
|
||||
}
|
||||
|
||||
// Additional initialization
|
||||
if let Some(extra_init) = extra_init {
|
||||
extra_init(state)?;
|
||||
rawset_field(state, metatable, "__newindex")?;
|
||||
}
|
||||
|
||||
ffi::lua_pushboolean(state, 0);
|
||||
rawset_field(state, -2, "__metatable")?;
|
||||
|
||||
ffi::lua_pop(state, 1);
|
||||
rawset_field(state, metatable, "__metatable")?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -356,7 +339,7 @@ unsafe fn init_userdata_metatable_newindex(state: *mut ffi::lua_State) -> Result
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "luau"))]
|
||||
pub(crate) unsafe extern "C-unwind" fn userdata_destructor<T>(state: *mut ffi::lua_State) -> c_int {
|
||||
unsafe extern "C-unwind" fn userdata_destructor<T>(state: *mut ffi::lua_State) -> c_int {
|
||||
// It's probably NOT a good idea to catch Rust panics in finalizer
|
||||
// Lua 5.4 ignores it, other versions generates `LUA_ERRGCMM` without calling message handler
|
||||
take_userdata::<T>(state);
|
||||
|
||||
+24
-20
@@ -104,15 +104,15 @@ impl Value {
|
||||
/// Compares two values for equality.
|
||||
///
|
||||
/// Equality comparisons do not convert strings to numbers or vice versa.
|
||||
/// Tables, Functions, Threads, and Userdata are compared by reference:
|
||||
/// Tables, functions, threads, and userdata are compared by reference:
|
||||
/// two objects are considered equal only if they are the same object.
|
||||
///
|
||||
/// If Tables or Userdata have `__eq` metamethod then mlua will try to invoke it.
|
||||
/// If table or userdata have `__eq` metamethod then mlua will try to invoke it.
|
||||
/// The first value is checked first. If that value does not define a metamethod
|
||||
/// for `__eq`, then mlua will check the second value.
|
||||
/// Then mlua calls the metamethod with the two values as arguments, if found.
|
||||
pub fn equals<T: AsRef<Self>>(&self, other: T) -> Result<bool> {
|
||||
match (self, other.as_ref()) {
|
||||
pub fn equals(&self, other: &Self) -> Result<bool> {
|
||||
match (self, other) {
|
||||
(Value::Table(a), Value::Table(b)) => a.equals(b),
|
||||
(Value::UserData(a), Value::UserData(b)) => a.equals(b),
|
||||
(a, b) => Ok(a == b),
|
||||
@@ -129,9 +129,14 @@ impl Value {
|
||||
#[inline]
|
||||
pub fn to_pointer(&self) -> *const c_void {
|
||||
match self {
|
||||
Value::String(String(vref)) => {
|
||||
// In Lua < 5.4 (excluding Luau), string pointers are NULL
|
||||
// Use alternative approach
|
||||
let lua = vref.lua.lock();
|
||||
unsafe { ffi::lua_tostring(lua.ref_thread(), vref.index) as *const c_void }
|
||||
}
|
||||
Value::LightUserData(ud) => ud.0,
|
||||
Value::String(String(vref))
|
||||
| Value::Table(Table(vref))
|
||||
Value::Table(Table(vref))
|
||||
| Value::Function(Function(vref))
|
||||
| Value::Thread(Thread(vref, ..))
|
||||
| Value::UserData(AnyUserData(vref))
|
||||
@@ -188,6 +193,8 @@ impl Value {
|
||||
}
|
||||
|
||||
/// Returns `true` if the value is a [`NULL`].
|
||||
///
|
||||
/// [`NULL`]: Value::NULL
|
||||
#[inline]
|
||||
pub fn is_null(&self) -> bool {
|
||||
self == &Self::NULL
|
||||
@@ -428,9 +435,11 @@ impl Value {
|
||||
}
|
||||
}
|
||||
|
||||
/// Cast the value to a `Buffer`.
|
||||
/// Cast the value to a [`Buffer`].
|
||||
///
|
||||
/// If the value is `Buffer`, returns it or `None` otherwise.
|
||||
/// If the value is [`Buffer`], returns it or `None` otherwise.
|
||||
///
|
||||
/// [`Buffer`]: crate::Buffer
|
||||
#[cfg(any(feature = "luau", doc))]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
|
||||
#[inline]
|
||||
@@ -441,7 +450,9 @@ impl Value {
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `true` if the value is a `Buffer`.
|
||||
/// Returns `true` if the value is a [`Buffer`].
|
||||
///
|
||||
/// [`Buffer`]: crate::Buffer
|
||||
#[cfg(any(feature = "luau", doc))]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
|
||||
#[inline]
|
||||
@@ -501,9 +512,9 @@ impl Value {
|
||||
(_, Value::Boolean(_)) => Ordering::Greater,
|
||||
// Integer && Number
|
||||
(Value::Integer(a), Value::Integer(b)) => a.cmp(b),
|
||||
(&Value::Integer(a), &Value::Number(b)) => cmp_num(a as Number, b),
|
||||
(&Value::Number(a), &Value::Integer(b)) => cmp_num(a, b as Number),
|
||||
(&Value::Number(a), &Value::Number(b)) => cmp_num(a, b),
|
||||
(Value::Integer(a), Value::Number(b)) => cmp_num(*a as Number, *b),
|
||||
(Value::Number(a), Value::Integer(b)) => cmp_num(*a, *b as Number),
|
||||
(Value::Number(a), Value::Number(b)) => cmp_num(*a, *b),
|
||||
(Value::Integer(_) | Value::Number(_), _) => Ordering::Less,
|
||||
(_, Value::Integer(_) | Value::Number(_)) => Ordering::Greater,
|
||||
// Vector (Luau)
|
||||
@@ -610,13 +621,6 @@ impl PartialEq for Value {
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<Value> for Value {
|
||||
#[inline]
|
||||
fn as_ref(&self) -> &Self {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
/// A wrapped [`Value`] with customized serialization behavior.
|
||||
#[cfg(feature = "serialize")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "serialize")))]
|
||||
@@ -691,7 +695,7 @@ impl<'a> SerializableValue<'a> {
|
||||
}
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
impl<'a> Serialize for SerializableValue<'a> {
|
||||
impl Serialize for SerializableValue<'_> {
|
||||
fn serialize<S>(&self, serializer: S) -> StdResult<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
use std::fmt;
|
||||
|
||||
#[cfg(all(any(feature = "luau", doc), feature = "serialize"))]
|
||||
#[cfg(feature = "serialize")]
|
||||
use serde::ser::{Serialize, SerializeTupleStruct, Serializer};
|
||||
|
||||
use super::LuaType;
|
||||
|
||||
/// A Luau vector type.
|
||||
///
|
||||
/// By default vectors are 3-dimensional, but can be 4-dimensional
|
||||
@@ -23,6 +21,7 @@ impl fmt::Display for Vector {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg_attr(not(feature = "luau"), allow(unused))]
|
||||
impl Vector {
|
||||
pub(crate) const SIZE: usize = if cfg!(feature = "luau-vector4") { 4 } else { 3 };
|
||||
|
||||
@@ -67,7 +66,7 @@ impl Vector {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(any(feature = "luau", doc), feature = "serialize"))]
|
||||
#[cfg(feature = "serialize")]
|
||||
impl Serialize for Vector {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error> {
|
||||
let mut ts = serializer.serialize_tuple_struct("Vector", Self::SIZE)?;
|
||||
@@ -87,11 +86,7 @@ impl PartialEq<[f32; Self::SIZE]> for Vector {
|
||||
}
|
||||
}
|
||||
|
||||
impl LuaType for Vector {
|
||||
#[cfg(feature = "luau")]
|
||||
const TYPE_ID: i32 = ffi::LUA_TVECTOR;
|
||||
|
||||
// This is a dummy value, as `Vector` is supported only by Luau
|
||||
#[cfg(not(feature = "luau"))]
|
||||
const TYPE_ID: i32 = ffi::LUA_TNONE;
|
||||
#[cfg(feature = "luau")]
|
||||
impl crate::types::LuaType for Vector {
|
||||
const TYPE_ID: std::os::raw::c_int = ffi::LUA_TVECTOR;
|
||||
}
|
||||
+11
-8
@@ -1,20 +1,23 @@
|
||||
[lua54_coverage]
|
||||
features = "lua54,vendored,async,send,serialize,macros"
|
||||
[lua54]
|
||||
features = "lua54,vendored,async,send,serialize,macros,anyhow,userdata-wrappers"
|
||||
|
||||
[lua54_with_memory_limit_coverage]
|
||||
features = "lua54,vendored,async,send,serialize,macros"
|
||||
[lua54_non_send]
|
||||
features = "lua54,vendored,async,serialize,macros,anyhow,userdata-wrappers"
|
||||
|
||||
[lua54_with_memory_limit]
|
||||
features = "lua54,vendored,async,send,serialize,macros,anyhow,userdata-wrappers"
|
||||
rustflags = "--cfg force_memory_limit"
|
||||
|
||||
[lua51_coverage]
|
||||
[lua51]
|
||||
features = "lua51,vendored,async,send,serialize,macros"
|
||||
|
||||
[lua51_with_memory_limit_coverage]
|
||||
[lua51_with_memory_limit]
|
||||
features = "lua51,vendored,async,send,serialize,macros"
|
||||
rustflags = "--cfg force_memory_limit"
|
||||
|
||||
[luau_coverage]
|
||||
[luau]
|
||||
features = "luau,async,send,serialize,macros"
|
||||
|
||||
[luau_with_memory_limit_coverage]
|
||||
[luau_with_memory_limit]
|
||||
features = "luau,async,send,serialize,macros"
|
||||
rustflags = "--cfg force_memory_limit"
|
||||
|
||||
@@ -48,6 +48,19 @@ async fn test_async_function_wrap() -> Result<()> {
|
||||
let res: String = lua.load(r#"f("hello")"#).eval_async().await?;
|
||||
assert_eq!(res, "hello");
|
||||
|
||||
// Return error
|
||||
let ferr = Function::wrap_async(|| async move { Err::<(), _>(Error::runtime("some async error")) });
|
||||
lua.globals().set("ferr", ferr)?;
|
||||
lua.load(
|
||||
r#"
|
||||
local ok, err = pcall(ferr)
|
||||
assert(not ok and tostring(err):find("some async error"))
|
||||
"#,
|
||||
)
|
||||
.exec_async()
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -376,6 +389,13 @@ async fn test_async_table_object_like() -> Result<()> {
|
||||
table.set_metatable(Some(metatable));
|
||||
assert_eq!(table.call_async::<i64>(()).await.unwrap(), 15);
|
||||
|
||||
match table.call_async_method::<()>("non_existent", ()).await {
|
||||
Err(Error::RuntimeError(err)) => {
|
||||
assert!(err.contains("attempt to call a nil value (function 'non_existent')"))
|
||||
}
|
||||
r => panic!("expected RuntimeError, got {r:?}"),
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
@@ -35,6 +35,7 @@ fn test_buffer() -> Result<()> {
|
||||
|
||||
let buf3 = lua.create_buffer(b"")?;
|
||||
assert!(buf3.is_empty());
|
||||
assert!(!Value::Buffer(buf3).to_pointer().is_null());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
+54
-1
@@ -19,7 +19,7 @@ fn test_chunk_path() -> Result<()> {
|
||||
return 321
|
||||
"#,
|
||||
)?;
|
||||
let i: i32 = lua.load(&*temp_dir.path().join("module.lua")).eval()?;
|
||||
let i: i32 = lua.load(temp_dir.path().join("module.lua")).eval()?;
|
||||
assert_eq!(i, 321);
|
||||
|
||||
match lua.load(&*temp_dir.path().join("module2.lua")).exec() {
|
||||
@@ -27,6 +27,30 @@ fn test_chunk_path() -> Result<()> {
|
||||
res => panic!("expected io::Error, got {:?}", res),
|
||||
};
|
||||
|
||||
// &Path
|
||||
assert_eq!(
|
||||
(lua.load(&*temp_dir.path().join("module.lua").as_path())).eval::<i32>()?,
|
||||
321
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_chunk_impls() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
// StdString
|
||||
assert_eq!(lua.load(String::from("1")).eval::<i32>()?, 1);
|
||||
assert_eq!(lua.load(&String::from("2")).eval::<i32>()?, 2);
|
||||
|
||||
// &[u8]
|
||||
assert_eq!(lua.load(&b"3"[..]).eval::<i32>()?, 3);
|
||||
|
||||
// Vec<u8>
|
||||
assert_eq!(lua.load(b"4".to_vec()).eval::<i32>()?, 4);
|
||||
assert_eq!(lua.load(&b"5".to_vec()).eval::<i32>()?, 5);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -68,3 +92,32 @@ fn test_chunk_macro() -> Result<()> {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(feature = "luau")]
|
||||
#[test]
|
||||
fn test_compiler() -> Result<()> {
|
||||
use std::vec;
|
||||
|
||||
let compiler = mlua::Compiler::new()
|
||||
.set_optimization_level(2)
|
||||
.set_debug_level(2)
|
||||
.set_type_info_level(1)
|
||||
.set_coverage_level(2)
|
||||
.set_vector_lib("vector")
|
||||
.set_vector_ctor("new")
|
||||
.set_vector_type("vector")
|
||||
.set_mutable_globals(vec!["mutable_global".into()])
|
||||
.set_userdata_types(vec!["MyUserdata".into()]);
|
||||
|
||||
assert!(compiler.compile("return vector.new(1, 2, 3)").is_ok());
|
||||
|
||||
// Error
|
||||
match compiler.compile("%") {
|
||||
Err(mlua::Error::SyntaxError { ref message, .. }) => {
|
||||
assert!(message.contains("Expected identifier when parsing expression, got '%'"),);
|
||||
}
|
||||
res => panic!("expected result: {res:?}"),
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
+114
-3
@@ -94,6 +94,21 @@ fn test_function_into_lua() -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_function_from_lua() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
assert!(lua.globals().get::<Function>("print").is_ok());
|
||||
match lua.globals().get::<Function>("math") {
|
||||
Err(err @ Error::FromLuaConversionError { .. }) => {
|
||||
assert_eq!(err.to_string(), "error converting Lua table to function");
|
||||
}
|
||||
_ => panic!("expected `Error::FromLuaConversionError`"),
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_thread_into_lua() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
@@ -112,6 +127,20 @@ fn test_thread_into_lua() -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_thread_from_lua() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
match lua.globals().get::<Thread>("print") {
|
||||
Err(err @ Error::FromLuaConversionError { .. }) => {
|
||||
assert_eq!(err.to_string(), "error converting Lua function to thread");
|
||||
}
|
||||
_ => panic!("expected `Error::FromLuaConversionError`"),
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_anyuserdata_into_lua() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
@@ -130,6 +159,45 @@ fn test_anyuserdata_into_lua() -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_anyuserdata_from_lua() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
match lua.globals().get::<AnyUserData>("print") {
|
||||
Err(err @ Error::FromLuaConversionError { .. }) => {
|
||||
assert_eq!(err.to_string(), "error converting Lua function to userdata");
|
||||
}
|
||||
_ => panic!("expected `Error::FromLuaConversionError`"),
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_error_conversion() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
// Any Lua value can be converted to `Error`
|
||||
match lua.convert::<Error>(Error::external("external error")) {
|
||||
Ok(Error::ExternalError(msg)) => assert_eq!(msg.to_string(), "external error"),
|
||||
res => panic!("expected `Error::ExternalError`, got {res:?}"),
|
||||
}
|
||||
match lua.convert::<Error>("abc") {
|
||||
Ok(Error::RuntimeError(msg)) => assert_eq!(msg, "abc"),
|
||||
res => panic!("expected `Error::RuntimeError`, got {res:?}"),
|
||||
}
|
||||
match lua.convert::<Error>(true) {
|
||||
Ok(Error::RuntimeError(msg)) => assert_eq!(msg, "true"),
|
||||
res => panic!("expected `Error::RuntimeError`, got {res:?}"),
|
||||
}
|
||||
match lua.convert::<Error>(lua.globals()) {
|
||||
Ok(Error::RuntimeError(msg)) => assert!(msg.starts_with("table:")),
|
||||
res => panic!("expected `Error::RuntimeError`, got {res:?}"),
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_registry_value_into_lua() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
@@ -140,7 +208,7 @@ fn test_registry_value_into_lua() -> Result<()> {
|
||||
let value1 = lua.pack(&r)?;
|
||||
let value2 = lua.pack(r)?;
|
||||
assert_eq!(value1.as_str().as_deref(), Some("hello, world"));
|
||||
assert_eq!(value2.to_pointer(), value2.to_pointer());
|
||||
assert_eq!(value1.to_pointer(), value2.to_pointer());
|
||||
|
||||
// Push into stack
|
||||
let t = lua.create_table()?;
|
||||
@@ -175,6 +243,32 @@ fn test_registry_key_from_lua() -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_bool_into_lua() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
// Direct conversion
|
||||
assert!(true.into_lua(&lua)?.is_boolean());
|
||||
|
||||
// Push into stack
|
||||
let table = lua.create_table()?;
|
||||
table.set("b", true)?;
|
||||
assert_eq!(true, table.get::<bool>("b")?);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_bool_from_lua() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
assert!(lua.globals().get::<bool>("print")?);
|
||||
assert!(lua.convert::<bool>(123)?);
|
||||
assert!(!lua.convert::<bool>(Value::Nil)?);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_integer_from_lua() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
@@ -474,6 +568,7 @@ fn test_either_enum() -> Result<()> {
|
||||
*either.as_mut().left().unwrap() = 44;
|
||||
assert_eq!(*either.as_ref().left().unwrap(), 44);
|
||||
assert_eq!(format!("{either}"), "44");
|
||||
assert_eq!(either.right(), None);
|
||||
|
||||
// Right
|
||||
either = Either::Right("hello".to_string());
|
||||
@@ -482,6 +577,7 @@ fn test_either_enum() -> Result<()> {
|
||||
*either.as_mut().right().unwrap() = "world".to_string();
|
||||
assert_eq!(*either.as_ref().right().unwrap(), "world");
|
||||
assert_eq!(format!("{either}"), "world");
|
||||
assert_eq!(either.left(), None);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -492,8 +588,10 @@ fn test_either_into_lua() -> Result<()> {
|
||||
|
||||
// Direct conversion
|
||||
let mut either = Either::<i32, &Table>::Left(42);
|
||||
let value = either.into_lua(&lua)?;
|
||||
assert_eq!(value, Value::Integer(42));
|
||||
assert_eq!(either.into_lua(&lua)?, Value::Integer(42));
|
||||
let t = lua.create_table()?;
|
||||
either = Either::Right(&t);
|
||||
assert!(matches!(either.into_lua(&lua)?, Value::Table(_)));
|
||||
|
||||
// Push into stack
|
||||
let f =
|
||||
@@ -514,6 +612,19 @@ fn test_either_into_lua() -> Result<()> {
|
||||
fn test_either_from_lua() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
// From value
|
||||
let mut either = lua.unpack::<Either<i32, Table>>(Value::Integer(42))?;
|
||||
assert!(either.is_left());
|
||||
assert_eq!(*either.as_ref().left().unwrap(), 42);
|
||||
let t = lua.create_table()?;
|
||||
either = lua.unpack::<Either<i32, Table>>(Value::Table(t.clone()))?;
|
||||
assert!(either.is_right());
|
||||
assert_eq!(either.as_ref().right().unwrap(), &t);
|
||||
match lua.unpack::<Either<i32, Table>>(Value::String(lua.create_string("abc")?)) {
|
||||
Err(Error::FromLuaConversionError { to, .. }) => assert_eq!(to, "Either<i32, Table>"),
|
||||
_ => panic!("expected `Error::FromLuaConversionError`"),
|
||||
}
|
||||
|
||||
// From stack
|
||||
let f = lua.create_function(|_, either: Either<i32, Table>| Ok(either))?;
|
||||
let either = f.call::<Either<i32, Table>>(42)?;
|
||||
|
||||
+1
-1
@@ -7,7 +7,7 @@ fn test_debug_format() -> Result<()> {
|
||||
// Globals
|
||||
let globals = lua.globals();
|
||||
let dump = format!("{globals:#?}");
|
||||
assert!(dump.starts_with("{\n [\"_G\"] = table:"));
|
||||
assert!(dump.starts_with("{\n _G = table:"));
|
||||
|
||||
// TODO: Other cases
|
||||
|
||||
|
||||
+14
-10
@@ -1,4 +1,5 @@
|
||||
use std::io;
|
||||
use std::error::Error as _;
|
||||
use std::{fmt, io};
|
||||
|
||||
use mlua::{Error, ErrorContext, Lua, Result};
|
||||
|
||||
@@ -27,7 +28,6 @@ fn test_error_context() -> Result<()> {
|
||||
.load("local _, err = pcall(func2); return tostring(err)")
|
||||
.eval::<String>()?;
|
||||
assert!(msg2.contains("failed to find global"));
|
||||
println!("{msg2}");
|
||||
assert!(msg2.contains("error converting Lua nil to String"));
|
||||
|
||||
// Rewrite context message and test `downcast_ref`
|
||||
@@ -36,13 +36,12 @@ fn test_error_context() -> Result<()> {
|
||||
.context("some context")
|
||||
.context("some new context")
|
||||
})?;
|
||||
let res = func3.call::<()>(()).err().unwrap();
|
||||
let Error::CallbackError { cause, .. } = &res else {
|
||||
unreachable!()
|
||||
};
|
||||
assert!(!res.to_string().contains("some context"));
|
||||
assert!(res.to_string().contains("some new context"));
|
||||
assert!(cause.downcast_ref::<io::Error>().is_some());
|
||||
let err = func3.call::<()>(()).unwrap_err();
|
||||
let err = err.parent().unwrap();
|
||||
assert!(!err.to_string().contains("some context"));
|
||||
assert!(err.to_string().contains("some new context"));
|
||||
assert!(err.downcast_ref::<io::Error>().is_some());
|
||||
assert!(err.downcast_ref::<fmt::Error>().is_none());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -59,7 +58,7 @@ fn test_error_chain() -> Result<()> {
|
||||
let err = Error::external(io::Error::new(io::ErrorKind::Other, "other")).context("io error");
|
||||
Err::<(), _>(err)
|
||||
})?;
|
||||
let err = func.call::<()>(()).err().unwrap();
|
||||
let err = func.call::<()>(()).unwrap_err();
|
||||
assert_eq!(err.chain().count(), 3);
|
||||
for (i, err) in err.chain().enumerate() {
|
||||
match i {
|
||||
@@ -70,6 +69,11 @@ fn test_error_chain() -> Result<()> {
|
||||
}
|
||||
}
|
||||
|
||||
let err = err.parent().unwrap();
|
||||
assert!(err.source().is_none()); // The source is included to the `Display` output
|
||||
assert!(err.to_string().contains("io error"));
|
||||
assert!(err.to_string().contains("other"));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
+121
-70
@@ -1,27 +1,34 @@
|
||||
use mlua::{Error, Function, Lua, Result, String, Table};
|
||||
use mlua::{Error, Function, Lua, Result, String, Table, Variadic};
|
||||
|
||||
#[test]
|
||||
fn test_function() -> Result<()> {
|
||||
fn test_function_call() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let globals = lua.globals();
|
||||
lua.load(
|
||||
r#"
|
||||
function concat(arg1, arg2)
|
||||
return arg1 .. arg2
|
||||
end
|
||||
"#,
|
||||
)
|
||||
.exec()?;
|
||||
|
||||
let concat = globals.get::<Function>("concat")?;
|
||||
let concat = lua
|
||||
.load(r#"function(arg1, arg2) return arg1 .. arg2 end"#)
|
||||
.eval::<Function>()?;
|
||||
assert_eq!(concat.call::<String>(("foo", "bar"))?, "foobar");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_bind() -> Result<()> {
|
||||
fn test_function_call_error() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let concat_err = lua
|
||||
.load(r#"function(arg1, arg2) error("concat error") end"#)
|
||||
.eval::<Function>()?;
|
||||
match concat_err.call::<String>(("foo", "bar")) {
|
||||
Err(Error::RuntimeError(msg)) if msg.contains("concat error") => {}
|
||||
other => panic!("unexpected result: {other:?}"),
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_function_bind() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let globals = lua.globals();
|
||||
@@ -54,59 +61,13 @@ fn test_bind() -> Result<()> {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_rust_function() -> Result<()> {
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
fn test_function_bind_error() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let globals = lua.globals();
|
||||
lua.load(
|
||||
r#"
|
||||
function lua_function()
|
||||
return rust_function()
|
||||
end
|
||||
|
||||
-- Test to make sure chunk return is ignored
|
||||
return 1
|
||||
"#,
|
||||
)
|
||||
.exec()?;
|
||||
|
||||
let lua_function = globals.get::<Function>("lua_function")?;
|
||||
let rust_function = lua.create_function(|_, ()| Ok("hello"))?;
|
||||
|
||||
globals.set("rust_function", rust_function)?;
|
||||
assert_eq!(lua_function.call::<String>(())?, "hello");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_c_function() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
unsafe extern "C-unwind" fn c_function(state: *mut mlua::lua_State) -> std::os::raw::c_int {
|
||||
ffi::lua_pushboolean(state, 1);
|
||||
ffi::lua_setglobal(state, b"c_function\0" as *const _ as *const _);
|
||||
0
|
||||
}
|
||||
|
||||
let func = unsafe { lua.create_c_function(c_function)? };
|
||||
func.call::<()>(())?;
|
||||
assert_eq!(lua.globals().get::<bool>("c_function")?, true);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "luau"))]
|
||||
#[test]
|
||||
fn test_dump() -> Result<()> {
|
||||
let lua = unsafe { Lua::unsafe_new() };
|
||||
|
||||
let concat_lua = lua
|
||||
.load(r#"function(arg1, arg2) return arg1 .. arg2 end"#)
|
||||
.eval::<Function>()?;
|
||||
let concat = lua.load(&concat_lua.dump(false)).into_function()?;
|
||||
|
||||
assert_eq!(concat.call::<String>(("foo", "bar"))?, "foobar");
|
||||
let func = lua.load(r#"function(...) end"#).eval::<Function>()?;
|
||||
assert!(func.bind(Variadic::from_iter(1..1000000)).is_err());
|
||||
assert!(func.call::<()>(Variadic::from_iter(1..1000000)).is_err());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -114,14 +75,15 @@ fn test_dump() -> Result<()> {
|
||||
#[test]
|
||||
fn test_function_environment() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
let globals = lua.globals();
|
||||
|
||||
// We must not get or set environment for C functions
|
||||
let rust_func = lua.create_function(|_, ()| Ok("hello"))?;
|
||||
assert_eq!(rust_func.environment(), None);
|
||||
assert_eq!(rust_func.set_environment(lua.globals()).ok(), Some(false));
|
||||
assert_eq!(rust_func.set_environment(globals.clone()).ok(), Some(false));
|
||||
|
||||
// Test getting Lua function environment
|
||||
lua.globals().set("hello", "global")?;
|
||||
globals.set("hello", "global")?;
|
||||
let lua_func = lua
|
||||
.load(
|
||||
r#"
|
||||
@@ -135,7 +97,7 @@ fn test_function_environment() -> Result<()> {
|
||||
.eval::<Function>()?;
|
||||
let lua_func2 = lua.load("return hello").into_function()?;
|
||||
assert_eq!(lua_func.call::<String>(())?, "global");
|
||||
assert_eq!(lua_func.environment(), Some(lua.globals()));
|
||||
assert_eq!(lua_func.environment().as_ref(), Some(&globals));
|
||||
|
||||
// Test changing the environment
|
||||
let env = lua.create_table_from([("hello", "local")])?;
|
||||
@@ -154,9 +116,9 @@ fn test_function_environment() -> Result<()> {
|
||||
"#,
|
||||
)
|
||||
.exec()?;
|
||||
let lucky = lua.globals().get::<Function>("lucky")?;
|
||||
let lucky = globals.get::<Function>("lucky")?;
|
||||
assert_eq!(lucky.call::<String>(())?, "number is 15");
|
||||
let new_env = lua.globals().get::<Table>("new_env")?;
|
||||
let new_env = globals.get::<Table>("new_env")?;
|
||||
lucky.set_environment(new_env)?;
|
||||
assert_eq!(lucky.call::<String>(())?, "15");
|
||||
|
||||
@@ -235,6 +197,95 @@ fn test_function_info() -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "luau"))]
|
||||
#[test]
|
||||
fn test_function_dump() -> Result<()> {
|
||||
let lua = unsafe { Lua::unsafe_new() };
|
||||
|
||||
let concat_lua = lua
|
||||
.load(r#"function(arg1, arg2) return arg1 .. arg2 end"#)
|
||||
.eval::<Function>()?;
|
||||
let concat = lua.load(&concat_lua.dump(false)).into_function()?;
|
||||
|
||||
assert_eq!(concat.call::<String>(("foo", "bar"))?, "foobar");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(feature = "luau")]
|
||||
#[test]
|
||||
fn test_finction_coverage() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
lua.set_compiler(mlua::Compiler::default().set_coverage_level(1));
|
||||
|
||||
let f = lua
|
||||
.load(
|
||||
r#"local s = "abc"
|
||||
assert(#s == 3)
|
||||
|
||||
function abc(i)
|
||||
if i < 5 then
|
||||
return 0
|
||||
else
|
||||
return 1
|
||||
end
|
||||
end
|
||||
|
||||
(function()
|
||||
(function() abc(10) end)()
|
||||
end)()
|
||||
"#,
|
||||
)
|
||||
.into_function()?;
|
||||
|
||||
f.call::<()>(())?;
|
||||
|
||||
let mut report = Vec::new();
|
||||
f.coverage(|cov| {
|
||||
report.push(cov);
|
||||
});
|
||||
|
||||
assert_eq!(
|
||||
report[0],
|
||||
mlua::CoverageInfo {
|
||||
function: None,
|
||||
line_defined: 1,
|
||||
depth: 0,
|
||||
hits: vec![-1, 1, 1, -1, 1, -1, -1, -1, -1, -1, -1, -1, 1, -1, -1, -1],
|
||||
}
|
||||
);
|
||||
assert_eq!(
|
||||
report[1],
|
||||
mlua::CoverageInfo {
|
||||
function: Some("abc".into()),
|
||||
line_defined: 4,
|
||||
depth: 1,
|
||||
hits: vec![-1, -1, -1, -1, -1, 1, 0, -1, 1, -1, -1, -1, -1, -1, -1, -1],
|
||||
}
|
||||
);
|
||||
assert_eq!(
|
||||
report[2],
|
||||
mlua::CoverageInfo {
|
||||
function: None,
|
||||
line_defined: 12,
|
||||
depth: 1,
|
||||
hits: vec![-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, -1, -1],
|
||||
}
|
||||
);
|
||||
assert_eq!(
|
||||
report[3],
|
||||
mlua::CoverageInfo {
|
||||
function: None,
|
||||
line_defined: 13,
|
||||
depth: 2,
|
||||
hits: vec![-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, -1, -1],
|
||||
}
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_function_pointer() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
+15
-87
@@ -6,10 +6,7 @@ use std::panic::{catch_unwind, AssertUnwindSafe};
|
||||
use std::sync::atomic::{AtomicU64, Ordering};
|
||||
use std::sync::Arc;
|
||||
|
||||
use mlua::{
|
||||
Compiler, CoverageInfo, Error, Lua, LuaOptions, Result, StdLib, Table, ThreadStatus, Value, Vector,
|
||||
VmState,
|
||||
};
|
||||
use mlua::{Compiler, Error, Lua, LuaOptions, Result, StdLib, Table, ThreadStatus, Value, Vector, VmState};
|
||||
|
||||
#[test]
|
||||
fn test_version() -> Result<()> {
|
||||
@@ -100,17 +97,19 @@ fn test_require() -> Result<()> {
|
||||
fn test_vectors() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let v: Vector = lua.load("vector(1, 2, 3) + vector(3, 2, 1)").eval()?;
|
||||
let v: Vector = lua
|
||||
.load("vector.create(1, 2, 3) + vector.create(3, 2, 1)")
|
||||
.eval()?;
|
||||
assert_eq!(v, [4.0, 4.0, 4.0]);
|
||||
|
||||
// Test conversion into Rust array
|
||||
let v: [f64; 3] = lua.load("vector(1, 2, 3)").eval()?;
|
||||
let v: [f64; 3] = lua.load("vector.create(1, 2, 3)").eval()?;
|
||||
assert!(v == [1.0, 2.0, 3.0]);
|
||||
|
||||
// Test vector methods
|
||||
lua.load(
|
||||
r#"
|
||||
local v = vector(1, 2, 3)
|
||||
local v = vector.create(1, 2, 3)
|
||||
assert(v.x == 1)
|
||||
assert(v.y == 2)
|
||||
assert(v.z == 3)
|
||||
@@ -121,7 +120,7 @@ fn test_vectors() -> Result<()> {
|
||||
// Test vector methods (fastcall)
|
||||
lua.load(
|
||||
r#"
|
||||
local v = vector(1, 2, 3)
|
||||
local v = vector.create(1, 2, 3)
|
||||
assert(v.x == 1)
|
||||
assert(v.y == 2)
|
||||
assert(v.z == 3)
|
||||
@@ -138,17 +137,19 @@ fn test_vectors() -> Result<()> {
|
||||
fn test_vectors() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let v: Vector = lua.load("vector(1, 2, 3, 4) + vector(4, 3, 2, 1)").eval()?;
|
||||
let v: Vector = lua
|
||||
.load("vector.create(1, 2, 3, 4) + vector.create(4, 3, 2, 1)")
|
||||
.eval()?;
|
||||
assert_eq!(v, [5.0, 5.0, 5.0, 5.0]);
|
||||
|
||||
// Test conversion into Rust array
|
||||
let v: [f64; 4] = lua.load("vector(1, 2, 3, 4)").eval()?;
|
||||
let v: [f64; 4] = lua.load("vector.create(1, 2, 3, 4)").eval()?;
|
||||
assert!(v == [1.0, 2.0, 3.0, 4.0]);
|
||||
|
||||
// Test vector methods
|
||||
lua.load(
|
||||
r#"
|
||||
local v = vector(1, 2, 3, 4)
|
||||
local v = vector.create(1, 2, 3, 4)
|
||||
assert(v.x == 1)
|
||||
assert(v.y == 2)
|
||||
assert(v.z == 3)
|
||||
@@ -160,7 +161,7 @@ fn test_vectors() -> Result<()> {
|
||||
// Test vector methods (fastcall)
|
||||
lua.load(
|
||||
r#"
|
||||
local v = vector(1, 2, 3, 4)
|
||||
local v = vector.create(1, 2, 3, 4)
|
||||
assert(v.x == 1)
|
||||
assert(v.y == 2)
|
||||
assert(v.z == 3)
|
||||
@@ -183,10 +184,10 @@ fn test_vector_metatable() -> Result<()> {
|
||||
r#"
|
||||
{
|
||||
__index = {
|
||||
new = vector,
|
||||
new = vector.create,
|
||||
|
||||
product = function(a, b)
|
||||
return vector(a.x * b.x, a.y * b.y, a.z * b.z)
|
||||
return vector.create(a.x * b.x, a.y * b.y, a.z * b.z)
|
||||
end
|
||||
}
|
||||
}
|
||||
@@ -392,79 +393,6 @@ fn test_interrupts() -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_coverage() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
lua.set_compiler(Compiler::default().set_coverage_level(1));
|
||||
|
||||
let f = lua
|
||||
.load(
|
||||
r#"local s = "abc"
|
||||
assert(#s == 3)
|
||||
|
||||
function abc(i)
|
||||
if i < 5 then
|
||||
return 0
|
||||
else
|
||||
return 1
|
||||
end
|
||||
end
|
||||
|
||||
(function()
|
||||
(function() abc(10) end)()
|
||||
end)()
|
||||
"#,
|
||||
)
|
||||
.into_function()?;
|
||||
|
||||
f.call::<()>(())?;
|
||||
|
||||
let mut report = Vec::new();
|
||||
f.coverage(|cov| {
|
||||
report.push(cov);
|
||||
});
|
||||
|
||||
assert_eq!(
|
||||
report[0],
|
||||
CoverageInfo {
|
||||
function: None,
|
||||
line_defined: 1,
|
||||
depth: 0,
|
||||
hits: vec![-1, 1, 1, -1, 1, -1, -1, -1, -1, -1, -1, -1, 1, -1, -1, -1],
|
||||
}
|
||||
);
|
||||
assert_eq!(
|
||||
report[1],
|
||||
CoverageInfo {
|
||||
function: Some("abc".into()),
|
||||
line_defined: 4,
|
||||
depth: 1,
|
||||
hits: vec![-1, -1, -1, -1, -1, 1, 0, -1, 1, -1, -1, -1, -1, -1, -1, -1],
|
||||
}
|
||||
);
|
||||
assert_eq!(
|
||||
report[2],
|
||||
CoverageInfo {
|
||||
function: None,
|
||||
line_defined: 12,
|
||||
depth: 1,
|
||||
hits: vec![-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, -1, -1],
|
||||
}
|
||||
);
|
||||
assert_eq!(
|
||||
report[3],
|
||||
CoverageInfo {
|
||||
function: None,
|
||||
line_defined: 13,
|
||||
depth: 2,
|
||||
hits: vec![-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, -1, -1],
|
||||
}
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_fflags() {
|
||||
// We cannot really on any particular feature flag to be present
|
||||
|
||||
+26
-1
@@ -1,4 +1,4 @@
|
||||
use mlua::{Error, ExternalError, IntoLuaMulti, Lua, Result, String, Value};
|
||||
use mlua::{Error, ExternalError, Integer, IntoLuaMulti, Lua, MultiValue, Result, String, Value, Variadic};
|
||||
|
||||
#[test]
|
||||
fn test_result_conversions() -> Result<()> {
|
||||
@@ -58,3 +58,28 @@ fn test_result_conversions() -> Result<()> {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_multivalue() {
|
||||
let mut multi = MultiValue::with_capacity(3);
|
||||
multi.push_back(Value::Integer(1));
|
||||
multi.push_back(Value::Integer(2));
|
||||
multi.push_front(Value::Integer(3));
|
||||
assert_eq!(multi.iter().filter_map(|v| v.as_integer()).sum::<Integer>(), 6);
|
||||
|
||||
let vec = multi.into_vec();
|
||||
assert_eq!(&vec, &[Value::Integer(3), Value::Integer(1), Value::Integer(2)]);
|
||||
let _multi2 = MultiValue::from_vec(vec);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_variadic() {
|
||||
let mut var = Variadic::with_capacity(3);
|
||||
var.extend_from_slice(&[1, 2, 3]);
|
||||
assert_eq!(var.iter().sum::<u32>(), 6);
|
||||
|
||||
let vec = Vec::<u32>::from(var);
|
||||
assert_eq!(&vec, &[1, 2, 3]);
|
||||
let var2 = Variadic::from(vec);
|
||||
assert_eq!(var2.as_slice(), &[1, 2, 3]);
|
||||
}
|
||||
|
||||
+127
-23
@@ -1,6 +1,7 @@
|
||||
use std::cell::Cell;
|
||||
use std::rc::Rc;
|
||||
use std::string::String as StdString;
|
||||
use std::sync::Arc;
|
||||
|
||||
use mlua::{
|
||||
AnyUserData, Error, Function, Lua, MetaMethod, ObjectLike, Result, String, UserData, UserDataFields,
|
||||
@@ -66,6 +67,27 @@ fn test_scope_outer_lua_access() -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_scope_capture_scope() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let i = Cell::new(0);
|
||||
lua.scope(|scope| {
|
||||
let f = scope.create_function(|_, ()| {
|
||||
scope.create_function(|_, n: u32| {
|
||||
i.set(i.get() + n);
|
||||
Ok(())
|
||||
})
|
||||
})?;
|
||||
f.call::<Function>(())?.call::<()>(10)?;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
assert_eq!(i.get(), 10);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_scope_userdata_fields() -> Result<()> {
|
||||
struct MyUserData<'a>(&'a Cell<i64>);
|
||||
@@ -332,7 +354,15 @@ fn test_scope_userdata_ref() -> Result<()> {
|
||||
let data = MyUserData(Cell::new(1));
|
||||
lua.scope(|scope| {
|
||||
let ud = scope.create_userdata_ref(&data)?;
|
||||
modify_userdata(&lua, ud)
|
||||
modify_userdata(&lua, &ud)?;
|
||||
|
||||
// We can only borrow userdata scoped
|
||||
assert!((matches!(ud.borrow::<MyUserData>(), Err(Error::UserDataTypeMismatch))));
|
||||
ud.borrow_scoped::<MyUserData, ()>(|ud_inst| {
|
||||
assert_eq!(ud_inst.0.get(), 2);
|
||||
})?;
|
||||
|
||||
Ok(())
|
||||
})?;
|
||||
assert_eq!(data.0.get(), 2);
|
||||
|
||||
@@ -362,9 +392,16 @@ fn test_scope_userdata_ref_mut() -> Result<()> {
|
||||
let mut data = MyUserData(1);
|
||||
lua.scope(|scope| {
|
||||
let ud = scope.create_userdata_ref_mut(&mut data)?;
|
||||
modify_userdata(&lua, ud)
|
||||
modify_userdata(&lua, &ud)?;
|
||||
|
||||
assert!((matches!(ud.borrow_mut::<MyUserData>(), Err(Error::UserDataTypeMismatch))));
|
||||
ud.borrow_mut_scoped::<MyUserData, ()>(|ud_inst| {
|
||||
ud_inst.0 += 10;
|
||||
})?;
|
||||
|
||||
Ok(())
|
||||
})?;
|
||||
assert_eq!(data.0, 2);
|
||||
assert_eq!(data.0, 12);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -373,15 +410,26 @@ fn test_scope_userdata_ref_mut() -> Result<()> {
|
||||
fn test_scope_any_userdata() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
lua.register_userdata_type::<StdString>(|reg| {
|
||||
reg.add_meta_method("__tostring", |_, data, ()| Ok(data.clone()));
|
||||
})?;
|
||||
fn register(reg: &mut UserDataRegistry<&mut StdString>) {
|
||||
reg.add_method_mut("push", |_, this, s: String| {
|
||||
this.push_str(&s.to_str()?);
|
||||
Ok(())
|
||||
});
|
||||
reg.add_meta_method("__tostring", |_, data, ()| Ok((*data).clone()));
|
||||
}
|
||||
|
||||
let data = StdString::from("foo");
|
||||
let mut data = StdString::from("foo");
|
||||
lua.scope(|scope| {
|
||||
let ud = scope.create_any_userdata_ref(&data)?;
|
||||
let ud = scope.create_any_userdata(&mut data, register)?;
|
||||
lua.globals().set("ud", ud)?;
|
||||
lua.load("assert(tostring(ud) == 'foo')").exec()
|
||||
lua.load(
|
||||
r#"
|
||||
assert(tostring(ud) == "foo")
|
||||
ud:push("bar")
|
||||
assert(tostring(ud) == "foobar")
|
||||
"#,
|
||||
)
|
||||
.exec()
|
||||
})?;
|
||||
|
||||
// Check that userdata is destructed
|
||||
@@ -415,27 +463,83 @@ fn test_scope_any_userdata_ref() -> Result<()> {
|
||||
let data = Cell::new(1i64);
|
||||
lua.scope(|scope| {
|
||||
let ud = scope.create_any_userdata_ref(&data)?;
|
||||
modify_userdata(&lua, ud)
|
||||
modify_userdata(&lua, &ud)
|
||||
})?;
|
||||
assert_eq!(data.get(), 2);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn modify_userdata(lua: &Lua, ud: AnyUserData) -> Result<()> {
|
||||
let f: Function = lua
|
||||
.load(
|
||||
r#"
|
||||
function(u)
|
||||
u:inc()
|
||||
u:dec()
|
||||
u:inc()
|
||||
end
|
||||
"#,
|
||||
)
|
||||
.eval()?;
|
||||
#[test]
|
||||
fn test_scope_any_userdata_ref_mut() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
f.call::<()>(ud)?;
|
||||
lua.register_userdata_type::<i64>(|reg| {
|
||||
reg.add_method_mut("inc", |_, data, ()| {
|
||||
*data += 1;
|
||||
Ok(())
|
||||
});
|
||||
|
||||
reg.add_method_mut("dec", |_, data, ()| {
|
||||
*data -= 1;
|
||||
Ok(())
|
||||
});
|
||||
})?;
|
||||
|
||||
let mut data = 1i64;
|
||||
lua.scope(|scope| {
|
||||
let ud = scope.create_any_userdata_ref_mut(&mut data)?;
|
||||
modify_userdata(&lua, &ud)
|
||||
})?;
|
||||
assert_eq!(data, 2);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_scope_destructors() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
lua.register_userdata_type::<Arc<StdString>>(|reg| {
|
||||
reg.add_meta_method("__tostring", |_, data, ()| Ok(data.to_string()));
|
||||
})?;
|
||||
|
||||
let arc_str = Arc::new(StdString::from("foo"));
|
||||
|
||||
let ud = lua.create_any_userdata(arc_str.clone())?;
|
||||
lua.scope(|scope| {
|
||||
scope.add_destructor(|| {
|
||||
assert!(ud.destroy().is_ok());
|
||||
});
|
||||
Ok(())
|
||||
})?;
|
||||
assert_eq!(Arc::strong_count(&arc_str), 1);
|
||||
|
||||
// Try destructing the userdata while it's borrowed
|
||||
let ud = lua.create_any_userdata(arc_str.clone())?;
|
||||
ud.borrow_scoped::<Arc<StdString>, _>(|arc_str| {
|
||||
assert_eq!(arc_str.as_str(), "foo");
|
||||
lua.scope(|scope| {
|
||||
scope.add_destructor(|| {
|
||||
assert!(ud.destroy().is_err());
|
||||
});
|
||||
Ok(())
|
||||
})
|
||||
.unwrap();
|
||||
assert_eq!(arc_str.as_str(), "foo");
|
||||
})?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn modify_userdata(lua: &Lua, ud: &AnyUserData) -> Result<()> {
|
||||
lua.load(
|
||||
r#"
|
||||
local u = ...
|
||||
u:inc()
|
||||
u:dec()
|
||||
u:inc()
|
||||
"#,
|
||||
)
|
||||
.call(ud)
|
||||
}
|
||||
|
||||
+59
-9
@@ -4,25 +4,35 @@ use std::cell::UnsafeCell;
|
||||
use std::marker::PhantomData;
|
||||
use std::string::String as StdString;
|
||||
|
||||
use mlua::{AnyUserData, Error, Lua, Result, UserDataRef};
|
||||
use mlua::{AnyUserData, Error, Lua, ObjectLike, Result, UserData, UserDataMethods, UserDataRef};
|
||||
use static_assertions::{assert_impl_all, assert_not_impl_all};
|
||||
|
||||
#[test]
|
||||
fn test_userdata_multithread_access() -> Result<()> {
|
||||
fn test_userdata_multithread_access_send_only() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
// This type is `Send` but not `Sync`.
|
||||
struct MyUserData(#[allow(unused)] StdString, PhantomData<UnsafeCell<()>>);
|
||||
|
||||
struct MyUserData(StdString, PhantomData<UnsafeCell<()>>);
|
||||
assert_impl_all!(MyUserData: Send);
|
||||
assert_not_impl_all!(MyUserData: Sync);
|
||||
|
||||
lua.globals().set(
|
||||
"ud",
|
||||
AnyUserData::wrap(MyUserData("hello".to_string(), PhantomData)),
|
||||
)?;
|
||||
impl UserData for MyUserData {
|
||||
fn add_methods<M: UserDataMethods<Self>>(methods: &mut M) {
|
||||
methods.add_method("method", |lua, this, ()| {
|
||||
let ud = lua.globals().get::<AnyUserData>("ud")?;
|
||||
assert_eq!(ud.call_method::<String>("method2", ())?, "method2");
|
||||
Ok(this.0.clone())
|
||||
});
|
||||
|
||||
methods.add_method("method2", |_, _, ()| Ok("method2"));
|
||||
}
|
||||
}
|
||||
|
||||
lua.globals()
|
||||
.set("ud", MyUserData("hello".to_string(), PhantomData))?;
|
||||
|
||||
// We acquired the exclusive reference.
|
||||
let _ud1 = lua.globals().get::<UserDataRef<MyUserData>>("ud")?;
|
||||
let ud = lua.globals().get::<UserDataRef<MyUserData>>("ud")?;
|
||||
|
||||
std::thread::scope(|s| {
|
||||
s.spawn(|| {
|
||||
@@ -31,5 +41,45 @@ fn test_userdata_multithread_access() -> Result<()> {
|
||||
});
|
||||
});
|
||||
|
||||
drop(ud);
|
||||
lua.load("ud:method()").exec().unwrap();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_userdata_multithread_access_sync() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
// This type is `Send` and `Sync`.
|
||||
struct MyUserData(StdString);
|
||||
assert_impl_all!(MyUserData: Send, Sync);
|
||||
|
||||
impl UserData for MyUserData {
|
||||
fn add_methods<M: UserDataMethods<Self>>(methods: &mut M) {
|
||||
methods.add_method("method", |lua, this, ()| {
|
||||
let ud = lua.globals().get::<AnyUserData>("ud")?;
|
||||
assert!(ud.call_method::<()>("method2", ()).is_ok());
|
||||
Ok(this.0.clone())
|
||||
});
|
||||
|
||||
methods.add_method("method2", |_, _, ()| Ok(()));
|
||||
}
|
||||
}
|
||||
|
||||
lua.globals().set("ud", MyUserData("hello".to_string()))?;
|
||||
|
||||
// We acquired the shared reference.
|
||||
let _ud = lua.globals().get::<UserDataRef<MyUserData>>("ud")?;
|
||||
|
||||
std::thread::scope(|s| {
|
||||
s.spawn(|| {
|
||||
// Getting another shared reference for `Sync` type is allowed.
|
||||
let _ = lua.globals().get::<UserDataRef<MyUserData>>("ud").unwrap();
|
||||
});
|
||||
});
|
||||
|
||||
lua.load("ud:method()").exec().unwrap();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
+4
-14
@@ -133,13 +133,7 @@ fn test_serialize_failure() -> Result<(), Box<dyn StdError>> {
|
||||
fn test_serialize_vector() -> Result<(), Box<dyn StdError>> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let globals = lua.globals();
|
||||
globals.set(
|
||||
"vector",
|
||||
lua.create_function(|_, (x, y, z)| Ok(mlua::Vector::new(x, y, z)))?,
|
||||
)?;
|
||||
|
||||
let val = lua.load("{_vector = vector(1, 2, 3)}").eval::<Value>()?;
|
||||
let val = lua.load("{_vector = vector.create(1, 2, 3)}").eval::<Value>()?;
|
||||
let json = serde_json::json!({
|
||||
"_vector": [1.0, 2.0, 3.0],
|
||||
});
|
||||
@@ -156,13 +150,9 @@ fn test_serialize_vector() -> Result<(), Box<dyn StdError>> {
|
||||
fn test_serialize_vector() -> Result<(), Box<dyn StdError>> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let globals = lua.globals();
|
||||
globals.set(
|
||||
"vector",
|
||||
lua.create_function(|_, (x, y, z, w)| Ok(mlua::Vector::new(x, y, z, w)))?,
|
||||
)?;
|
||||
|
||||
let val = lua.load("{_vector = vector(1, 2, 3, 4)}").eval::<Value>()?;
|
||||
let val = lua
|
||||
.load("{_vector = vector.create(1, 2, 3, 4)}")
|
||||
.eval::<Value>()?;
|
||||
let json = serde_json::json!({
|
||||
"_vector": [1.0, 2.0, 3.0, 4.0],
|
||||
});
|
||||
|
||||
+27
-2
@@ -17,6 +17,15 @@ fn test_string_compare() {
|
||||
with_str("teststring", |t| assert_eq!(t, t)); // mlua::String
|
||||
with_str("teststring", |t| assert_eq!(t, Cow::from(b"teststring".as_ref()))); // Cow (borrowed)
|
||||
with_str("bla", |t| assert_eq!(t, Cow::from(b"bla".to_vec()))); // Cow (owned)
|
||||
|
||||
// Test ordering
|
||||
with_str("a", |a| {
|
||||
assert!(!(a < a));
|
||||
assert!(!(a > a));
|
||||
});
|
||||
with_str("a", |a| assert!(a < "b"));
|
||||
with_str("a", |a| assert!(a < b"b"));
|
||||
with_str("a", |a| with_str("b", |b| assert!(a < b)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -52,7 +61,7 @@ fn test_string_views() -> Result<()> {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_raw_string() -> Result<()> {
|
||||
fn test_string_from_bytes() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let rs = lua.create_string(&[0, 1, 2, 3, 0, 1, 2, 3])?;
|
||||
@@ -77,12 +86,14 @@ fn test_string_hash() -> Result<()> {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_string_debug() -> Result<()> {
|
||||
fn test_string_fmt_debug() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
// Valid utf8
|
||||
let s = lua.create_string("hello")?;
|
||||
assert_eq!(format!("{s:?}"), r#""hello""#);
|
||||
assert_eq!(format!("{:?}", s.to_str()?), r#""hello""#);
|
||||
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")?;
|
||||
@@ -103,3 +114,17 @@ fn test_string_pointer() -> Result<()> {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_string_display() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let s = lua.create_string("hello")?;
|
||||
assert_eq!(format!("{}", s.display()), "hello");
|
||||
|
||||
// With invalid utf8
|
||||
let s = lua.create_string(b"hello\0world\xFF")?;
|
||||
assert_eq!(format!("{}", s.display()), "hello\0world�");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
+84
-59
@@ -1,4 +1,4 @@
|
||||
use mlua::{Error, Lua, Nil, ObjectLike, Result, Table, Value};
|
||||
use mlua::{Error, Lua, ObjectLike, Result, Table, Value};
|
||||
|
||||
#[test]
|
||||
fn test_globals_set_get() -> Result<()> {
|
||||
@@ -10,6 +10,8 @@ fn test_globals_set_get() -> Result<()> {
|
||||
assert_eq!(globals.get::<String>("foo")?, "bar");
|
||||
assert_eq!(globals.get::<String>("baz")?, "baf");
|
||||
|
||||
lua.load(r#"assert(foo == "bar")"#).exec().unwrap();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -19,16 +21,6 @@ fn test_table() -> Result<()> {
|
||||
|
||||
let globals = lua.globals();
|
||||
|
||||
globals.set("table", lua.create_table()?)?;
|
||||
let table1: Table = globals.get("table")?;
|
||||
let table2: Table = globals.get("table")?;
|
||||
|
||||
table1.set("foo", "bar")?;
|
||||
table2.set("baz", "baf")?;
|
||||
|
||||
assert_eq!(table2.get::<String>("foo")?, "bar");
|
||||
assert_eq!(table1.get::<String>("baz")?, "baf");
|
||||
|
||||
lua.load(
|
||||
r#"
|
||||
table1 = {1, 2, 3, 4, 5}
|
||||
@@ -39,29 +31,26 @@ fn test_table() -> Result<()> {
|
||||
.exec()?;
|
||||
|
||||
let table1 = globals.get::<Table>("table1")?;
|
||||
let table2 = globals.get::<Table>("table2")?;
|
||||
let table3 = globals.get::<Table>("table3")?;
|
||||
|
||||
assert_eq!(table1.len()?, 5);
|
||||
assert!(!table1.is_empty());
|
||||
assert_eq!(
|
||||
table1.clone().pairs().collect::<Result<Vec<(i64, i64)>>>()?,
|
||||
table1.pairs().collect::<Result<Vec<(i64, i64)>>>()?,
|
||||
vec![(1, 1), (2, 2), (3, 3), (4, 4), (5, 5)]
|
||||
);
|
||||
assert_eq!(
|
||||
table1.clone().sequence_values().collect::<Result<Vec<i64>>>()?,
|
||||
table1.sequence_values().collect::<Result<Vec<i64>>>()?,
|
||||
vec![1, 2, 3, 4, 5]
|
||||
);
|
||||
assert_eq!(table1, [1, 2, 3, 4, 5]);
|
||||
assert_eq!(table1, [1, 2, 3, 4, 5].as_slice());
|
||||
|
||||
let table2 = globals.get::<Table>("table2")?;
|
||||
assert_eq!(table2.len()?, 0);
|
||||
assert!(table2.is_empty());
|
||||
assert_eq!(
|
||||
table2.clone().pairs().collect::<Result<Vec<(i64, i64)>>>()?,
|
||||
vec![]
|
||||
);
|
||||
assert_eq!(table2.pairs().collect::<Result<Vec<(i64, i64)>>>()?, vec![]);
|
||||
assert_eq!(table2, [0; 0]);
|
||||
|
||||
let table3 = globals.get::<Table>("table3")?;
|
||||
// sequence_values should only iterate until the first border
|
||||
assert_eq!(table3, [1, 2]);
|
||||
assert_eq!(
|
||||
@@ -69,26 +58,6 @@ fn test_table() -> Result<()> {
|
||||
vec![1, 2]
|
||||
);
|
||||
|
||||
globals.set("table4", lua.create_sequence_from(vec![1, 2, 3, 4, 5])?)?;
|
||||
let table4 = globals.get::<Table>("table4")?;
|
||||
assert_eq!(
|
||||
table4.clone().pairs().collect::<Result<Vec<(i64, i64)>>>()?,
|
||||
vec![(1, 1), (2, 2), (3, 3), (4, 4), (5, 5)]
|
||||
);
|
||||
|
||||
table4.raw_insert(4, 35)?;
|
||||
table4.raw_insert(7, 7)?;
|
||||
assert_eq!(
|
||||
table4.clone().pairs().collect::<Result<Vec<(i64, i64)>>>()?,
|
||||
vec![(1, 1), (2, 2), (3, 3), (4, 35), (5, 4), (6, 5), (7, 7)]
|
||||
);
|
||||
|
||||
table4.raw_remove(1)?;
|
||||
assert_eq!(
|
||||
table4.clone().pairs().collect::<Result<Vec<(i64, i64)>>>()?,
|
||||
vec![(1, 2), (2, 3), (3, 35), (4, 4), (5, 5), (6, 7)]
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -97,7 +66,7 @@ fn test_table_push_pop() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
// Test raw access
|
||||
let table1 = lua.create_sequence_from(vec![123])?;
|
||||
let table1 = lua.create_sequence_from([123])?;
|
||||
table1.raw_push(321)?;
|
||||
assert_eq!(table1, [123, 321]);
|
||||
assert_eq!(table1.raw_pop::<i64>()?, 321);
|
||||
@@ -123,10 +92,7 @@ fn test_table_push_pop() -> Result<()> {
|
||||
table2.push(345)?;
|
||||
assert_eq!(table2.len()?, 2);
|
||||
assert_eq!(
|
||||
table2
|
||||
.clone()
|
||||
.sequence_values::<i64>()
|
||||
.collect::<Result<Vec<_>>>()?,
|
||||
table2.sequence_values::<i64>().collect::<Result<Vec<_>>>()?,
|
||||
vec![]
|
||||
);
|
||||
assert_eq!(table2.pop::<i64>()?, 345);
|
||||
@@ -137,22 +103,53 @@ fn test_table_push_pop() -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_table_insert_remove() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let globals = lua.globals();
|
||||
|
||||
globals.set("table4", [1, 2, 3, 4, 5])?;
|
||||
let table4 = globals.get::<Table>("table4")?;
|
||||
assert_eq!(
|
||||
table4.pairs().collect::<Result<Vec<(i64, i64)>>>()?,
|
||||
vec![(1, 1), (2, 2), (3, 3), (4, 4), (5, 5)]
|
||||
);
|
||||
table4.raw_insert(4, 35)?;
|
||||
table4.raw_insert(7, 7)?;
|
||||
assert_eq!(
|
||||
table4.pairs().collect::<Result<Vec<(i64, i64)>>>()?,
|
||||
vec![(1, 1), (2, 2), (3, 3), (4, 35), (5, 4), (6, 5), (7, 7)]
|
||||
);
|
||||
table4.raw_remove(1)?;
|
||||
assert_eq!(
|
||||
table4.pairs().collect::<Result<Vec<(i64, i64)>>>()?,
|
||||
vec![(1, 2), (2, 3), (3, 35), (4, 4), (5, 5), (6, 7)]
|
||||
);
|
||||
|
||||
// Wrong index, tables are 1-indexed
|
||||
assert!(table4.raw_insert(0, "123").is_err());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_table_clear() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let t = lua.create_table()?;
|
||||
|
||||
// Check readonly error
|
||||
#[cfg(feature = "luau")]
|
||||
{
|
||||
let t = lua.create_table()?;
|
||||
t.set_readonly(true);
|
||||
assert!(matches!(
|
||||
t.clear(),
|
||||
Err(Error::RuntimeError(err)) if err.contains("attempt to modify a readonly table")
|
||||
));
|
||||
t.set_readonly(false);
|
||||
}
|
||||
|
||||
let t = lua.create_table()?;
|
||||
// Set array and hash parts
|
||||
t.push("abc")?;
|
||||
t.push("bcd")?;
|
||||
@@ -217,15 +214,14 @@ fn test_table_pairs() -> Result<()> {
|
||||
)
|
||||
.eval::<Table>()?;
|
||||
|
||||
let table2 = table.clone();
|
||||
for (i, kv) in table.pairs::<String, Value>().enumerate() {
|
||||
let (k, _v) = kv.unwrap();
|
||||
match i {
|
||||
// Try to add a new key
|
||||
0 => table2.set("new_key", "new_value")?,
|
||||
0 => table.set("new_key", "new_value")?,
|
||||
// Try to delete the 2nd key
|
||||
1 => {
|
||||
table2.set(k, Value::Nil)?;
|
||||
table.set(k, Value::Nil)?;
|
||||
lua.gc_collect()?;
|
||||
}
|
||||
_ => {}
|
||||
@@ -304,21 +300,15 @@ fn test_metatable() -> Result<()> {
|
||||
metatable.set("__index", lua.create_function(|_, ()| Ok("index_value"))?)?;
|
||||
table.set_metatable(Some(metatable));
|
||||
assert_eq!(table.get::<String>("any_key")?, "index_value");
|
||||
match table.raw_get::<Value>("any_key")? {
|
||||
Nil => {}
|
||||
_ => panic!(),
|
||||
}
|
||||
assert_eq!(table.raw_get::<Value>("any_key")?, Value::Nil);
|
||||
table.set_metatable(None);
|
||||
match table.get::<Value>("any_key")? {
|
||||
Nil => {}
|
||||
_ => panic!(),
|
||||
};
|
||||
assert_eq!(table.get::<Value>("any_key")?, Value::Nil);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_table_eq() -> Result<()> {
|
||||
fn test_table_equals() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
let globals = lua.globals();
|
||||
|
||||
@@ -358,6 +348,7 @@ fn test_table_pointer() -> Result<()> {
|
||||
let table1 = lua.create_table()?;
|
||||
let table2 = lua.create_table()?;
|
||||
|
||||
// Clone should not create a new table
|
||||
assert_eq!(table1.to_pointer(), table1.clone().to_pointer());
|
||||
assert_ne!(table1.to_pointer(), table2.to_pointer());
|
||||
|
||||
@@ -398,6 +389,40 @@ fn test_table_error() -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_table_fmt() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let table = lua
|
||||
.load(
|
||||
r#"
|
||||
local t = {1, 2, 3, a = 5, b = { 6 }}
|
||||
t["special-<chars>"] = 10
|
||||
t[9.2] = 9.2
|
||||
t[1.99] = 1.99
|
||||
t[true] = true
|
||||
t[false] = false
|
||||
return t
|
||||
"#,
|
||||
)
|
||||
.eval::<Table>()?;
|
||||
assert!(format!("{table:?}").starts_with("Table(Ref("));
|
||||
|
||||
// Pretty print
|
||||
assert_eq!(
|
||||
format!("{table:#?}"),
|
||||
"{\n [false] = false,\n [true] = true,\n [1] = 1,\n [1.99] = 1.99,\n [2] = 2,\n [3] = 3,\n [9.2] = 9.2,\n a = 5,\n b = {\n 6,\n },\n [\"special-<chars>\"] = 10,\n}"
|
||||
);
|
||||
|
||||
let table2 = lua.create_table_from([("1", "first"), ("2", "second")])?;
|
||||
assert_eq!(
|
||||
format!("{table2:#?}"),
|
||||
"{\n [\"1\"] = \"first\",\n [\"2\"] = \"second\",\n}"
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_table_object_like() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
+78
-23
@@ -106,7 +106,6 @@ fn test_exec() -> Result<()> {
|
||||
"#,
|
||||
)
|
||||
.eval()?;
|
||||
println!("checkpoint");
|
||||
assert!(module.contains_key("func")?);
|
||||
assert_eq!(module.get::<Function>("func")?.call::<String>(())?, "hello");
|
||||
|
||||
@@ -631,8 +630,7 @@ fn test_recursive_mut_callback_error() -> Result<()> {
|
||||
// Whoops, this will recurse into the function and produce another mutable reference!
|
||||
lua.globals().get::<Function>("f")?.call::<()>(true)?;
|
||||
println!("Should not get here, mutable aliasing has occurred!");
|
||||
println!("value at {:p}", r as *mut _);
|
||||
println!("value is {}", r);
|
||||
println!("value at {:p} is {r}", r as *mut _);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@@ -846,10 +844,12 @@ fn test_application_data() -> Result<()> {
|
||||
assert_eq!(format!("{s:?}"), "\"test1\"");
|
||||
|
||||
// Borrowing immutably and mutably of the same type is not allowed
|
||||
assert!(lua.try_app_data_mut::<&str>().is_err());
|
||||
match catch_unwind(AssertUnwindSafe(|| lua.app_data_mut::<&str>().unwrap())) {
|
||||
Ok(_) => panic!("expected panic"),
|
||||
Err(_) => {}
|
||||
}
|
||||
assert!(lua.try_app_data_ref::<Vec<&str>>().is_err());
|
||||
drop((s, v));
|
||||
|
||||
// Test that application data is accessible from anywhere
|
||||
@@ -874,6 +874,49 @@ fn test_application_data() -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_rust_function() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let globals = lua.globals();
|
||||
lua.load(
|
||||
r#"
|
||||
function lua_function()
|
||||
return rust_function()
|
||||
end
|
||||
|
||||
-- Test to make sure chunk return is ignored
|
||||
return 1
|
||||
"#,
|
||||
)
|
||||
.exec()?;
|
||||
|
||||
let lua_function = globals.get::<Function>("lua_function")?;
|
||||
let rust_function = lua.create_function(|_, ()| Ok("hello"))?;
|
||||
|
||||
globals.set("rust_function", rust_function)?;
|
||||
assert_eq!(lua_function.call::<String>(())?, "hello");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_c_function() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
unsafe extern "C-unwind" fn c_function(state: *mut mlua::lua_State) -> std::os::raw::c_int {
|
||||
ffi::lua_pushboolean(state, 1);
|
||||
ffi::lua_setglobal(state, b"c_function\0" as *const _ as *const _);
|
||||
0
|
||||
}
|
||||
|
||||
let func = unsafe { lua.create_c_function(c_function)? };
|
||||
func.call::<()>(())?;
|
||||
assert_eq!(lua.globals().get::<bool>("c_function")?, true);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
fn test_recursion() -> Result<()> {
|
||||
@@ -925,26 +968,6 @@ fn test_too_many_recursions() -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
fn test_too_many_binds() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
let globals = lua.globals();
|
||||
lua.load(
|
||||
r#"
|
||||
function f(...)
|
||||
end
|
||||
"#,
|
||||
)
|
||||
.exec()?;
|
||||
|
||||
let concat = globals.get::<Function>("f")?;
|
||||
assert!(concat.bind(Variadic::from_iter(1..1000000)).is_err());
|
||||
assert!(concat.call::<()>(Variadic::from_iter(1..1000000)).is_err());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
fn test_ref_stack_exhaustion() {
|
||||
@@ -1172,6 +1195,38 @@ fn test_inspect_stack() -> Result<()> {
|
||||
.set_name("chunk")
|
||||
.exec()?;
|
||||
|
||||
let stack_info = lua.create_function(|lua, ()| {
|
||||
let debug = lua.inspect_stack(1).unwrap(); // caller
|
||||
let stack_info = debug.stack();
|
||||
Ok(format!("{stack_info:?}"))
|
||||
})?;
|
||||
lua.globals().set("stack_info", stack_info)?;
|
||||
|
||||
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52", feature = "luau"))]
|
||||
lua.load(
|
||||
r#"
|
||||
local stack_info = stack_info
|
||||
local function baz(a, b, c, ...)
|
||||
return stack_info()
|
||||
end
|
||||
assert(baz() == 'DebugStack { num_ups: 1, num_params: 3, is_vararg: true }')
|
||||
"#,
|
||||
)
|
||||
.exec()?;
|
||||
|
||||
// LuaJIT does not pass this test for some reason
|
||||
#[cfg(feature = "lua51")]
|
||||
lua.load(
|
||||
r#"
|
||||
local stack_info = stack_info
|
||||
local function baz(a, b, c, ...)
|
||||
return stack_info()
|
||||
end
|
||||
assert(baz() == 'DebugStack { num_ups: 1 }')
|
||||
"#,
|
||||
)
|
||||
.exec()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
+188
-3
@@ -195,7 +195,7 @@ fn test_metamethods() -> Result<()> {
|
||||
|
||||
assert!(lua.load("userdata2 == userdata3").eval::<bool>()?);
|
||||
assert!(userdata2 != userdata3); // because references are differ
|
||||
assert!(userdata2.equals(userdata3)?);
|
||||
assert!(userdata2.equals(&userdata3)?);
|
||||
|
||||
let userdata1: AnyUserData = globals.get("userdata1")?;
|
||||
assert!(userdata1.metatable()?.contains(MetaMethod::Add)?);
|
||||
@@ -376,7 +376,18 @@ fn test_userdata_take() -> Result<()> {
|
||||
fn test_userdata_destroy() -> Result<()> {
|
||||
struct MyUserdata(#[allow(unused)] Arc<()>);
|
||||
|
||||
impl UserData for MyUserdata {}
|
||||
impl UserData for MyUserdata {
|
||||
fn add_methods<M: UserDataMethods<Self>>(methods: &mut M) {
|
||||
methods.add_method("try_destroy", |lua, _this, ()| {
|
||||
let ud = lua.globals().get::<AnyUserData>("ud")?;
|
||||
match ud.destroy() {
|
||||
Err(Error::UserDataBorrowMutError) => {}
|
||||
r => panic!("expected `UserDataBorrowMutError` error, got {:?}", r),
|
||||
}
|
||||
Ok(())
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
let rc = Arc::new(());
|
||||
|
||||
@@ -394,6 +405,23 @@ fn test_userdata_destroy() -> Result<()> {
|
||||
|
||||
assert_eq!(Arc::strong_count(&rc), 1);
|
||||
|
||||
let ud = lua.create_userdata(MyUserdata(rc.clone()))?;
|
||||
assert_eq!(Arc::strong_count(&rc), 2);
|
||||
let ud_ref = ud.borrow::<MyUserdata>()?;
|
||||
// With active `UserDataRef` this methods only marks userdata as destructed
|
||||
// without running destructor
|
||||
ud.destroy()?;
|
||||
assert_eq!(Arc::strong_count(&rc), 2);
|
||||
drop(ud_ref);
|
||||
assert_eq!(Arc::strong_count(&rc), 1);
|
||||
|
||||
// We cannot destroy (internally) borrowed userdata
|
||||
let ud = lua.create_userdata(MyUserdata(rc.clone()))?;
|
||||
lua.globals().set("ud", &ud)?;
|
||||
lua.load("ud:try_destroy()").exec().unwrap();
|
||||
ud.destroy()?;
|
||||
assert_eq!(Arc::strong_count(&rc), 1);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -418,6 +446,7 @@ fn test_user_values() -> Result<()> {
|
||||
assert!(ud.nth_user_value::<Value>(65536).is_err());
|
||||
|
||||
// Named user values
|
||||
let ud = lua.create_userdata(MyUserData)?;
|
||||
ud.set_named_user_value("name", "alex")?;
|
||||
ud.set_named_user_value("age", 10)?;
|
||||
|
||||
@@ -802,7 +831,6 @@ fn test_userdata_method_errors() -> Result<()> {
|
||||
} => {
|
||||
assert_eq!(to.as_deref(), Some("MyUserData.get_value"));
|
||||
assert_eq!(name.as_deref(), Some("self"));
|
||||
println!("{}", cause2.to_string());
|
||||
assert_eq!(
|
||||
cause2.to_string(),
|
||||
"error converting Lua string to userdata (expected userdata of type 'MyUserData')"
|
||||
@@ -881,3 +909,160 @@ fn test_nested_userdata_gc() -> Result<()> {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
#[test]
|
||||
fn test_userdata_wrappers() -> Result<()> {
|
||||
struct MyUserData(i64);
|
||||
|
||||
impl UserData for MyUserData {
|
||||
fn add_fields<F: UserDataFields<Self>>(fields: &mut F) {
|
||||
fields.add_field("static", "constant");
|
||||
fields.add_field_method_get("data", |_, this| Ok(this.0));
|
||||
fields.add_field_method_set("data", |_, this, val| {
|
||||
this.0 = val;
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
let lua = Lua::new();
|
||||
let globals = lua.globals();
|
||||
|
||||
// Rc<T>
|
||||
#[cfg(not(feature = "send"))]
|
||||
{
|
||||
let ud = std::rc::Rc::new(MyUserData(1));
|
||||
globals.set("rc_ud", ud.clone())?;
|
||||
lua.load(
|
||||
r#"
|
||||
assert(rc_ud.static == "constant")
|
||||
local ok, err = pcall(function() rc_ud.data = 2 end)
|
||||
assert(
|
||||
tostring(err):sub(1, 32) == "error mutably borrowing userdata",
|
||||
"expected error mutably borrowing userdata, got " .. tostring(err)
|
||||
)
|
||||
assert(rc_ud.data == 1)
|
||||
"#,
|
||||
)
|
||||
.exec()
|
||||
.unwrap();
|
||||
globals.set("rc_ud", Nil)?;
|
||||
lua.gc_collect()?;
|
||||
assert_eq!(std::rc::Rc::strong_count(&ud), 1);
|
||||
}
|
||||
|
||||
// Rc<RefCell<T>>
|
||||
#[cfg(not(feature = "send"))]
|
||||
{
|
||||
let ud = std::rc::Rc::new(std::cell::RefCell::new(MyUserData(2)));
|
||||
globals.set("rc_refcell_ud", ud.clone())?;
|
||||
lua.load(
|
||||
r#"
|
||||
assert(rc_refcell_ud.static == "constant")
|
||||
rc_refcell_ud.data = rc_refcell_ud.data + 1
|
||||
assert(rc_refcell_ud.data == 3)
|
||||
"#,
|
||||
)
|
||||
.exec()?;
|
||||
assert_eq!(ud.borrow().0, 3);
|
||||
globals.set("rc_refcell_ud", Nil)?;
|
||||
lua.gc_collect()?;
|
||||
assert_eq!(std::rc::Rc::strong_count(&ud), 1);
|
||||
}
|
||||
|
||||
// Arc<T>
|
||||
{
|
||||
let ud = Arc::new(MyUserData(3));
|
||||
globals.set("arc_ud", ud.clone())?;
|
||||
lua.load(
|
||||
r#"
|
||||
assert(arc_ud.static == "constant")
|
||||
local ok, err = pcall(function() arc_ud.data = 10 end)
|
||||
assert(
|
||||
tostring(err):sub(1, 32) == "error mutably borrowing userdata",
|
||||
"expected error mutably borrowing userdata, got " .. tostring(err)
|
||||
)
|
||||
assert(arc_ud.data == 3)
|
||||
"#,
|
||||
)
|
||||
.exec()?;
|
||||
globals.set("arc_ud", Nil)?;
|
||||
lua.gc_collect()?;
|
||||
assert_eq!(Arc::strong_count(&ud), 1);
|
||||
}
|
||||
|
||||
// Arc<Mutex<T>>
|
||||
{
|
||||
let ud = Arc::new(std::sync::Mutex::new(MyUserData(4)));
|
||||
globals.set("arc_mutex_ud", ud.clone())?;
|
||||
lua.load(
|
||||
r#"
|
||||
assert(arc_mutex_ud.static == "constant")
|
||||
arc_mutex_ud.data = arc_mutex_ud.data + 1
|
||||
assert(arc_mutex_ud.data == 5)
|
||||
"#,
|
||||
)
|
||||
.exec()?;
|
||||
assert_eq!(ud.lock().unwrap().0, 5);
|
||||
globals.set("arc_mutex_ud", Nil)?;
|
||||
lua.gc_collect()?;
|
||||
assert_eq!(Arc::strong_count(&ud), 1);
|
||||
}
|
||||
|
||||
// Arc<RwLock<T>>
|
||||
{
|
||||
let ud = Arc::new(std::sync::RwLock::new(MyUserData(6)));
|
||||
globals.set("arc_rwlock_ud", ud.clone())?;
|
||||
lua.load(
|
||||
r#"
|
||||
assert(arc_rwlock_ud.static == "constant")
|
||||
arc_rwlock_ud.data = arc_rwlock_ud.data + 1
|
||||
assert(arc_rwlock_ud.data == 7)
|
||||
"#,
|
||||
)
|
||||
.exec()?;
|
||||
assert_eq!(ud.read().unwrap().0, 7);
|
||||
globals.set("arc_rwlock_ud", Nil)?;
|
||||
lua.gc_collect()?;
|
||||
assert_eq!(Arc::strong_count(&ud), 1);
|
||||
}
|
||||
|
||||
// Arc<parking_lot::Mutex<T>>
|
||||
{
|
||||
let ud = Arc::new(parking_lot::Mutex::new(MyUserData(8)));
|
||||
globals.set("arc_parking_lot_mutex_ud", ud.clone())?;
|
||||
lua.load(
|
||||
r#"
|
||||
assert(arc_parking_lot_mutex_ud.static == "constant")
|
||||
arc_parking_lot_mutex_ud.data = arc_parking_lot_mutex_ud.data + 1
|
||||
assert(arc_parking_lot_mutex_ud.data == 9)
|
||||
"#,
|
||||
)
|
||||
.exec()?;
|
||||
assert_eq!(ud.lock().0, 9);
|
||||
globals.set("arc_parking_lot_mutex_ud", Nil)?;
|
||||
lua.gc_collect()?;
|
||||
assert_eq!(Arc::strong_count(&ud), 1);
|
||||
}
|
||||
|
||||
// Arc<parking_lot::RwLock<T>>
|
||||
{
|
||||
let ud = Arc::new(parking_lot::RwLock::new(MyUserData(10)));
|
||||
globals.set("arc_parking_lot_rwlock_ud", ud.clone())?;
|
||||
lua.load(
|
||||
r#"
|
||||
assert(arc_parking_lot_rwlock_ud.static == "constant")
|
||||
arc_parking_lot_rwlock_ud.data = arc_parking_lot_rwlock_ud.data + 1
|
||||
assert(arc_parking_lot_rwlock_ud.data == 11)
|
||||
"#,
|
||||
)
|
||||
.exec()?;
|
||||
assert_eq!(ud.read().0, 11);
|
||||
globals.set("arc_parking_lot_rwlock_ud", Nil)?;
|
||||
lua.gc_collect()?;
|
||||
assert_eq!(Arc::strong_count(&ud), 1);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
+57
-2
@@ -52,7 +52,7 @@ fn test_value_eq() -> Result<()> {
|
||||
assert!(string1 == string2);
|
||||
assert!(string1.equals(&string2)?);
|
||||
assert!(num1 == num2);
|
||||
assert!(num1.equals(num2)?);
|
||||
assert!(num1.equals(&num2)?);
|
||||
assert!(num1 != num3);
|
||||
assert!(func1 == func2);
|
||||
assert!(func1 != func3);
|
||||
@@ -88,30 +88,79 @@ fn test_multi_value() {
|
||||
assert!(multi_value.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_value_to_pointer() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let globals = lua.globals();
|
||||
lua.load(
|
||||
r#"
|
||||
table = {}
|
||||
string = "hello"
|
||||
num = 1
|
||||
func = function() end
|
||||
thread = coroutine.create(function() end)
|
||||
"#,
|
||||
)
|
||||
.exec()?;
|
||||
globals.set("null", Value::NULL)?;
|
||||
|
||||
let table: Value = globals.get("table")?;
|
||||
let string: Value = globals.get("string")?;
|
||||
let num: Value = globals.get("num")?;
|
||||
let func: Value = globals.get("func")?;
|
||||
let thread: Value = globals.get("thread")?;
|
||||
let null: Value = globals.get("null")?;
|
||||
let ud: Value = Value::UserData(lua.create_any_userdata(())?);
|
||||
|
||||
assert!(!table.to_pointer().is_null());
|
||||
assert!(!string.to_pointer().is_null());
|
||||
assert!(num.to_pointer().is_null());
|
||||
assert!(!func.to_pointer().is_null());
|
||||
assert!(!thread.to_pointer().is_null());
|
||||
assert!(null.to_pointer().is_null());
|
||||
assert!(!ud.to_pointer().is_null());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_value_to_string() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
assert_eq!(Value::Nil.to_string()?, "nil");
|
||||
assert_eq!(Value::Nil.type_name(), "nil");
|
||||
assert_eq!(Value::Boolean(true).to_string()?, "true");
|
||||
assert_eq!(Value::Boolean(true).type_name(), "boolean");
|
||||
assert_eq!(Value::NULL.to_string()?, "null");
|
||||
assert_eq!(Value::NULL.type_name(), "lightuserdata");
|
||||
assert_eq!(
|
||||
Value::LightUserData(LightUserData(0x1 as *const c_void as *mut _)).to_string()?,
|
||||
"lightuserdata: 0x1"
|
||||
);
|
||||
assert_eq!(Value::Integer(1).to_string()?, "1");
|
||||
assert_eq!(Value::Integer(1).type_name(), "integer");
|
||||
assert_eq!(Value::Number(34.59).to_string()?, "34.59");
|
||||
assert_eq!(Value::Number(34.59).type_name(), "number");
|
||||
#[cfg(all(feature = "luau", not(feature = "luau-vector4")))]
|
||||
assert_eq!(
|
||||
Value::Vector(mlua::Vector::new(10.0, 11.1, 12.2)).to_string()?,
|
||||
"vector(10, 11.1, 12.2)"
|
||||
);
|
||||
#[cfg(all(feature = "luau", not(feature = "luau-vector4")))]
|
||||
assert_eq!(
|
||||
Value::Vector(mlua::Vector::new(10.0, 11.1, 12.2)).type_name(),
|
||||
"vector"
|
||||
);
|
||||
#[cfg(feature = "luau-vector4")]
|
||||
assert_eq!(
|
||||
Value::Vector(mlua::Vector::new(10.0, 11.1, 12.2, 13.3)).to_string()?,
|
||||
"vector(10, 11.1, 12.2, 13.3)"
|
||||
);
|
||||
assert_eq!(Value::String(lua.create_string("hello")?).to_string()?, "hello");
|
||||
|
||||
let s = Value::String(lua.create_string("hello")?);
|
||||
assert_eq!(s.to_string()?, "hello");
|
||||
assert_eq!(s.type_name(), "string");
|
||||
|
||||
let table: Value = lua.load("{}").eval()?;
|
||||
assert!(table.to_string()?.starts_with("table:"));
|
||||
@@ -119,18 +168,22 @@ fn test_value_to_string() -> Result<()> {
|
||||
.load("setmetatable({}, {__tostring = function() return 'test table' end})")
|
||||
.eval()?;
|
||||
assert_eq!(table.to_string()?, "test table");
|
||||
assert_eq!(table.type_name(), "table");
|
||||
|
||||
let func: Value = lua.load("function() end").eval()?;
|
||||
assert!(func.to_string()?.starts_with("function:"));
|
||||
assert_eq!(func.type_name(), "function");
|
||||
|
||||
let thread: Value = lua.load("coroutine.create(function() end)").eval()?;
|
||||
assert!(thread.to_string()?.starts_with("thread:"));
|
||||
assert_eq!(thread.type_name(), "thread");
|
||||
|
||||
lua.register_userdata_type::<StdString>(|reg| {
|
||||
reg.add_meta_method("__tostring", |_, this, ()| Ok(this.clone()));
|
||||
})?;
|
||||
let ud: Value = Value::UserData(lua.create_any_userdata(String::from("string userdata"))?);
|
||||
assert_eq!(ud.to_string()?, "string userdata");
|
||||
assert_eq!(ud.type_name(), "userdata");
|
||||
|
||||
struct MyUserData;
|
||||
impl UserData for MyUserData {}
|
||||
@@ -139,11 +192,13 @@ fn test_value_to_string() -> Result<()> {
|
||||
|
||||
let err = Value::Error(Box::new(Error::runtime("test error")));
|
||||
assert_eq!(err.to_string()?, "runtime error: test error");
|
||||
assert_eq!(err.type_name(), "error");
|
||||
|
||||
#[cfg(feature = "luau")]
|
||||
{
|
||||
let buf = Value::Buffer(lua.create_buffer(b"hello")?);
|
||||
assert!(buf.to_string()?.starts_with("buffer:"));
|
||||
assert_eq!(buf.type_name(), "buffer");
|
||||
|
||||
// Set `__tostring` metamethod for buffer
|
||||
let mt = lua.load("{__tostring = buffer.tostring}").eval()?;
|
||||
|
||||
Reference in New Issue
Block a user