mirror of
https://github.com/mlua-rs/mlua
synced 2026-06-08 16:05:43 +00:00
Compare commits
154 Commits
v0.9.0-rc.2
...
v0.9
| Author | SHA1 | Date | |
|---|---|---|---|
| 7d3704c222 | |||
| 496c8fa0e9 | |||
| 9ce0e11518 | |||
| b77836920a | |||
| f1ceaf0ff1 | |||
| 884a025b52 | |||
| c23fa5aa6c | |||
| 4f1d2abbcb | |||
| a25e81036e | |||
| b46cad1db1 | |||
| f2d48ce296 | |||
| 0fa39d431d | |||
| 0aa86c4d47 | |||
| ea2faa3755 | |||
| 59c9abbac7 | |||
| 8f3de8aa19 | |||
| 317ce7caa6 | |||
| 3a44729a48 | |||
| 3d46fad459 | |||
| ffc4bd599c | |||
| 1c969da286 | |||
| 45fd2fa40a | |||
| 806bd202d6 | |||
| a644049087 | |||
| ad31bed1db | |||
| 62f0bb97b0 | |||
| f67f8646ae | |||
| fa217d3706 | |||
| b62f2ee0f7 | |||
| 6e6c73e4c7 | |||
| 508517c45e | |||
| a79840afc9 | |||
| 39afe4c6f7 | |||
| 038cc5f974 | |||
| 59b14000f3 | |||
| 849206ef9d | |||
| 80fff4f2e7 | |||
| 9734146313 | |||
| 58be624222 | |||
| 3d43103431 | |||
| 5a22437d5f | |||
| 83c075c72b | |||
| 270b98a429 | |||
| 0ee3324462 | |||
| 8a9c4f0b15 | |||
| 34db5f985e | |||
| 020e8a78a8 | |||
| 3ca7b4942e | |||
| 1754226c74 | |||
| 3014c4d7a1 | |||
| 908f37656a | |||
| f4d783cb41 | |||
| f5982bc204 | |||
| e30b425224 | |||
| 512921404c | |||
| dfd82edc42 | |||
| 75a15ceabf | |||
| 60730fd068 | |||
| df778b7b33 | |||
| 45299c0ef1 | |||
| 38eec1236c | |||
| 145c5b316b | |||
| e97e69a309 | |||
| 2ac7b23596 | |||
| 8200bee467 | |||
| fe6ab250bf | |||
| 804972b099 | |||
| 727f99ee4d | |||
| 3c801e7b17 | |||
| a38e484fe9 | |||
| eed48889cd | |||
| b5896173fd | |||
| 205989f569 | |||
| 12472de1d2 | |||
| a68708c12e | |||
| 4c0474d573 | |||
| 60e859f643 | |||
| 9ed0d90746 | |||
| 514ec24252 | |||
| 244e6c9c12 | |||
| 4749e3a22a | |||
| cf153f38de | |||
| c0a0983025 | |||
| 69ff0c5509 | |||
| bf79d6c212 | |||
| 0b9a85e183 | |||
| 59974d7bde | |||
| 61e846326c | |||
| 3547985bb0 | |||
| 4c92580201 | |||
| e3f34f319c | |||
| b16f3895a0 | |||
| a4c919231c | |||
| e4d6e92287 | |||
| 642201a7e0 | |||
| c36808b251 | |||
| 2022de2156 | |||
| 66e01548ce | |||
| 93b505cff9 | |||
| 44f5688c32 | |||
| 2bee5ed33a | |||
| 2d775695ef | |||
| 34476ebf53 | |||
| 5043447f23 | |||
| b879abc418 | |||
| a1e39a8620 | |||
| f5021daaea | |||
| ec0fb7614e | |||
| 04c0763146 | |||
| 7d0aa7c01b | |||
| 2a3980ef62 | |||
| adb979761e | |||
| 37d722d71c | |||
| 6e3af593f6 | |||
| 54c14cc5b8 | |||
| 7f0de5790a | |||
| 6488477bc4 | |||
| 53b7b5b70b | |||
| fc159e0c46 | |||
| a802276c56 | |||
| 65b816f2f0 | |||
| e2b3464ec9 | |||
| 89cf5bf362 | |||
| bb0a6070c4 | |||
| 60f1c16ddb | |||
| 17809a390c | |||
| 7662a7e4ff | |||
| d769a95fc5 | |||
| 0e4476c2e3 | |||
| d48a2b3f6c | |||
| b3592bc23e | |||
| 052310e93d | |||
| 09eb7f251b | |||
| c137da7618 | |||
| c0c6a33f94 | |||
| 0cb0a345dd | |||
| 3e479be4e5 | |||
| 170aa53e29 | |||
| 021ee946fc | |||
| 0b928fdfee | |||
| e858384cd4 | |||
| c062cddd87 | |||
| 94a79656ad | |||
| cd0c8a4584 | |||
| 4fff14a144 | |||
| 196c09a0d6 | |||
| 114f072269 | |||
| b3211f13ee | |||
| 1f1463c482 | |||
| bec40ee5ea | |||
| dc94d51d97 | |||
| 3a096ae64a | |||
| a85e757d4d | |||
| 312886846c |
+34
-11
@@ -18,7 +18,7 @@ jobs:
|
||||
- os: windows-latest
|
||||
target: x86_64-pc-windows-msvc
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
toolchain: ${{ matrix.rust }}
|
||||
@@ -44,7 +44,7 @@ jobs:
|
||||
matrix:
|
||||
lua: [lua54, lua53, lua52, lua51, luajit]
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
toolchain: stable
|
||||
@@ -60,7 +60,7 @@ jobs:
|
||||
matrix:
|
||||
lua: [lua54, lua53, lua52, lua51, luajit]
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
toolchain: stable
|
||||
@@ -82,7 +82,7 @@ jobs:
|
||||
matrix:
|
||||
lua: [lua54, lua53, lua52, lua51]
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
toolchain: stable
|
||||
@@ -113,7 +113,7 @@ jobs:
|
||||
- os: windows-latest
|
||||
target: x86_64-pc-windows-msvc
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
toolchain: ${{ matrix.rust }}
|
||||
@@ -145,7 +145,7 @@ jobs:
|
||||
- os: ubuntu-22.04
|
||||
target: x86_64-unknown-linux-gnu
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
toolchain: ${{ matrix.rust }}
|
||||
@@ -166,14 +166,14 @@ jobs:
|
||||
matrix:
|
||||
os: [ubuntu-22.04, macos-latest]
|
||||
rust: [stable]
|
||||
lua: [lua54, lua53, lua52, lua51, luajit]
|
||||
lua: [lua54, lua53, lua52, lua51, luajit, luau]
|
||||
include:
|
||||
- os: ubuntu-22.04
|
||||
target: x86_64-unknown-linux-gnu
|
||||
- os: macos-latest
|
||||
target: x86_64-apple-darwin
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
toolchain: ${{ matrix.rust }}
|
||||
@@ -197,7 +197,7 @@ jobs:
|
||||
shell: msys2 {0}
|
||||
steps:
|
||||
- uses: msys2/setup-msys2@v2
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install Rust & Lua
|
||||
run: |
|
||||
pacman -S --noconfirm mingw-w64-x86_64-rust mingw-w64-x86_64-lua mingw-w64-x86_64-luajit mingw-w64-x86_64-pkg-config
|
||||
@@ -206,11 +206,34 @@ jobs:
|
||||
(cd tests/module && cargo build --release --features "${{ matrix.lua }}")
|
||||
(cd tests/module/loader && cargo test --release --features "${{ matrix.lua }}")
|
||||
|
||||
test_wasm32_emscripten:
|
||||
name: Test on wasm32-unknown-emscripten
|
||||
runs-on: ubuntu-22.04
|
||||
needs: build
|
||||
strategy:
|
||||
matrix:
|
||||
lua: [lua54, lua53, lua52, lua51, luau]
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
toolchain: stable
|
||||
target: wasm32-unknown-emscripten
|
||||
- name: Install Emscripten
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y --no-install-recommends emscripten
|
||||
- name: Run ${{ matrix.lua }} tests
|
||||
run: |
|
||||
cargo test --tests --features "${{ matrix.lua }},vendored"
|
||||
cargo test --tests --features "${{ matrix.lua }},vendored,async,send,serialize,macros,parking_lot"
|
||||
cargo test --tests --features "${{ matrix.lua }},vendored,async,serialize,macros,parking_lot,unstable"
|
||||
|
||||
rustfmt:
|
||||
name: Rustfmt
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
toolchain: stable
|
||||
@@ -224,7 +247,7 @@ jobs:
|
||||
matrix:
|
||||
lua: [lua54, lua53, lua52, lua51, luajit, luau, luau-jit, luau-vector4]
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
toolchain: nightly
|
||||
|
||||
@@ -1,3 +1,86 @@
|
||||
## v0.9.9
|
||||
|
||||
- Minimal Luau updated to 0.629
|
||||
- Fixed bug when attempting to reset or resume already running coroutines (#416).
|
||||
- Added `RegistryKey::id()` method to get the underlying Lua registry key id.
|
||||
|
||||
## v0.9.8
|
||||
|
||||
- Fixed serializing same table multiple times (#408)
|
||||
- Use `mlua-sys` v0.6 (to support Luau 0.624+)
|
||||
- Fixed cross compilation of windows dlls from unix (#394)
|
||||
|
||||
## v0.9.7
|
||||
|
||||
- Implemented `IntoLua` for `RegistryKey`
|
||||
- Mark `__idiv` metamethod as available for luau
|
||||
- Added `Function::deep_clone()` method (Luau)
|
||||
- Added `SerializeOptions::detect_serde_json_arbitrary_precision` option
|
||||
- Added `Lua::create_buffer()` method (Luau)
|
||||
- Support serializing buffer type as a byte slice (Luau)
|
||||
- Perf: Implemented `push_into_stack`/`from_stack` for `Option<T>`
|
||||
- Added `Lua::create_ser_any_userdata()` method
|
||||
|
||||
## v0.9.6
|
||||
|
||||
- Added `to_pointer` function to `Function`/`Table`/`Thread`
|
||||
- Implemented `IntoLua` for `&Value`
|
||||
- Implemented `FromLua` for `RegistryKey`
|
||||
- Faster (~5%) table array traversal during serialization
|
||||
- Some performance improvements for bool/int types
|
||||
|
||||
## v0.9.5
|
||||
|
||||
- Minimal Luau updated to 0.609
|
||||
- Luau max stack size increased to 1M (from 100K)
|
||||
- Implemented `IntoLua` for refs to `String`/`Table`/`Function`/`AnyUserData`/`Thread` + `RegistryKey`
|
||||
- Implemented `IntoLua` and `FromLua` for `OwnedThread`/`OwnedString`
|
||||
- Fixed `FromLua` derive proc macro to cover more cases
|
||||
|
||||
## v0.9.4
|
||||
|
||||
- Fixed loading all-in-one modules under mixed states (eg. main state and coroutines)
|
||||
|
||||
## v0.9.3
|
||||
|
||||
- WebAssembly support (`wasm32-unknown-emscripten` target)
|
||||
- Performance improvements (faster Lua function calls for lua51/jit/luau)
|
||||
|
||||
## v0.9.2
|
||||
|
||||
- Added binary modules support to Luau
|
||||
- Added Luau package module (uses `StdLib::PACKAGE`) with loaders (follows lua5.1 interface)
|
||||
- Added support of Luau 0.601+ buffer type (represented as userdata in Rust)
|
||||
- LuaJIT `cdata` type is also represented as userdata in Rust (instead of panic)
|
||||
- Vendored LuaJIT switched to rolling vanilla (from openresty)
|
||||
- Added `Table::for_each` method for fast table pairs traversal (faster than `pairs`)
|
||||
- Performance improvements around table traversal (and faster serialization)
|
||||
- Bug fixes and improvements
|
||||
|
||||
## v0.9.1
|
||||
|
||||
- impl Default for Lua
|
||||
- impl IntoLuaMulti for `std::result::Result<(), E>`
|
||||
- Fix using wrong userdata index after processing Variadic args (#311)
|
||||
|
||||
## v0.9.0
|
||||
|
||||
Changes since v0.9.0-rc.3
|
||||
|
||||
- Improved non-static (scoped) userdata support
|
||||
- Added `Scope::create_any_userdata()` method
|
||||
- Added `Lua::set_vector_metatable()` method (`unstable` feature flag)
|
||||
- Added `OwnedThread` type (`unstable` feature flag)
|
||||
- Minimal Luau updated to 0.590
|
||||
- Added new option `sort_keys` to `DeserializeOptions` (`Lua::from_value()` method)
|
||||
- Changed `Table::raw_len()` output type to `usize`
|
||||
- Helper functions for `Value` (eg: `Value::as_number()`/`Value::as_string`/etc)
|
||||
- Performance improvements
|
||||
|
||||
## v0.9.0-rc.3
|
||||
|
||||
- Minimal Luau updated to 0.588
|
||||
|
||||
## v0.9.0-rc.2
|
||||
|
||||
- Added `#[derive(FromLua)]` macro to opt-in into `FromLua<T> where T: 'static + Clone` (userdata type).
|
||||
@@ -81,6 +164,18 @@ Other:
|
||||
- Support setting module name in `#[lua_module(name = "...")]` macro
|
||||
- Minor fixes and improvements
|
||||
|
||||
## v0.8.10
|
||||
|
||||
- Update to Luau 0.590 (luau0-src to 0.7.x)
|
||||
- Fix loading luau code starting with \t
|
||||
- Pin lua-src and luajit-src versions
|
||||
|
||||
## v0.8.9
|
||||
|
||||
- Update minimal (vendored) Lua 5.4 to 5.4.6
|
||||
- Use `lua_closethread` instead of `lua_resetthread` in vendored mode (Lua 5.4.6)
|
||||
- Allow deserializing Lua null into unit (`()`) or unit struct.
|
||||
|
||||
## v0.8.8
|
||||
|
||||
- Fix potential deadlock when trying to reuse dropped registry keys.
|
||||
|
||||
+33
-17
@@ -1,7 +1,7 @@
|
||||
[package]
|
||||
name = "mlua"
|
||||
version = "0.9.0-rc.2" # remember to update mlua_derive
|
||||
authors = ["Aleksandr Orlenko <zxteam@pm.me>", "kyren <catherine@chucklefish.org>"]
|
||||
version = "0.9.9" # remember to update mlua_derive
|
||||
authors = ["Aleksandr Orlenko <zxteam@pm.me>", "kyren <catherine@kyju.org>"]
|
||||
rust-version = "1.71"
|
||||
edition = "2021"
|
||||
repository = "https://github.com/khvzak/mlua"
|
||||
@@ -32,50 +32,66 @@ lua52 = ["ffi/lua52"]
|
||||
lua51 = ["ffi/lua51"]
|
||||
luajit = ["ffi/luajit"]
|
||||
luajit52 = ["luajit", "ffi/luajit52"]
|
||||
luau = ["ffi/luau"]
|
||||
luau = ["ffi/luau", "dep:libloading"]
|
||||
luau-jit = ["luau", "ffi/luau-codegen"]
|
||||
luau-vector4 = ["luau", "ffi/luau-vector4"]
|
||||
vendored = ["ffi/vendored"]
|
||||
module = ["mlua_derive", "ffi/module"]
|
||||
async = ["futures-util"]
|
||||
module = ["dep:mlua_derive", "ffi/module"]
|
||||
async = ["dep:futures-util"]
|
||||
send = []
|
||||
serialize = ["serde", "erased-serde", "serde-value"]
|
||||
serialize = ["dep:serde", "dep:erased-serde", "dep:serde-value"]
|
||||
macros = ["mlua_derive/macros"]
|
||||
unstable = []
|
||||
|
||||
[dependencies]
|
||||
mlua_derive = { version = "=0.9.0-rc.2", optional = true, path = "mlua_derive" }
|
||||
bstr = { version = "1.0", features = ["std"], default_features = false }
|
||||
mlua_derive = { version = "=0.9.3", optional = true, path = "mlua_derive" }
|
||||
bstr = { version = "1.0", features = ["std"], default-features = false }
|
||||
once_cell = { version = "1.0" }
|
||||
num-traits = { version = "0.2.14" }
|
||||
rustc-hash = "1.0"
|
||||
rustc-hash = "2.0"
|
||||
futures-util = { version = "0.3", optional = true, default-features = false, features = ["std"] }
|
||||
serde = { version = "1.0", optional = true }
|
||||
erased-serde = { version = "0.3", optional = true }
|
||||
erased-serde = { version = "0.4", optional = true }
|
||||
serde-value = { version = "0.7", optional = true }
|
||||
parking_lot = { version = "0.12", optional = true }
|
||||
|
||||
ffi = { package = "mlua-sys", version = "0.3.0", path = "mlua-sys" }
|
||||
ffi = { package = "mlua-sys", version = "0.6.1", path = "mlua-sys" }
|
||||
|
||||
[target.'cfg(unix)'.dependencies]
|
||||
libloading = { version = "0.8", optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
rustyline = "12.0"
|
||||
criterion = { version = "0.5", features = ["async_tokio"] }
|
||||
trybuild = "1.0"
|
||||
futures = "0.3.5"
|
||||
hyper = { version = "0.14", features = ["client", "server"] }
|
||||
reqwest = { version = "0.11", features = ["json"] }
|
||||
tokio = { version = "1.0", features = ["full"] }
|
||||
hyper = { version = "1.2", features = ["full"] }
|
||||
hyper-util = { version = "0.1.3", features = ["full"] }
|
||||
http-body-util = "0.1.1"
|
||||
reqwest = { version = "0.12", features = ["json"] }
|
||||
tokio = { version = "1.0", features = ["macros", "rt", "time"] }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
serde_json = { version = "1.0", features = ["arbitrary_precision"] }
|
||||
maplit = "1.0"
|
||||
tempfile = "3"
|
||||
static_assertions = "1.0"
|
||||
|
||||
[target.'cfg(not(target_arch = "wasm32"))'.dev-dependencies]
|
||||
criterion = { version = "0.5", features = ["async_tokio"] }
|
||||
rustyline = "14.0"
|
||||
tokio = { version = "1.0", features = ["full"] }
|
||||
|
||||
[lints.rust]
|
||||
unexpected_cfgs = { level = "allow", check-cfg = ['cfg(tarpaulin_include)'] }
|
||||
|
||||
[[bench]]
|
||||
name = "benchmark"
|
||||
harness = false
|
||||
required-features = ["async"]
|
||||
|
||||
[[bench]]
|
||||
name = "serde"
|
||||
harness = false
|
||||
required-features = ["serialize"]
|
||||
|
||||
[[example]]
|
||||
name = "async_http_client"
|
||||
required-features = ["async", "macros"]
|
||||
|
||||
@@ -7,8 +7,8 @@
|
||||
[crates.io]: https://crates.io/crates/mlua
|
||||
[API Documentation]: https://docs.rs/mlua/badge.svg
|
||||
[docs.rs]: https://docs.rs/mlua
|
||||
[Coverage Status]: https://codecov.io/gh/khvzak/mlua/branch/master/graph/badge.svg?token=99339FS1CG
|
||||
[codecov.io]: https://codecov.io/gh/khvzak/mlua
|
||||
[Coverage Status]: https://codecov.io/gh/mlua-rs/mlua/branch/main/graph/badge.svg?token=99339FS1CG
|
||||
[codecov.io]: https://codecov.io/gh/mlua-rs/mlua
|
||||
[MSRV]: https://img.shields.io/badge/rust-1.71+-brightgreen.svg?&logo=rust
|
||||
|
||||
[Guided Tour] | [Benchmarks] | [FAQ]
|
||||
@@ -19,9 +19,7 @@
|
||||
|
||||
> **Note**
|
||||
>
|
||||
> Please see the [v0.8](https://github.com/khvzak/mlua/tree/v0.8) branch for the stable versions of `mlua` released to crates.io.
|
||||
>
|
||||
> v0.9 release notes can be found [here](docs/release_notes/v0.9.md).
|
||||
> See v0.9 [release notes](https://github.com/khvzak/mlua/blob/main/docs/release_notes/v0.9.md).
|
||||
|
||||
`mlua` is bindings to [Lua](https://www.lua.org) programming language for Rust with a goal to provide
|
||||
_safe_ (as far as it's possible), high level, easy to use, practical and flexible API.
|
||||
@@ -30,6 +28,8 @@ Started as `rlua` fork, `mlua` supports Lua 5.4, 5.3, 5.2, 5.1 (including LuaJIT
|
||||
|
||||
`mlua` tested on Windows/macOS/Linux including module mode in [GitHub Actions] on `x86_64` platform and cross-compilation to `aarch64` (other targets are also supported).
|
||||
|
||||
WebAssembly (WASM) is supported through `wasm32-unknown-emscripten` target for all Lua versions excluding JIT.
|
||||
|
||||
[GitHub Actions]: https://github.com/khvzak/mlua/actions
|
||||
[Roblox Luau]: https://luau-lang.org
|
||||
|
||||
@@ -47,7 +47,7 @@ Below is a list of the available feature flags. By default `mlua` does not enabl
|
||||
* `luajit`: activate [LuaJIT] support
|
||||
* `luajit52`: activate [LuaJIT] support with partial compatibility with Lua 5.2
|
||||
* `luau`: activate [Luau] support (auto vendored mode)
|
||||
* `luau-jit`: activate [Luau] support with experimental jit backend. This is unstable feature and not recommended to use.
|
||||
* `luau-jit`: activate [Luau] support with JIT backend.
|
||||
* `luau-vector4`: activate [Luau] support with 4-dimensional vector.
|
||||
* `vendored`: build static Lua(JIT) library from sources during `mlua` compilation using [lua-src] or [luajit-src] crates
|
||||
* `module`: enable module mode (building loadable `cdylib` library for Lua)
|
||||
@@ -84,6 +84,20 @@ This works using Lua [coroutines](https://www.lua.org/manual/5.3/manual.html#2.6
|
||||
- [HTTP Server](examples/async_http_server.rs)
|
||||
- [TCP Server](examples/async_tcp_server.rs)
|
||||
|
||||
|
||||
**shell command examples**:
|
||||
```shell
|
||||
# async http client (hyper)
|
||||
cargo run --example async_http_client --features=lua54,async,macros
|
||||
|
||||
# async http client (reqwest)
|
||||
cargo run --example async_http_reqwest --features=lua54,async,macros,serialize
|
||||
|
||||
# async http server
|
||||
cargo run --example async_http_server --features=lua54,async,macros
|
||||
curl -v http://localhost:3000
|
||||
```
|
||||
|
||||
### Serialization (serde) support
|
||||
|
||||
With `serialize` feature flag enabled, `mlua` allows you to serialize/deserialize any type that implements [`serde::Serialize`] and [`serde::Deserialize`] into/from [`mlua::Value`]. In addition `mlua` provides [`serde::Serialize`] trait implementation for it (including `UserData` support).
|
||||
@@ -119,7 +133,7 @@ Add to `Cargo.toml` :
|
||||
|
||||
``` toml
|
||||
[dependencies]
|
||||
mlua = { version = "0.9.0-rc.2", features = ["lua54", "vendored"] }
|
||||
mlua = { version = "0.9.9", features = ["lua54", "vendored"] }
|
||||
```
|
||||
|
||||
`main.rs`
|
||||
@@ -154,7 +168,7 @@ Add to `Cargo.toml` :
|
||||
crate-type = ["cdylib"]
|
||||
|
||||
[dependencies]
|
||||
mlua = { version = "0.9.0-rc.2", features = ["lua54", "vendored", "module"] }
|
||||
mlua = { version = "0.9.9", features = ["lua54", "module"] }
|
||||
```
|
||||
|
||||
`lib.rs` :
|
||||
@@ -210,7 +224,11 @@ Module builds don't require Lua lib or headers to be installed on the system.
|
||||
There is a LuaRocks build backend for mlua modules [`luarocks-build-rust-mlua`].
|
||||
|
||||
Modules written in Rust and published to luarocks:
|
||||
- [`decasify`](https://github.com/alerque/decasify)
|
||||
- [`lua-ryaml`](https://github.com/khvzak/lua-ryaml)
|
||||
- [`tiktoken_core`](https://github.com/gptlang/lua-tiktoken)
|
||||
- [`toml-edit`](https://github.com/vhyrro/toml-edit.lua)
|
||||
- [`typst-lua`](https://github.com/rousbound/typst-lua)
|
||||
|
||||
[`luarocks-build-rust-mlua`]: https://luarocks.org/modules/khvzak/luarocks-build-rust-mlua
|
||||
|
||||
|
||||
+269
-155
@@ -1,5 +1,7 @@
|
||||
use criterion::{criterion_group, criterion_main, BatchSize, Criterion};
|
||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||
use std::time::Duration;
|
||||
|
||||
use criterion::{criterion_group, criterion_main, BatchSize, Criterion};
|
||||
use tokio::runtime::Runtime;
|
||||
use tokio::task;
|
||||
|
||||
@@ -10,10 +12,10 @@ fn collect_gc_twice(lua: &Lua) {
|
||||
lua.gc_collect().unwrap();
|
||||
}
|
||||
|
||||
fn create_table(c: &mut Criterion) {
|
||||
fn table_create_empty(c: &mut Criterion) {
|
||||
let lua = Lua::new();
|
||||
|
||||
c.bench_function("create [table empty]", |b| {
|
||||
c.bench_function("table [create empty]", |b| {
|
||||
b.iter_batched(
|
||||
|| collect_gc_twice(&lua),
|
||||
|_| {
|
||||
@@ -24,71 +26,55 @@ fn create_table(c: &mut Criterion) {
|
||||
});
|
||||
}
|
||||
|
||||
fn create_array(c: &mut Criterion) {
|
||||
fn table_create_array(c: &mut Criterion) {
|
||||
let lua = Lua::new();
|
||||
|
||||
c.bench_function("create [array] 10", |b| {
|
||||
c.bench_function("table [create array]", |b| {
|
||||
b.iter_batched(
|
||||
|| collect_gc_twice(&lua),
|
||||
|_| {
|
||||
let table = lua.create_table().unwrap();
|
||||
for i in 1..=10 {
|
||||
table.set(i, i).unwrap();
|
||||
}
|
||||
lua.create_sequence_from(1..=10).unwrap();
|
||||
},
|
||||
BatchSize::SmallInput,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
fn create_string_table(c: &mut Criterion) {
|
||||
fn table_create_hash(c: &mut Criterion) {
|
||||
let lua = Lua::new();
|
||||
|
||||
c.bench_function("create [table string] 10", |b| {
|
||||
c.bench_function("table [create hash]", |b| {
|
||||
b.iter_batched(
|
||||
|| collect_gc_twice(&lua),
|
||||
|_| {
|
||||
let table = lua.create_table().unwrap();
|
||||
for &s in &["1", "2", "3", "4", "5", "6", "7", "8", "9", "10"] {
|
||||
let s = lua.create_string(s).unwrap();
|
||||
table.set(s.clone(), s).unwrap();
|
||||
}
|
||||
lua.create_table_from(
|
||||
["1", "2", "3", "4", "5", "6", "7", "8", "9", "10"]
|
||||
.into_iter()
|
||||
.map(|s| (s, s)),
|
||||
)
|
||||
.unwrap();
|
||||
},
|
||||
BatchSize::SmallInput,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
fn create_function(c: &mut Criterion) {
|
||||
fn table_get_set(c: &mut Criterion) {
|
||||
let lua = Lua::new();
|
||||
|
||||
c.bench_function("create [function] 10", |b| {
|
||||
c.bench_function("table [get and set]", |b| {
|
||||
b.iter_batched(
|
||||
|| collect_gc_twice(&lua),
|
||||
|_| {
|
||||
for i in 0..10 {
|
||||
lua.create_function(move |_, ()| Ok(i)).unwrap();
|
||||
}
|
||||
},
|
||||
BatchSize::SmallInput,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
fn call_lua_function(c: &mut Criterion) {
|
||||
let lua = Lua::new();
|
||||
|
||||
c.bench_function("call Lua function [sum] 3 10", |b| {
|
||||
b.iter_batched_ref(
|
||||
|| {
|
||||
collect_gc_twice(&lua);
|
||||
lua.load("function(a, b, c) return a + b + c end")
|
||||
.eval::<LuaFunction>()
|
||||
.unwrap()
|
||||
lua.create_table().unwrap()
|
||||
},
|
||||
|function| {
|
||||
for i in 0..10 {
|
||||
let _result: i64 = function.call((i, i + 1, i + 2)).unwrap();
|
||||
|table| {
|
||||
for (i, s) in ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j"]
|
||||
.into_iter()
|
||||
.enumerate()
|
||||
{
|
||||
table.raw_set(s, i).unwrap();
|
||||
assert_eq!(table.raw_get::<_, usize>(s).unwrap(), i);
|
||||
}
|
||||
},
|
||||
BatchSize::SmallInput,
|
||||
@@ -96,199 +82,318 @@ fn call_lua_function(c: &mut Criterion) {
|
||||
});
|
||||
}
|
||||
|
||||
fn call_sum_callback(c: &mut Criterion) {
|
||||
fn table_traversal_pairs(c: &mut Criterion) {
|
||||
let lua = Lua::new();
|
||||
let callback = lua
|
||||
.create_function(|_, (a, b, c): (i64, i64, i64)| Ok(a + b + c))
|
||||
|
||||
c.bench_function("table [traversal pairs]", |b| {
|
||||
b.iter_batched(
|
||||
|| lua.globals(),
|
||||
|globals| {
|
||||
for kv in globals.pairs::<String, LuaValue>() {
|
||||
let (_k, _v) = kv.unwrap();
|
||||
}
|
||||
},
|
||||
BatchSize::SmallInput,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
fn table_traversal_for_each(c: &mut Criterion) {
|
||||
let lua = Lua::new();
|
||||
|
||||
c.bench_function("table [traversal for_each]", |b| {
|
||||
b.iter_batched(
|
||||
|| lua.globals(),
|
||||
|globals| globals.for_each::<String, LuaValue>(|_k, _v| Ok(())),
|
||||
BatchSize::SmallInput,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
fn table_traversal_sequence(c: &mut Criterion) {
|
||||
let lua = Lua::new();
|
||||
|
||||
let table = lua.create_sequence_from(1..1000).unwrap();
|
||||
|
||||
c.bench_function("table [traversal sequence]", |b| {
|
||||
b.iter_batched(
|
||||
|| table.clone(),
|
||||
|table| {
|
||||
for v in table.sequence_values::<i32>() {
|
||||
let _i = v.unwrap();
|
||||
}
|
||||
},
|
||||
BatchSize::SmallInput,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
fn function_create(c: &mut Criterion) {
|
||||
let lua = Lua::new();
|
||||
|
||||
c.bench_function("function [create Rust]", |b| {
|
||||
b.iter_batched(
|
||||
|| collect_gc_twice(&lua),
|
||||
|_| {
|
||||
lua.create_function(|_, ()| Ok(123)).unwrap();
|
||||
},
|
||||
BatchSize::SmallInput,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
fn function_call_sum(c: &mut Criterion) {
|
||||
let lua = Lua::new();
|
||||
|
||||
let sum = lua
|
||||
.create_function(|_, (a, b, c): (i64, i64, i64)| Ok(a + b - c))
|
||||
.unwrap();
|
||||
lua.globals().set("callback", callback).unwrap();
|
||||
|
||||
c.bench_function("call Rust callback [sum] 3 10", |b| {
|
||||
b.iter_batched_ref(
|
||||
|| {
|
||||
collect_gc_twice(&lua);
|
||||
lua.load("function() for i = 1,10 do callback(i, i+1, i+2) end end")
|
||||
.eval::<LuaFunction>()
|
||||
.unwrap()
|
||||
},
|
||||
|function| {
|
||||
function.call::<_, ()>(()).unwrap();
|
||||
c.bench_function("function [call Rust sum]", |b| {
|
||||
b.iter_batched(
|
||||
|| collect_gc_twice(&lua),
|
||||
|_| {
|
||||
assert_eq!(sum.call::<_, i64>((10, 20, 30)).unwrap(), 0);
|
||||
},
|
||||
BatchSize::SmallInput,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
fn call_async_sum_callback(c: &mut Criterion) {
|
||||
let options = LuaOptions::new().thread_pool_size(1024);
|
||||
let lua = Lua::new_with(LuaStdLib::ALL_SAFE, options).unwrap();
|
||||
let callback = lua
|
||||
.create_async_function(|_, (a, b, c): (i64, i64, i64)| async move {
|
||||
task::yield_now().await;
|
||||
Ok(a + b + c)
|
||||
})
|
||||
.unwrap();
|
||||
lua.globals().set("callback", callback).unwrap();
|
||||
|
||||
c.bench_function("call async Rust callback [sum] 3 10", |b| {
|
||||
let rt = Runtime::new().unwrap();
|
||||
b.to_async(rt).iter_batched(
|
||||
|| {
|
||||
collect_gc_twice(&lua);
|
||||
lua.load("function() for i = 1,10 do callback(i, i+1, i+2) end end")
|
||||
.eval::<LuaFunction>()
|
||||
.unwrap()
|
||||
},
|
||||
|function| async move {
|
||||
function.call_async::<_, ()>(()).await.unwrap();
|
||||
},
|
||||
BatchSize::SmallInput,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
fn call_concat_callback(c: &mut Criterion) {
|
||||
fn function_call_lua_sum(c: &mut Criterion) {
|
||||
let lua = Lua::new();
|
||||
let callback = lua
|
||||
|
||||
let sum = lua
|
||||
.load("function(a, b, c) return a + b - c end")
|
||||
.eval::<LuaFunction>()
|
||||
.unwrap();
|
||||
|
||||
c.bench_function("function [call Lua sum]", |b| {
|
||||
b.iter_batched(
|
||||
|| collect_gc_twice(&lua),
|
||||
|_| {
|
||||
assert_eq!(sum.call::<_, i64>((10, 20, 30)).unwrap(), 0);
|
||||
},
|
||||
BatchSize::SmallInput,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
fn function_call_concat(c: &mut Criterion) {
|
||||
let lua = Lua::new();
|
||||
|
||||
let concat = lua
|
||||
.create_function(|_, (a, b): (LuaString, LuaString)| {
|
||||
Ok(format!("{}{}", a.to_str()?, b.to_str()?))
|
||||
})
|
||||
.unwrap();
|
||||
lua.globals().set("callback", callback).unwrap();
|
||||
let i = AtomicUsize::new(0);
|
||||
|
||||
c.bench_function("call Rust callback [concat string] 10", |b| {
|
||||
b.iter_batched_ref(
|
||||
c.bench_function("function [call Rust concat string]", |b| {
|
||||
b.iter_batched(
|
||||
|| {
|
||||
collect_gc_twice(&lua);
|
||||
lua.load("function() for i = 1,10 do callback('a', tostring(i)) end end")
|
||||
.eval::<LuaFunction>()
|
||||
.unwrap()
|
||||
i.fetch_add(1, Ordering::Relaxed)
|
||||
},
|
||||
|function| {
|
||||
function.call::<_, ()>(()).unwrap();
|
||||
|i| {
|
||||
assert_eq!(
|
||||
concat.call::<_, LuaString>(("num:", i)).unwrap(),
|
||||
format!("num:{i}")
|
||||
);
|
||||
},
|
||||
BatchSize::SmallInput,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
fn create_registry_values(c: &mut Criterion) {
|
||||
fn function_call_lua_concat(c: &mut Criterion) {
|
||||
let lua = Lua::new();
|
||||
|
||||
c.bench_function("create [registry value] 10", |b| {
|
||||
let concat = lua
|
||||
.load("function(a, b) return a..b end")
|
||||
.eval::<LuaFunction>()
|
||||
.unwrap();
|
||||
let i = AtomicUsize::new(0);
|
||||
|
||||
c.bench_function("function [call Lua concat string]", |b| {
|
||||
b.iter_batched(
|
||||
|| {
|
||||
collect_gc_twice(&lua);
|
||||
i.fetch_add(1, Ordering::Relaxed)
|
||||
},
|
||||
|i| {
|
||||
assert_eq!(
|
||||
concat.call::<_, LuaString>(("num:", i)).unwrap(),
|
||||
format!("num:{i}")
|
||||
);
|
||||
},
|
||||
BatchSize::SmallInput,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
fn function_async_call_sum(c: &mut Criterion) {
|
||||
let options = LuaOptions::new().thread_pool_size(1024);
|
||||
let lua = Lua::new_with(LuaStdLib::ALL_SAFE, options).unwrap();
|
||||
|
||||
let sum = lua
|
||||
.create_async_function(|_, (a, b, c): (i64, i64, i64)| async move {
|
||||
task::yield_now().await;
|
||||
Ok(a + b - c)
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
c.bench_function("function [async call Rust sum]", |b| {
|
||||
let rt = Runtime::new().unwrap();
|
||||
b.to_async(rt).iter_batched(
|
||||
|| collect_gc_twice(&lua),
|
||||
|_| async {
|
||||
assert_eq!(sum.call_async::<_, i64>((10, 20, 30)).await.unwrap(), 0);
|
||||
},
|
||||
BatchSize::SmallInput,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
fn registry_value_create(c: &mut Criterion) {
|
||||
let lua = Lua::new();
|
||||
lua.gc_stop();
|
||||
|
||||
c.bench_function("registry value [create]", |b| {
|
||||
b.iter_batched(
|
||||
|| collect_gc_twice(&lua),
|
||||
|_| lua.create_registry_value("hello").unwrap(),
|
||||
BatchSize::SmallInput,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
fn registry_value_get(c: &mut Criterion) {
|
||||
let lua = Lua::new();
|
||||
lua.gc_stop();
|
||||
|
||||
let value = lua.create_registry_value("hello").unwrap();
|
||||
|
||||
c.bench_function("registry value [get]", |b| {
|
||||
b.iter_batched(
|
||||
|| collect_gc_twice(&lua),
|
||||
|_| {
|
||||
for _ in 0..10 {
|
||||
lua.create_registry_value(lua.pack(true).unwrap()).unwrap();
|
||||
}
|
||||
lua.expire_registry_values();
|
||||
assert_eq!(lua.registry_value::<LuaString>(&value).unwrap(), "hello");
|
||||
},
|
||||
BatchSize::SmallInput,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
fn create_userdata(c: &mut Criterion) {
|
||||
struct UserData(i64);
|
||||
fn userdata_create(c: &mut Criterion) {
|
||||
struct UserData(#[allow(unused)] i64);
|
||||
impl LuaUserData for UserData {}
|
||||
|
||||
let lua = Lua::new();
|
||||
|
||||
c.bench_function("create [table userdata] 10", |b| {
|
||||
c.bench_function("userdata [create]", |b| {
|
||||
b.iter_batched(
|
||||
|| collect_gc_twice(&lua),
|
||||
|_| {
|
||||
let table: LuaTable = lua.create_table().unwrap();
|
||||
for i in 1..11 {
|
||||
table.set(i, UserData(i)).unwrap();
|
||||
}
|
||||
lua.create_userdata(UserData(123)).unwrap();
|
||||
},
|
||||
BatchSize::SmallInput,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
fn call_userdata_index(c: &mut Criterion) {
|
||||
struct UserData(i64);
|
||||
fn userdata_call_index(c: &mut Criterion) {
|
||||
struct UserData(#[allow(unused)] i64);
|
||||
impl LuaUserData for UserData {
|
||||
fn add_methods<'lua, M: LuaUserDataMethods<'lua, Self>>(methods: &mut M) {
|
||||
methods.add_meta_method(LuaMetaMethod::Index, move |_, _, index: String| Ok(index));
|
||||
methods.add_meta_method(LuaMetaMethod::Index, move |_, _, key: LuaString| Ok(key));
|
||||
}
|
||||
}
|
||||
|
||||
let lua = Lua::new();
|
||||
lua.globals().set("userdata", UserData(10)).unwrap();
|
||||
let ud = lua.create_userdata(UserData(123)).unwrap();
|
||||
let index = lua
|
||||
.load("function(ud) return ud.test end")
|
||||
.eval::<LuaFunction>()
|
||||
.unwrap();
|
||||
|
||||
c.bench_function("call [userdata index] 10", |b| {
|
||||
b.iter_batched_ref(
|
||||
|| {
|
||||
collect_gc_twice(&lua);
|
||||
lua.load("function() for i = 1,10 do local v = userdata.test end end")
|
||||
.eval::<LuaFunction>()
|
||||
.unwrap()
|
||||
},
|
||||
|function| {
|
||||
function.call::<_, ()>(()).unwrap();
|
||||
c.bench_function("userdata [call index]", |b| {
|
||||
b.iter_batched(
|
||||
|| collect_gc_twice(&lua),
|
||||
|_| {
|
||||
assert_eq!(index.call::<_, LuaString>(&ud).unwrap(), "test");
|
||||
},
|
||||
BatchSize::SmallInput,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
fn call_userdata_method(c: &mut Criterion) {
|
||||
fn userdata_call_method(c: &mut Criterion) {
|
||||
struct UserData(i64);
|
||||
impl LuaUserData for UserData {
|
||||
fn add_methods<'lua, M: LuaUserDataMethods<'lua, Self>>(methods: &mut M) {
|
||||
methods.add_method("method", |_, this, ()| Ok(this.0));
|
||||
methods.add_method("add", |_, this, i: i64| Ok(this.0 + i));
|
||||
}
|
||||
}
|
||||
|
||||
let lua = Lua::new();
|
||||
lua.globals().set("userdata", UserData(10)).unwrap();
|
||||
let ud = lua.create_userdata(UserData(123)).unwrap();
|
||||
let method = lua
|
||||
.load("function(ud, i) return ud:add(i) end")
|
||||
.eval::<LuaFunction>()
|
||||
.unwrap();
|
||||
let i = AtomicUsize::new(0);
|
||||
|
||||
c.bench_function("call [userdata method] 10", |b| {
|
||||
b.iter_batched_ref(
|
||||
c.bench_function("userdata [call method]", |b| {
|
||||
b.iter_batched(
|
||||
|| {
|
||||
collect_gc_twice(&lua);
|
||||
lua.load("function() for i = 1,10 do userdata:method() end end")
|
||||
.eval::<LuaFunction>()
|
||||
.unwrap()
|
||||
i.fetch_add(1, Ordering::Relaxed)
|
||||
},
|
||||
|function| {
|
||||
function.call::<_, ()>(()).unwrap();
|
||||
|i| {
|
||||
assert_eq!(method.call::<_, usize>((&ud, i)).unwrap(), 123 + i);
|
||||
},
|
||||
BatchSize::SmallInput,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
fn call_async_userdata_method(c: &mut Criterion) {
|
||||
struct UserData(String);
|
||||
|
||||
fn userdata_async_call_method(c: &mut Criterion) {
|
||||
struct UserData(i64);
|
||||
impl LuaUserData for UserData {
|
||||
fn add_methods<'lua, M: LuaUserDataMethods<'lua, Self>>(methods: &mut M) {
|
||||
methods.add_async_method("method", |_, this, ()| async move { Ok(this.0.clone()) });
|
||||
methods.add_async_method("add", |_, this, i: i64| async move {
|
||||
task::yield_now().await;
|
||||
Ok(this.0 + i)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
let options = LuaOptions::new().thread_pool_size(1024);
|
||||
let lua = Lua::new_with(LuaStdLib::ALL_SAFE, options).unwrap();
|
||||
lua.globals()
|
||||
.set("userdata", UserData("hello".to_string()))
|
||||
let ud = lua.create_userdata(UserData(123)).unwrap();
|
||||
let method = lua
|
||||
.load("function(ud, i) return ud:add(i) end")
|
||||
.eval::<LuaFunction>()
|
||||
.unwrap();
|
||||
let i = AtomicUsize::new(0);
|
||||
|
||||
c.bench_function("call async [userdata method] 10", |b| {
|
||||
c.bench_function("userdata [async call method] 10", |b| {
|
||||
let rt = Runtime::new().unwrap();
|
||||
b.to_async(rt).iter_batched(
|
||||
|| {
|
||||
collect_gc_twice(&lua);
|
||||
lua.load("function() for i = 1,10 do userdata:method() end end")
|
||||
.eval::<LuaFunction>()
|
||||
.unwrap()
|
||||
(
|
||||
method.clone(),
|
||||
ud.clone(),
|
||||
i.fetch_add(1, Ordering::Relaxed),
|
||||
)
|
||||
},
|
||||
|function| async move {
|
||||
function.call_async::<_, ()>(()).await.unwrap();
|
||||
|(method, ud, i)| async move {
|
||||
assert_eq!(
|
||||
method.call_async::<_, usize>((ud, i)).await.unwrap(),
|
||||
123 + i
|
||||
);
|
||||
},
|
||||
BatchSize::SmallInput,
|
||||
);
|
||||
@@ -298,23 +403,32 @@ fn call_async_userdata_method(c: &mut Criterion) {
|
||||
criterion_group! {
|
||||
name = benches;
|
||||
config = Criterion::default()
|
||||
.sample_size(300)
|
||||
.sample_size(500)
|
||||
.measurement_time(Duration::from_secs(10))
|
||||
.noise_threshold(0.02);
|
||||
targets =
|
||||
create_table,
|
||||
create_array,
|
||||
create_string_table,
|
||||
create_function,
|
||||
call_lua_function,
|
||||
call_sum_callback,
|
||||
call_async_sum_callback,
|
||||
call_concat_callback,
|
||||
create_registry_values,
|
||||
create_userdata,
|
||||
call_userdata_index,
|
||||
call_userdata_method,
|
||||
call_async_userdata_method,
|
||||
table_create_empty,
|
||||
table_create_array,
|
||||
table_create_hash,
|
||||
table_get_set,
|
||||
table_traversal_pairs,
|
||||
table_traversal_for_each,
|
||||
table_traversal_sequence,
|
||||
|
||||
function_create,
|
||||
function_call_sum,
|
||||
function_call_lua_sum,
|
||||
function_call_concat,
|
||||
function_call_lua_concat,
|
||||
function_async_call_sum,
|
||||
|
||||
registry_value_create,
|
||||
registry_value_get,
|
||||
|
||||
userdata_create,
|
||||
userdata_call_index,
|
||||
userdata_call_method,
|
||||
userdata_async_call_method,
|
||||
}
|
||||
|
||||
criterion_main!(benches);
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
use std::time::Duration;
|
||||
|
||||
use criterion::{criterion_group, criterion_main, BatchSize, Criterion};
|
||||
|
||||
use mlua::prelude::*;
|
||||
|
||||
fn collect_gc_twice(lua: &Lua) {
|
||||
lua.gc_collect().unwrap();
|
||||
lua.gc_collect().unwrap();
|
||||
}
|
||||
|
||||
fn encode_json(c: &mut Criterion) {
|
||||
let lua = Lua::new();
|
||||
|
||||
let encode = lua
|
||||
.create_function(|_, t: LuaValue| Ok(serde_json::to_string(&t).unwrap()))
|
||||
.unwrap();
|
||||
let table = lua
|
||||
.load(
|
||||
r#"{
|
||||
name = "Clark Kent",
|
||||
address = {
|
||||
city = "Smallville",
|
||||
state = "Kansas",
|
||||
country = "USA",
|
||||
},
|
||||
age = 22,
|
||||
parents = {"Jonathan Kent", "Martha Kent"},
|
||||
superman = true,
|
||||
interests = {"flying", "saving the world", "kryptonite"},
|
||||
}"#,
|
||||
)
|
||||
.eval::<LuaTable>()
|
||||
.unwrap();
|
||||
|
||||
c.bench_function("serialize json", |b| {
|
||||
b.iter_batched(
|
||||
|| collect_gc_twice(&lua),
|
||||
|_| {
|
||||
encode.call::<_, LuaString>(&table).unwrap();
|
||||
},
|
||||
BatchSize::SmallInput,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
fn decode_json(c: &mut Criterion) {
|
||||
let lua = Lua::new();
|
||||
|
||||
let decode = lua
|
||||
.create_function(|lua, s: String| {
|
||||
lua.to_value(&serde_json::from_str::<serde_json::Value>(&s).unwrap())
|
||||
})
|
||||
.unwrap();
|
||||
let json = r#"{
|
||||
"name": "Clark Kent",
|
||||
"address": {
|
||||
"city": "Smallville",
|
||||
"state": "Kansas",
|
||||
"country": "USA"
|
||||
},
|
||||
"age": 22,
|
||||
"parents": ["Jonathan Kent", "Martha Kent"],
|
||||
"superman": true,
|
||||
"interests": ["flying", "saving the world", "kryptonite"]
|
||||
}"#;
|
||||
|
||||
c.bench_function("deserialize json", |b| {
|
||||
b.iter_batched(
|
||||
|| collect_gc_twice(&lua),
|
||||
|_| {
|
||||
decode.call::<_, LuaTable>(json).unwrap();
|
||||
},
|
||||
BatchSize::SmallInput,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
criterion_group! {
|
||||
name = benches;
|
||||
config = Criterion::default()
|
||||
.sample_size(500)
|
||||
.measurement_time(Duration::from_secs(10))
|
||||
.noise_threshold(0.02);
|
||||
targets =
|
||||
encode_json,
|
||||
decode_json,
|
||||
}
|
||||
|
||||
criterion_main!(benches);
|
||||
+10
-13
@@ -3,7 +3,7 @@
|
||||
The v0.9 version of mlua is a major release that includes a number of API changes and improvements. This release is a stepping stone towards the v1.0.
|
||||
This document highlights the most important changes. For a full list of changes, see the [CHANGELOG].
|
||||
|
||||
[CHANGELOG]: https://github.com/khvzak/mlua/blob/master/CHANGELOG.md
|
||||
[CHANGELOG]: https://github.com/khvzak/mlua/blob/main/CHANGELOG.md
|
||||
|
||||
### New features
|
||||
|
||||
@@ -83,7 +83,7 @@ println!("{s}!");
|
||||
|
||||
One of the common questions was how to embed a Lua type into Rust struct to use it later. It was non-trivial to do because of the `'lua` lifetime attached to every Lua value.
|
||||
|
||||
In v0.9 mlua introduces "owned" types `OwnedTable`/`OwnedFunction`/`OwnedString`/`OwnedAnyUserData` that are `'static` (no lifetime attached).
|
||||
In v0.9 mlua introduces "owned" types `OwnedTable`/`OwnedFunction`/`OwnedString`/`OwnedAnyUserData`/ `OwnedThread`that are `'static` (no lifetime attached).
|
||||
|
||||
```rust
|
||||
let lua = Lua::new();
|
||||
@@ -141,11 +141,10 @@ unsafe {
|
||||
|
||||
#### Luau JIT support
|
||||
|
||||
mlua brings support for the new experimental [Luau] JIT backend under the `luau-jit` feature flag. This backend is still under development and not yet ready for production use.
|
||||
mlua brings support for the new [Luau] JIT backend under the `luau-jit` feature flag.
|
||||
|
||||
To enable it, just call `lua.enable_jit(true)` before loading Lua code. mlua will automatically trigger JIT compilation for new Lua chunks.
|
||||
|
||||
When calling this function with `false` argument, mlua will disable JIT compilation but any previously compiled chunks will remain JIT-compiled.
|
||||
It will automatically trigger JIT compilation for new Lua chunks. To disable it, just call `lua.enable_jit(false)` before loading Lua code
|
||||
(but any previously compiled chunks will remain JIT-compiled).
|
||||
|
||||
[Luau]: https://luau-lang.org
|
||||
|
||||
@@ -197,16 +196,14 @@ let read = lua.create_function(|lua, path: String| {
|
||||
|
||||
lua.load(chunk! {
|
||||
local ok, err = pcall($read, "/nonexistent")
|
||||
/// Prints:
|
||||
/// Failed to open /nonexistent
|
||||
/// No such file or directory (os error 2)
|
||||
/// stack traceback:
|
||||
/// ...
|
||||
print(err)
|
||||
})
|
||||
.exec()?;
|
||||
|
||||
Prints:
|
||||
```text
|
||||
Failed to open /nonexistent
|
||||
No such file or directory (os error 2)
|
||||
stack traceback:
|
||||
...
|
||||
```
|
||||
|
||||
[`anyhow`]: https://crates.io/crates/anyhow
|
||||
|
||||
@@ -1,30 +1,34 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use hyper::body::{Body as HyperBody, HttpBody as _};
|
||||
use hyper::Client as HyperClient;
|
||||
use http_body_util::BodyExt as _;
|
||||
use hyper::body::Incoming;
|
||||
use hyper_util::client::legacy::Client as HyperClient;
|
||||
use hyper_util::rt::TokioExecutor;
|
||||
|
||||
use mlua::{chunk, ExternalResult, Lua, Result, UserData, UserDataMethods};
|
||||
|
||||
struct BodyReader(HyperBody);
|
||||
struct BodyReader(Incoming);
|
||||
|
||||
impl UserData for BodyReader {
|
||||
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
|
||||
// Every call returns a next chunk
|
||||
methods.add_async_method_mut("read", |lua, reader, ()| async move {
|
||||
if let Some(bytes) = reader.0.data().await {
|
||||
let bytes = bytes.into_lua_err()?;
|
||||
return Some(lua.create_string(&bytes)).transpose();
|
||||
if let Some(bytes) = reader.0.frame().await {
|
||||
if let Some(bytes) = bytes.into_lua_err()?.data_ref() {
|
||||
return Some(lua.create_string(&bytes)).transpose();
|
||||
}
|
||||
}
|
||||
Ok(None)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
#[tokio::main(flavor = "current_thread")]
|
||||
async fn main() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let fetch_url = lua.create_async_function(|lua, uri: String| async move {
|
||||
let client = HyperClient::new();
|
||||
let client = HyperClient::builder(TokioExecutor::new()).build_http::<String>();
|
||||
let uri = uri.parse().into_lua_err()?;
|
||||
let resp = client.get(uri).await.into_lua_err()?;
|
||||
|
||||
@@ -55,11 +59,11 @@ async fn main() -> Result<()> {
|
||||
end
|
||||
end
|
||||
repeat
|
||||
local body = res.body:read()
|
||||
if body then
|
||||
print(body)
|
||||
local chunk = res.body:read()
|
||||
if chunk then
|
||||
print(chunk)
|
||||
end
|
||||
until not body
|
||||
until not chunk
|
||||
})
|
||||
.into_function()?;
|
||||
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
use mlua::{chunk, ExternalResult, Lua, LuaSerdeExt, Result};
|
||||
use mlua::{chunk, ExternalResult, Lua, LuaSerdeExt, Result, Value};
|
||||
|
||||
#[tokio::main]
|
||||
#[tokio::main(flavor = "current_thread")]
|
||||
async fn main() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let null = lua.null();
|
||||
|
||||
let fetch_json = lua.create_async_function(|lua, uri: String| async move {
|
||||
let resp = reqwest::get(&uri)
|
||||
.await
|
||||
@@ -15,19 +13,15 @@ async fn main() -> Result<()> {
|
||||
lua.to_value(&json)
|
||||
})?;
|
||||
|
||||
let dbg = lua.create_function(|_, value: Value| {
|
||||
println!("{value:#?}");
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
let f = lua
|
||||
.load(chunk! {
|
||||
function print_r(t, indent)
|
||||
local indent = indent or ""
|
||||
for k, v in pairs(t) do
|
||||
io.write(indent, tostring(k))
|
||||
if type(v) == "table" then io.write(":\n") print_r(v, indent.." ")
|
||||
else io.write(": ", v == $null and "null" or tostring(v), "\n") end
|
||||
end
|
||||
end
|
||||
|
||||
local res = $fetch_json(...)
|
||||
print_r(res)
|
||||
$dbg(res)
|
||||
})
|
||||
.into_function()?;
|
||||
|
||||
|
||||
@@ -1,43 +1,63 @@
|
||||
use std::convert::Infallible;
|
||||
use std::future::Future;
|
||||
use std::net::SocketAddr;
|
||||
use std::pin::Pin;
|
||||
use std::rc::Rc;
|
||||
use std::task::{Context, Poll};
|
||||
|
||||
use hyper::server::conn::AddrStream;
|
||||
use hyper::service::Service;
|
||||
use hyper::{Body, Request, Response, Server};
|
||||
use futures::future::LocalBoxFuture;
|
||||
use http_body_util::{combinators::BoxBody, BodyExt as _, Empty, Full};
|
||||
use hyper::body::{Bytes, Incoming};
|
||||
use hyper::{Request, Response};
|
||||
use hyper_util::rt::TokioIo;
|
||||
use hyper_util::server::conn::auto::Builder as ServerConnBuilder;
|
||||
use tokio::net::TcpListener;
|
||||
use tokio::task::LocalSet;
|
||||
|
||||
use mlua::{
|
||||
chunk, Error as LuaError, Function, Lua, String as LuaString, Table, UserData, UserDataMethods,
|
||||
chunk, Error as LuaError, Function, Lua, RegistryKey, String as LuaString, Table, UserData,
|
||||
UserDataMethods,
|
||||
};
|
||||
|
||||
struct LuaRequest(SocketAddr, Request<Body>);
|
||||
/// Wrapper around incoming request that implements UserData
|
||||
struct LuaRequest(SocketAddr, Request<Incoming>);
|
||||
|
||||
impl UserData for LuaRequest {
|
||||
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
|
||||
methods.add_method("remote_addr", |_lua, req, ()| Ok((req.0).to_string()));
|
||||
methods.add_method("method", |_lua, req, ()| Ok((req.1).method().to_string()));
|
||||
methods.add_method("remote_addr", |_, req, ()| Ok((req.0).to_string()));
|
||||
methods.add_method("method", |_, req, ()| Ok((req.1).method().to_string()));
|
||||
methods.add_method("path", |_, req, ()| Ok(req.1.uri().path().to_string()));
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Svc(Rc<Lua>, SocketAddr);
|
||||
/// Service that handles incoming requests
|
||||
#[derive(Clone)]
|
||||
pub struct Svc {
|
||||
lua: Rc<Lua>,
|
||||
handler: Rc<RegistryKey>,
|
||||
peer_addr: SocketAddr,
|
||||
}
|
||||
|
||||
impl Service<Request<Body>> for Svc {
|
||||
type Response = Response<Body>;
|
||||
type Error = LuaError;
|
||||
type Future = Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>>>>;
|
||||
|
||||
fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||
Poll::Ready(Ok(()))
|
||||
impl Svc {
|
||||
pub fn new(lua: Rc<Lua>, handler: Rc<RegistryKey>, peer_addr: SocketAddr) -> Self {
|
||||
Self {
|
||||
lua,
|
||||
handler,
|
||||
peer_addr,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn call(&mut self, req: Request<Body>) -> Self::Future {
|
||||
impl hyper::service::Service<Request<Incoming>> for Svc {
|
||||
type Response = Response<BoxBody<Bytes, Infallible>>;
|
||||
type Error = LuaError;
|
||||
type Future = LocalBoxFuture<'static, Result<Self::Response, Self::Error>>;
|
||||
|
||||
fn call(&self, req: Request<Incoming>) -> Self::Future {
|
||||
// If handler returns an error then generate 5xx response
|
||||
let lua = self.0.clone();
|
||||
let lua_req = LuaRequest(self.1, req);
|
||||
let lua = self.lua.clone();
|
||||
let handler_key = self.handler.clone();
|
||||
let lua_req = LuaRequest(self.peer_addr, req);
|
||||
Box::pin(async move {
|
||||
let handler: Function = lua.named_registry_value("http_handler")?;
|
||||
let handler: Function = lua.registry_value(&handler_key)?;
|
||||
match handler.call_async::<_, Table>(lua_req).await {
|
||||
Ok(lua_resp) => {
|
||||
let status = lua_resp.get::<_, Option<u16>>("status")?.unwrap_or(200);
|
||||
@@ -51,10 +71,11 @@ impl Service<Request<Body>> for Svc {
|
||||
}
|
||||
}
|
||||
|
||||
// Set body
|
||||
let body = lua_resp
|
||||
.get::<_, Option<LuaString>>("body")?
|
||||
.map(|b| Body::from(b.as_bytes().to_vec()))
|
||||
.unwrap_or_else(Body::empty);
|
||||
.map(|b| Full::new(Bytes::copy_from_slice(b.as_bytes())).boxed())
|
||||
.unwrap_or_else(|| Empty::<Bytes>::new().boxed());
|
||||
|
||||
Ok(resp.body(body).unwrap())
|
||||
}
|
||||
@@ -62,7 +83,7 @@ impl Service<Request<Body>> for Svc {
|
||||
eprintln!("{}", err);
|
||||
Ok(Response::builder()
|
||||
.status(500)
|
||||
.body(Body::from("Internal Server Error"))
|
||||
.body(Full::new(Bytes::from("Internal Server Error")).boxed())
|
||||
.unwrap())
|
||||
}
|
||||
}
|
||||
@@ -75,13 +96,14 @@ async fn main() {
|
||||
let lua = Rc::new(Lua::new());
|
||||
|
||||
// Create Lua handler function
|
||||
let handler: Function = lua
|
||||
let handler: RegistryKey = lua
|
||||
.load(chunk! {
|
||||
function(req)
|
||||
return {
|
||||
status = 200,
|
||||
headers = {
|
||||
["X-Req-Method"] = req:method(),
|
||||
["X-Req-Path"] = req:path(),
|
||||
["X-Remote-Addr"] = req:remote_addr(),
|
||||
},
|
||||
body = "Hello from Lua!\n"
|
||||
@@ -89,37 +111,35 @@ async fn main() {
|
||||
end
|
||||
})
|
||||
.eval()
|
||||
.expect("cannot create Lua handler");
|
||||
.expect("Failed to create Lua handler");
|
||||
let handler = Rc::new(handler);
|
||||
|
||||
// Store it in the Registry
|
||||
lua.set_named_registry_value("http_handler", handler)
|
||||
.expect("cannot store Lua handler");
|
||||
let listen_addr = "127.0.0.1:3000";
|
||||
let listener = TcpListener::bind(listen_addr).await.unwrap();
|
||||
println!("Listening on http://{listen_addr}");
|
||||
|
||||
let addr = ([127, 0, 0, 1], 3000).into();
|
||||
let server = Server::bind(&addr).executor(LocalExec).serve(MakeSvc(lua));
|
||||
let local = LocalSet::new();
|
||||
loop {
|
||||
let (stream, peer_addr) = match listener.accept().await {
|
||||
Ok(x) => x,
|
||||
Err(err) => {
|
||||
eprintln!("Failed to accept connection: {err}");
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
||||
println!("Listening on http://{}", addr);
|
||||
|
||||
// Create `LocalSet` to spawn !Send futures
|
||||
let local = tokio::task::LocalSet::new();
|
||||
local.run_until(server).await.expect("cannot run server")
|
||||
}
|
||||
|
||||
struct MakeSvc(Rc<Lua>);
|
||||
|
||||
impl Service<&AddrStream> for MakeSvc {
|
||||
type Response = Svc;
|
||||
type Error = hyper::Error;
|
||||
type Future = Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>>>>;
|
||||
|
||||
fn poll_ready(&mut self, _: &mut Context) -> Poll<Result<(), Self::Error>> {
|
||||
Poll::Ready(Ok(()))
|
||||
}
|
||||
|
||||
fn call(&mut self, stream: &AddrStream) -> Self::Future {
|
||||
let lua = self.0.clone();
|
||||
let remote_addr = stream.remote_addr();
|
||||
Box::pin(async move { Ok(Svc(lua, remote_addr)) })
|
||||
let svc = Svc::new(lua.clone(), handler.clone(), peer_addr);
|
||||
local
|
||||
.run_until(async move {
|
||||
let result = ServerConnBuilder::new(LocalExec)
|
||||
.http1()
|
||||
.serve_connection(TokioIo::new(stream), svc)
|
||||
.await;
|
||||
if let Err(err) = result {
|
||||
eprintln!("Error serving connection: {err:?}");
|
||||
}
|
||||
})
|
||||
.await;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -128,7 +148,7 @@ struct LocalExec;
|
||||
|
||||
impl<F> hyper::rt::Executor<F> for LocalExec
|
||||
where
|
||||
F: std::future::Future + 'static, // not requiring `Send`
|
||||
F: Future + 'static, // not requiring `Send`
|
||||
{
|
||||
fn execute(&self, fut: F) {
|
||||
tokio::task::spawn_local(fut);
|
||||
|
||||
@@ -24,7 +24,7 @@ fn main() -> Result<()> {
|
||||
|
||||
// You can load and evaluate Lua code. The returned type of `Lua::load` is a builder
|
||||
// that allows you to change settings before running Lua code. Here, we are using it to set
|
||||
// the name of the laoded chunk to "example code", which will be used when Lua error
|
||||
// the name of the loaded chunk to "example code", which will be used when Lua error
|
||||
// messages are printed.
|
||||
|
||||
lua.load(
|
||||
@@ -89,7 +89,7 @@ fn main() -> Result<()> {
|
||||
let print: Function = globals.get("print")?;
|
||||
print.call::<_, ()>("hello from rust")?;
|
||||
|
||||
// This API generally handles variadics using tuples. This is one way to call a function with
|
||||
// This API generally handles variadic using tuples. This is one way to call a function with
|
||||
// multiple parameters:
|
||||
|
||||
print.call::<_, ()>(("hello", "again", "from", "rust"))?;
|
||||
@@ -100,7 +100,7 @@ fn main() -> Result<()> {
|
||||
["hello", "yet", "again", "from", "rust"].iter().cloned(),
|
||||
))?;
|
||||
|
||||
// You can bind rust functions to Lua as well. Callbacks receive the Lua state inself as their
|
||||
// You can bind rust functions to Lua as well. Callbacks receive the Lua state itself as their
|
||||
// first parameter, and the arguments given to the function as the second parameter. The type
|
||||
// of the arguments can be anything that is convertible from the parameters given by Lua, in
|
||||
// this case, the function expects two string sequences.
|
||||
|
||||
+8
-4
@@ -1,7 +1,8 @@
|
||||
[package]
|
||||
name = "mlua-sys"
|
||||
version = "0.3.0"
|
||||
version = "0.6.2"
|
||||
authors = ["Aleksandr Orlenko <zxteam@pm.me>"]
|
||||
rust-version = "1.71"
|
||||
edition = "2021"
|
||||
repository = "https://github.com/khvzak/mlua"
|
||||
documentation = "https://docs.rs/mlua-sys"
|
||||
@@ -37,6 +38,9 @@ module = []
|
||||
cc = "1.0"
|
||||
cfg-if = "1.0"
|
||||
pkg-config = "0.3.17"
|
||||
lua-src = { version = ">= 546.0.0, < 546.1.0", optional = true }
|
||||
luajit-src = { version = ">= 210.4.6, < 210.6.0", optional = true }
|
||||
luau0-src = { version = "0.5.11", optional = true }
|
||||
lua-src = { version = ">= 547.0.0, < 547.1.0", optional = true }
|
||||
luajit-src = { version = ">= 210.5.0, < 210.6.0", optional = true }
|
||||
luau0-src = { version = "0.10.0", optional = true }
|
||||
|
||||
[lints.rust]
|
||||
unexpected_cfgs = { level = "allow", check-cfg = ['cfg(raw_dylib)'] }
|
||||
|
||||
@@ -4,6 +4,12 @@ use std::env;
|
||||
use std::ops::Bound;
|
||||
|
||||
pub fn probe_lua() {
|
||||
let target_arch = env::var("CARGO_CFG_TARGET_ARCH").unwrap();
|
||||
|
||||
if target_arch == "wasm32" && cfg!(not(feature = "vendored")) {
|
||||
panic!("Please enable `vendored` feature to build for wasm32");
|
||||
}
|
||||
|
||||
let lib_dir = env::var("LUA_LIB").unwrap_or_default();
|
||||
let lua_lib = env::var("LUA_LIB_NAME").unwrap_or_default();
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@ pub fn probe_lua() {
|
||||
#[cfg(feature = "luau")]
|
||||
let artifacts = luau0_src::Build::new()
|
||||
.enable_codegen(cfg!(feature = "luau-codegen"))
|
||||
.set_max_cstack_size(1000000)
|
||||
.set_vector_size(if cfg!(feature = "luau-vector4") { 4 } else { 3 })
|
||||
.build();
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
use std::env;
|
||||
|
||||
cfg_if::cfg_if! {
|
||||
if #[cfg(any(feature = "luau", feature = "vendored"))] {
|
||||
#[path = "find_vendored.rs"]
|
||||
@@ -9,16 +11,16 @@ cfg_if::cfg_if! {
|
||||
}
|
||||
|
||||
fn main() {
|
||||
#[cfg(all(feature = "luau", feature = "module"))]
|
||||
compile_error!("Luau does not support `module` mode");
|
||||
#[cfg(all(feature = "luau", feature = "module", windows))]
|
||||
compile_error!("Luau does not support `module` mode on Windows");
|
||||
|
||||
#[cfg(all(feature = "module", feature = "vendored"))]
|
||||
compile_error!("`vendored` and `module` features are mutually exclusive");
|
||||
|
||||
println!("cargo:rerun-if-changed=build");
|
||||
|
||||
#[cfg(windows)]
|
||||
if cfg!(feature = "module") {
|
||||
let target_os = env::var("CARGO_CFG_TARGET_OS").unwrap();
|
||||
if target_os == "windows" && cfg!(feature = "module") {
|
||||
if !std::env::var("LUA_LIB_NAME").unwrap_or_default().is_empty() {
|
||||
// Don't use raw-dylib linking
|
||||
find::probe_lua();
|
||||
|
||||
+13
-5
@@ -39,20 +39,25 @@ pub const LUA_MAX_UPVALUES: c_int = 200;
|
||||
#[doc(hidden)]
|
||||
pub const LUA_TRACEBACK_STACK: c_int = 11;
|
||||
|
||||
// Copied from https://github.com/rust-lang/rust/blob/master/library/std/src/sys/pal/common/alloc.rs
|
||||
// The minimum alignment guaranteed by the architecture. This value is used to
|
||||
// add fast paths for low alignment values.
|
||||
// Copied from https://github.com/rust-lang/rust/blob/master/library/std/src/sys/common/alloc.rs
|
||||
#[cfg(any(
|
||||
target_arch = "x86",
|
||||
target_arch = "arm",
|
||||
target_arch = "m68k",
|
||||
target_arch = "csky",
|
||||
target_arch = "mips",
|
||||
target_arch = "mips32r6",
|
||||
target_arch = "powerpc",
|
||||
target_arch = "powerpc64",
|
||||
target_arch = "sparc",
|
||||
target_arch = "asmjs",
|
||||
target_arch = "wasm32",
|
||||
target_arch = "hexagon",
|
||||
all(target_arch = "riscv32", not(target_os = "espidf")),
|
||||
all(
|
||||
target_arch = "riscv32",
|
||||
not(any(target_os = "espidf", target_os = "zkvm"))
|
||||
),
|
||||
all(target_arch = "xtensa", not(target_os = "espidf")),
|
||||
))]
|
||||
#[doc(hidden)]
|
||||
@@ -60,7 +65,10 @@ pub const SYS_MIN_ALIGN: usize = 8;
|
||||
#[cfg(any(
|
||||
target_arch = "x86_64",
|
||||
target_arch = "aarch64",
|
||||
target_arch = "arm64ec",
|
||||
target_arch = "loongarch64",
|
||||
target_arch = "mips64",
|
||||
target_arch = "mips64r6",
|
||||
target_arch = "s390x",
|
||||
target_arch = "sparc64",
|
||||
target_arch = "riscv64",
|
||||
@@ -68,9 +76,9 @@ pub const SYS_MIN_ALIGN: usize = 8;
|
||||
))]
|
||||
#[doc(hidden)]
|
||||
pub const SYS_MIN_ALIGN: usize = 16;
|
||||
// The allocator on the esp-idf platform guarentees 4 byte alignment.
|
||||
// The allocator on the esp-idf and zkvm platforms guarantee 4 byte alignment.
|
||||
#[cfg(any(
|
||||
all(target_arch = "riscv32", target_os = "espidf"),
|
||||
all(target_arch = "riscv32", any(target_os = "espidf", target_os = "zkvm")),
|
||||
all(target_arch = "xtensa", target_os = "espidf"),
|
||||
))]
|
||||
#[doc(hidden)]
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
//!
|
||||
//! Based on github.com/keplerproject/lua-compat-5.3
|
||||
|
||||
use std::convert::TryInto;
|
||||
use std::mem;
|
||||
use std::os::raw::{c_char, c_int, c_void};
|
||||
use std::ptr;
|
||||
|
||||
@@ -43,7 +43,7 @@ extern "C-unwind" {
|
||||
pub fn luaL_checkudata(L: *mut lua_State, ud: c_int, tname: *const c_char) -> *mut c_void;
|
||||
|
||||
pub fn luaL_where(L: *mut lua_State, lvl: c_int);
|
||||
pub fn luaL_error(L: *mut lua_State, fmt: *const c_char, ...) -> !;
|
||||
pub fn luaL_error(L: *mut lua_State, fmt: *const c_char, ...) -> c_int;
|
||||
|
||||
pub fn luaL_checkoption(
|
||||
L: *mut lua_State,
|
||||
|
||||
@@ -228,13 +228,23 @@ extern "C-unwind" {
|
||||
//
|
||||
#[cfg_attr(all(windows, raw_dylib), link(name = "lua51", kind = "raw-dylib"))]
|
||||
extern "C-unwind" {
|
||||
pub fn lua_error(L: *mut lua_State) -> !;
|
||||
#[link_name = "lua_error"]
|
||||
fn lua_error_(L: *mut lua_State) -> c_int;
|
||||
pub fn lua_next(L: *mut lua_State, idx: c_int) -> c_int;
|
||||
pub fn lua_concat(L: *mut lua_State, n: c_int);
|
||||
pub fn lua_getallocf(L: *mut lua_State, ud: *mut *mut c_void) -> lua_Alloc;
|
||||
pub fn lua_setallocf(L: *mut lua_State, f: lua_Alloc, ud: *mut c_void);
|
||||
}
|
||||
|
||||
// lua_error does not return but is declared to return int, and Rust translates
|
||||
// ! to void which can cause link-time errors if the platform linker is aware
|
||||
// of return types and requires they match (for example: wasm does this).
|
||||
#[inline(always)]
|
||||
pub unsafe fn lua_error(L: *mut lua_State) -> ! {
|
||||
lua_error_(L);
|
||||
unreachable!();
|
||||
}
|
||||
|
||||
//
|
||||
// Some useful macros (implemented as Rust functions)
|
||||
//
|
||||
@@ -318,6 +328,14 @@ pub unsafe fn lua_getglobal_(L: *mut lua_State, var: *const c_char) {
|
||||
lua_getfield_(L, LUA_GLOBALSINDEX, var)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn lua_tolightuserdata(L: *mut lua_State, idx: c_int) -> *mut c_void {
|
||||
if lua_islightuserdata(L, idx) != 0 {
|
||||
return lua_touserdata(L, idx);
|
||||
}
|
||||
ptr::null_mut()
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn lua_tostring(L: *mut lua_State, i: c_int) -> *const c_char {
|
||||
lua_tolstring(L, i, ptr::null_mut())
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
//!
|
||||
//! Based on github.com/keplerproject/lua-compat-5.3
|
||||
|
||||
use std::convert::TryInto;
|
||||
use std::os::raw::{c_char, c_int, c_void};
|
||||
use std::ptr;
|
||||
|
||||
|
||||
@@ -49,7 +49,7 @@ extern "C-unwind" {
|
||||
pub fn luaL_checkudata(L: *mut lua_State, ud: c_int, tname: *const c_char) -> *mut c_void;
|
||||
|
||||
pub fn luaL_where(L: *mut lua_State, lvl: c_int);
|
||||
pub fn luaL_error(L: *mut lua_State, fmt: *const c_char, ...) -> !;
|
||||
pub fn luaL_error(L: *mut lua_State, fmt: *const c_char, ...) -> c_int;
|
||||
|
||||
pub fn luaL_checkoption(
|
||||
L: *mut lua_State,
|
||||
|
||||
@@ -308,7 +308,8 @@ extern "C-unwind" {
|
||||
//
|
||||
// Miscellaneous functions
|
||||
//
|
||||
pub fn lua_error(L: *mut lua_State) -> !;
|
||||
#[link_name = "lua_error"]
|
||||
fn lua_error_(L: *mut lua_State) -> c_int;
|
||||
pub fn lua_next(L: *mut lua_State, idx: c_int) -> c_int;
|
||||
pub fn lua_concat(L: *mut lua_State, n: c_int);
|
||||
pub fn lua_len(L: *mut lua_State, idx: c_int);
|
||||
@@ -316,6 +317,15 @@ extern "C-unwind" {
|
||||
pub fn lua_setallocf(L: *mut lua_State, f: lua_Alloc, ud: *mut c_void);
|
||||
}
|
||||
|
||||
// lua_error does not return but is declared to return int, and Rust translates
|
||||
// ! to void which can cause link-time errors if the platform linker is aware
|
||||
// of return types and requires they match (for example: wasm does this).
|
||||
#[inline(always)]
|
||||
pub unsafe fn lua_error(L: *mut lua_State) -> ! {
|
||||
lua_error_(L);
|
||||
unreachable!();
|
||||
}
|
||||
|
||||
//
|
||||
// Some useful macros (implemented as Rust functions)
|
||||
//
|
||||
@@ -407,6 +417,14 @@ pub unsafe fn lua_pushglobaltable(L: *mut lua_State) {
|
||||
lua_rawgeti_(L, LUA_REGISTRYINDEX, LUA_RIDX_GLOBALS as _)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn lua_tolightuserdata(L: *mut lua_State, idx: c_int) -> *mut c_void {
|
||||
if lua_islightuserdata(L, idx) != 0 {
|
||||
return lua_touserdata(L, idx);
|
||||
}
|
||||
ptr::null_mut()
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn lua_tostring(L: *mut lua_State, i: c_int) -> *const c_char {
|
||||
lua_tolstring(L, i, ptr::null_mut())
|
||||
@@ -453,7 +471,12 @@ extern "C-unwind" {
|
||||
pub fn lua_upvalueid(L: *mut lua_State, fidx: c_int, n: c_int) -> *mut c_void;
|
||||
pub fn lua_upvaluejoin(L: *mut lua_State, fidx1: c_int, n1: c_int, fidx2: c_int, n2: c_int);
|
||||
|
||||
pub fn lua_sethook(L: *mut lua_State, func: Option<lua_Hook>, mask: c_int, count: c_int);
|
||||
pub fn lua_sethook(
|
||||
L: *mut lua_State,
|
||||
func: Option<lua_Hook>,
|
||||
mask: c_int,
|
||||
count: c_int,
|
||||
) -> c_int;
|
||||
pub fn lua_gethook(L: *mut lua_State) -> Option<lua_Hook>;
|
||||
pub fn lua_gethookmask(L: *mut lua_State) -> c_int;
|
||||
pub fn lua_gethookcount(L: *mut lua_State) -> c_int;
|
||||
|
||||
@@ -20,7 +20,7 @@ pub struct luaL_Reg {
|
||||
pub func: lua_CFunction,
|
||||
}
|
||||
|
||||
#[cfg_attr(all(windows, raw_dylib), link(name = "lua54", kind = "raw-dylib"))]
|
||||
#[cfg_attr(all(windows, raw_dylib), link(name = "lua53", kind = "raw-dylib"))]
|
||||
extern "C-unwind" {
|
||||
pub fn luaL_checkversion_(L: *mut lua_State, ver: lua_Number, sz: usize);
|
||||
|
||||
@@ -51,7 +51,7 @@ extern "C-unwind" {
|
||||
pub fn luaL_checkudata(L: *mut lua_State, ud: c_int, tname: *const c_char) -> *mut c_void;
|
||||
|
||||
pub fn luaL_where(L: *mut lua_State, lvl: c_int);
|
||||
pub fn luaL_error(L: *mut lua_State, fmt: *const c_char, ...) -> !;
|
||||
pub fn luaL_error(L: *mut lua_State, fmt: *const c_char, ...) -> c_int;
|
||||
|
||||
pub fn luaL_checkoption(
|
||||
L: *mut lua_State,
|
||||
|
||||
@@ -314,7 +314,8 @@ extern "C-unwind" {
|
||||
//
|
||||
// Miscellaneous functions
|
||||
//
|
||||
pub fn lua_error(L: *mut lua_State) -> !;
|
||||
#[link_name = "lua_error"]
|
||||
fn lua_error_(L: *mut lua_State) -> c_int;
|
||||
pub fn lua_next(L: *mut lua_State, idx: c_int) -> c_int;
|
||||
pub fn lua_concat(L: *mut lua_State, n: c_int);
|
||||
pub fn lua_len(L: *mut lua_State, idx: c_int);
|
||||
@@ -323,6 +324,15 @@ extern "C-unwind" {
|
||||
pub fn lua_setallocf(L: *mut lua_State, f: lua_Alloc, ud: *mut c_void);
|
||||
}
|
||||
|
||||
// lua_error does not return but is declared to return int, and Rust translates
|
||||
// ! to void which can cause link-time errors if the platform linker is aware
|
||||
// of return types and requires they match (for example: wasm does this).
|
||||
#[inline(always)]
|
||||
pub unsafe fn lua_error(L: *mut lua_State) -> ! {
|
||||
lua_error_(L);
|
||||
unreachable!();
|
||||
}
|
||||
|
||||
//
|
||||
// Some useful macros (implemented as Rust functions)
|
||||
//
|
||||
@@ -414,6 +424,14 @@ pub unsafe fn lua_pushglobaltable(L: *mut lua_State) -> c_int {
|
||||
lua_rawgeti(L, LUA_REGISTRYINDEX, LUA_RIDX_GLOBALS)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn lua_tolightuserdata(L: *mut lua_State, idx: c_int) -> *mut c_void {
|
||||
if lua_islightuserdata(L, idx) != 0 {
|
||||
return lua_touserdata(L, idx);
|
||||
}
|
||||
ptr::null_mut()
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn lua_tostring(L: *mut lua_State, i: c_int) -> *const c_char {
|
||||
lua_tolstring(L, i, ptr::null_mut())
|
||||
|
||||
@@ -50,7 +50,7 @@ extern "C-unwind" {
|
||||
pub fn luaL_checkudata(L: *mut lua_State, ud: c_int, tname: *const c_char) -> *mut c_void;
|
||||
|
||||
pub fn luaL_where(L: *mut lua_State, lvl: c_int);
|
||||
pub fn luaL_error(L: *mut lua_State, fmt: *const c_char, ...) -> !;
|
||||
pub fn luaL_error(L: *mut lua_State, fmt: *const c_char, ...) -> c_int;
|
||||
|
||||
pub fn luaL_checkoption(
|
||||
L: *mut lua_State,
|
||||
|
||||
@@ -149,13 +149,21 @@ extern "C-unwind" {
|
||||
pub fn lua_tointegerx(L: *mut lua_State, idx: c_int, isnum: *mut c_int) -> lua_Integer;
|
||||
pub fn lua_toboolean(L: *mut lua_State, idx: c_int) -> c_int;
|
||||
pub fn lua_tolstring(L: *mut lua_State, idx: c_int, len: *mut usize) -> *const c_char;
|
||||
pub fn lua_rawlen(L: *mut lua_State, idx: c_int) -> usize;
|
||||
#[link_name = "lua_rawlen"]
|
||||
fn lua_rawlen_(L: *mut lua_State, idx: c_int) -> lua_Unsigned;
|
||||
pub fn lua_tocfunction(L: *mut lua_State, idx: c_int) -> Option<lua_CFunction>;
|
||||
pub fn lua_touserdata(L: *mut lua_State, idx: c_int) -> *mut c_void;
|
||||
pub fn lua_tothread(L: *mut lua_State, idx: c_int) -> *mut lua_State;
|
||||
pub fn lua_topointer(L: *mut lua_State, idx: c_int) -> *const c_void;
|
||||
}
|
||||
|
||||
// lua_rawlen's return type changed from size_t to lua_Unsigned int in Lua 5.4.
|
||||
// This adapts the crate API to the new Lua ABI.
|
||||
#[inline(always)]
|
||||
pub unsafe fn lua_rawlen(L: *mut lua_State, idx: c_int) -> usize {
|
||||
lua_rawlen_(L, idx) as usize
|
||||
}
|
||||
|
||||
//
|
||||
// Comparison and arithmetic functions
|
||||
//
|
||||
@@ -336,7 +344,8 @@ extern "C-unwind" {
|
||||
//
|
||||
// Miscellaneous functions
|
||||
//
|
||||
pub fn lua_error(L: *mut lua_State) -> !;
|
||||
#[link_name = "lua_error"]
|
||||
fn lua_error_(L: *mut lua_State) -> c_int;
|
||||
pub fn lua_next(L: *mut lua_State, idx: c_int) -> c_int;
|
||||
pub fn lua_concat(L: *mut lua_State, n: c_int);
|
||||
pub fn lua_len(L: *mut lua_State, idx: c_int);
|
||||
@@ -348,6 +357,15 @@ extern "C-unwind" {
|
||||
pub fn lua_closeslot(L: *mut lua_State, idx: c_int);
|
||||
}
|
||||
|
||||
// lua_error does not return but is declared to return int, and Rust translates
|
||||
// ! to void which can cause link-time errors if the platform linker is aware
|
||||
// of return types and requires they match (for example: wasm does this).
|
||||
#[inline(always)]
|
||||
pub unsafe fn lua_error(L: *mut lua_State) -> ! {
|
||||
lua_error_(L);
|
||||
unreachable!();
|
||||
}
|
||||
|
||||
//
|
||||
// Some useful macros (implemented as Rust functions)
|
||||
//
|
||||
@@ -439,6 +457,14 @@ pub unsafe fn lua_pushglobaltable(L: *mut lua_State) -> c_int {
|
||||
lua_rawgeti(L, LUA_REGISTRYINDEX, LUA_RIDX_GLOBALS)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn lua_tolightuserdata(L: *mut lua_State, idx: c_int) -> *mut c_void {
|
||||
if lua_islightuserdata(L, idx) != 0 {
|
||||
return lua_touserdata(L, idx);
|
||||
}
|
||||
ptr::null_mut()
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn lua_tostring(L: *mut lua_State, i: c_int) -> *const c_char {
|
||||
lua_tolstring(L, i, ptr::null_mut())
|
||||
|
||||
@@ -52,6 +52,8 @@ extern "C-unwind" {
|
||||
pub fn luaL_newmetatable_(L: *mut lua_State, tname: *const c_char) -> c_int;
|
||||
pub fn luaL_checkudata(L: *mut lua_State, ud: c_int, tname: *const c_char) -> *mut c_void;
|
||||
|
||||
pub fn luaL_checkbuffer(L: *mut lua_State, narg: c_int, len: *mut usize) -> *mut c_void;
|
||||
|
||||
pub fn luaL_where(L: *mut lua_State, lvl: c_int);
|
||||
|
||||
#[link_name = "luaL_errorL"]
|
||||
@@ -142,9 +144,12 @@ pub unsafe fn luaL_sandbox(L: *mut lua_State, enabled: c_int) {
|
||||
|
||||
// set all builtin metatables to read-only
|
||||
lua_pushliteral(L, "");
|
||||
lua_getmetatable(L, -1);
|
||||
lua_setreadonly(L, -1, enabled);
|
||||
lua_pop(L, 2);
|
||||
if lua_getmetatable(L, -1) != 0 {
|
||||
lua_setreadonly(L, -1, enabled);
|
||||
lua_pop(L, 2);
|
||||
} else {
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
|
||||
// set globals to readonly and activate safeenv since the env is immutable
|
||||
lua_setreadonly(L, LUA_GLOBALSINDEX, enabled);
|
||||
@@ -152,5 +157,48 @@ pub unsafe fn luaL_sandbox(L: *mut lua_State, enabled: c_int) {
|
||||
}
|
||||
|
||||
//
|
||||
// TODO: Generic Buffer Manipulation
|
||||
// Generic Buffer Manipulation
|
||||
//
|
||||
|
||||
/// Buffer size used for on-stack string operations. This limit depends on native stack size.
|
||||
pub const LUA_BUFFERSIZE: usize = 512;
|
||||
|
||||
#[repr(C)]
|
||||
pub struct luaL_Strbuf {
|
||||
p: *mut c_char, // current position in buffer
|
||||
end: *mut c_char, // end of the current buffer
|
||||
L: *mut lua_State,
|
||||
storage: *mut c_void, // TString
|
||||
buffer: [c_char; LUA_BUFFERSIZE],
|
||||
}
|
||||
|
||||
// For compatibility
|
||||
pub type luaL_Buffer = luaL_Strbuf;
|
||||
|
||||
extern "C-unwind" {
|
||||
pub fn luaL_buffinit(L: *mut lua_State, B: *mut luaL_Strbuf);
|
||||
pub fn luaL_buffinitsize(L: *mut lua_State, B: *mut luaL_Strbuf, size: usize) -> *mut c_char;
|
||||
pub fn luaL_prepbuffsize(B: *mut luaL_Strbuf, size: usize) -> *mut c_char;
|
||||
pub fn luaL_addlstring(B: *mut luaL_Strbuf, s: *const c_char, l: usize);
|
||||
pub fn luaL_addvalue(B: *mut luaL_Strbuf);
|
||||
pub fn luaL_addvalueany(B: *mut luaL_Strbuf, idx: c_int);
|
||||
pub fn luaL_pushresult(B: *mut luaL_Strbuf);
|
||||
pub fn luaL_pushresultsize(B: *mut luaL_Strbuf, size: usize);
|
||||
}
|
||||
|
||||
pub unsafe fn luaL_addchar(B: *mut luaL_Strbuf, c: c_char) {
|
||||
if (*B).p >= (*B).end {
|
||||
luaL_prepbuffsize(B, 1);
|
||||
}
|
||||
*(*B).p = c;
|
||||
(*B).p = (*B).p.add(1);
|
||||
}
|
||||
|
||||
pub unsafe fn luaL_addstring(B: *mut luaL_Strbuf, s: *const c_char) {
|
||||
// Calculate length of s
|
||||
let mut len = 0;
|
||||
while *s.add(len) != 0 {
|
||||
len += 1;
|
||||
}
|
||||
luaL_addlstring(B, s, len);
|
||||
}
|
||||
|
||||
@@ -2,13 +2,19 @@
|
||||
|
||||
use std::marker::{PhantomData, PhantomPinned};
|
||||
use std::os::raw::{c_char, c_double, c_float, c_int, c_uint, c_void};
|
||||
use std::ptr;
|
||||
use std::{mem, ptr};
|
||||
|
||||
// Option for multiple returns in 'lua_pcall' and 'lua_call'
|
||||
pub const LUA_MULTRET: c_int = -1;
|
||||
|
||||
// Max number of Lua stack slots
|
||||
const LUAI_MAXCSTACK: c_int = 100000;
|
||||
const LUAI_MAXCSTACK: c_int = 1000000;
|
||||
|
||||
// Number of valid Lua userdata tags
|
||||
const LUA_UTAG_LIMIT: c_int = 128;
|
||||
|
||||
// Number of valid Lua lightuserdata tags
|
||||
const LUA_LUTAG_LIMIT: c_int = 128;
|
||||
|
||||
//
|
||||
// Pseudo-indices
|
||||
@@ -55,6 +61,7 @@ pub const LUA_TTABLE: c_int = 6;
|
||||
pub const LUA_TFUNCTION: c_int = 7;
|
||||
pub const LUA_TUSERDATA: c_int = 8;
|
||||
pub const LUA_TTHREAD: c_int = 9;
|
||||
pub const LUA_TBUFFER: c_int = 10;
|
||||
|
||||
/// Guaranteed number of Lua stack slots available to a C function.
|
||||
pub const LUA_MINSTACK: c_int = 20;
|
||||
@@ -143,10 +150,13 @@ extern "C-unwind" {
|
||||
pub fn lua_objlen(L: *mut lua_State, idx: c_int) -> usize;
|
||||
pub fn lua_tocfunction(L: *mut lua_State, idx: c_int) -> Option<lua_CFunction>;
|
||||
pub fn lua_tolightuserdata(L: *mut lua_State, idx: c_int) -> *mut c_void;
|
||||
pub fn lua_tolightuserdatatagged(L: *mut lua_State, idx: c_int, tag: c_int) -> *mut c_void;
|
||||
pub fn lua_touserdata(L: *mut lua_State, idx: c_int) -> *mut c_void;
|
||||
pub fn lua_touserdatatagged(L: *mut lua_State, idx: c_int, tag: c_int) -> *mut c_void;
|
||||
pub fn lua_userdatatag(L: *mut lua_State, idx: c_int) -> c_int;
|
||||
pub fn lua_lightuserdatatag(L: *mut lua_State, idx: c_int) -> c_int;
|
||||
pub fn lua_tothread(L: *mut lua_State, idx: c_int) -> *mut lua_State;
|
||||
pub fn lua_tobuffer(L: *mut lua_State, idx: c_int, len: *mut usize) -> *mut c_void;
|
||||
pub fn lua_topointer(L: *mut lua_State, idx: c_int) -> *const c_void;
|
||||
|
||||
//
|
||||
@@ -177,10 +187,12 @@ extern "C-unwind" {
|
||||
pub fn lua_pushboolean(L: *mut lua_State, b: c_int);
|
||||
pub fn lua_pushthread(L: *mut lua_State) -> c_int;
|
||||
|
||||
pub fn lua_pushlightuserdata(L: *mut lua_State, p: *mut c_void);
|
||||
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_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;
|
||||
|
||||
//
|
||||
// Get functions (Lua -> stack)
|
||||
//
|
||||
@@ -276,8 +288,13 @@ extern "C-unwind" {
|
||||
pub fn lua_setuserdatatag(L: *mut lua_State, idx: c_int, tag: c_int);
|
||||
pub fn lua_setuserdatadtor(L: *mut lua_State, tag: c_int, dtor: Option<lua_Destructor>);
|
||||
pub fn lua_getuserdatadtor(L: *mut lua_State, tag: c_int) -> Option<lua_Destructor>;
|
||||
pub fn lua_setuserdatametatable(L: *mut lua_State, tag: c_int, idx: c_int);
|
||||
pub fn lua_getuserdatametatable(L: *mut lua_State, tag: c_int);
|
||||
pub fn lua_setlightuserdataname(L: *mut lua_State, tag: c_int, name: *const c_char);
|
||||
pub fn lua_getlightuserdataname(L: *mut lua_State, tag: c_int) -> *const c_char;
|
||||
pub fn lua_clonefunction(L: *mut lua_State, idx: c_int);
|
||||
pub fn lua_cleartable(L: *mut lua_State, idx: c_int);
|
||||
pub fn lua_getallocf(L: *mut lua_State, ud: *mut *mut c_void) -> lua_Alloc;
|
||||
}
|
||||
|
||||
//
|
||||
@@ -325,6 +342,15 @@ pub unsafe fn lua_newuserdata(L: *mut lua_State, sz: usize) -> *mut c_void {
|
||||
lua_newuserdatatagged(L, sz, 0)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn lua_newuserdata_t<T>(L: *mut lua_State) -> *mut T {
|
||||
unsafe extern "C-unwind" fn destructor<T>(ud: *mut c_void) {
|
||||
ptr::drop_in_place(ud as *mut T);
|
||||
}
|
||||
|
||||
lua_newuserdatadtor(L, mem::size_of::<T>(), destructor::<T>) as *mut T
|
||||
}
|
||||
|
||||
// TODO: lua_strlen
|
||||
|
||||
#[inline(always)]
|
||||
@@ -362,6 +388,11 @@ pub unsafe fn lua_isthread(L: *mut lua_State, n: c_int) -> c_int {
|
||||
(lua_type(L, n) == LUA_TTHREAD) as c_int
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn lua_isbuffer(L: *mut lua_State, n: c_int) -> c_int {
|
||||
(lua_type(L, n) == LUA_TBUFFER) as c_int
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn lua_isnone(L: *mut lua_State, n: c_int) -> c_int {
|
||||
(lua_type(L, n) == LUA_TNONE) as c_int
|
||||
@@ -379,18 +410,22 @@ pub unsafe fn lua_pushliteral(L: *mut lua_State, s: &'static str) {
|
||||
lua_pushlstring_(L, c_str.as_ptr(), c_str.as_bytes().len())
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn lua_pushcfunction(L: *mut lua_State, f: lua_CFunction) {
|
||||
lua_pushcclosurek(L, f, ptr::null(), 0, None)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn lua_pushcfunctiond(L: *mut lua_State, f: lua_CFunction, debugname: *const c_char) {
|
||||
lua_pushcclosurek(L, f, debugname, 0, None)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn lua_pushcclosure(L: *mut lua_State, f: lua_CFunction, nup: c_int) {
|
||||
lua_pushcclosurek(L, f, ptr::null(), nup, None)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn lua_pushcclosured(
|
||||
L: *mut lua_State,
|
||||
f: lua_CFunction,
|
||||
@@ -400,6 +435,11 @@ pub unsafe fn lua_pushcclosured(
|
||||
lua_pushcclosurek(L, f, debugname, nup, None)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn lua_pushlightuserdata(L: *mut lua_State, p: *mut c_void) {
|
||||
lua_pushlightuserdatatagged(L, p, 0)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn lua_setglobal(L: *mut lua_State, var: *const c_char) {
|
||||
lua_setfield(L, LUA_GLOBALSINDEX, var)
|
||||
@@ -487,6 +527,7 @@ pub struct lua_Debug {
|
||||
//
|
||||
|
||||
#[repr(C)]
|
||||
#[non_exhaustive]
|
||||
pub struct lua_Callbacks {
|
||||
/// arbitrary userdata pointer that is never overwritten by Luau
|
||||
pub userdata: *mut c_void,
|
||||
@@ -514,3 +555,8 @@ pub struct lua_Callbacks {
|
||||
extern "C" {
|
||||
pub fn lua_callbacks(L: *mut lua_State) -> *mut lua_Callbacks;
|
||||
}
|
||||
|
||||
// Functions from customization lib
|
||||
extern "C" {
|
||||
pub fn luau_setfflag(name: *const c_char, value: c_int) -> c_int;
|
||||
}
|
||||
|
||||
@@ -1,16 +1,36 @@
|
||||
//! Contains definitions from `luacode.h`.
|
||||
|
||||
use std::os::raw::{c_char, c_int, c_void};
|
||||
use std::slice;
|
||||
use std::{ptr, slice};
|
||||
|
||||
#[repr(C)]
|
||||
#[non_exhaustive]
|
||||
pub struct lua_CompileOptions {
|
||||
pub optimizationLevel: c_int,
|
||||
pub debugLevel: c_int,
|
||||
pub typeInfoLevel: c_int,
|
||||
pub coverageLevel: c_int,
|
||||
pub vectorLib: *const c_char,
|
||||
pub vectorCtor: *const c_char,
|
||||
pub mutableGlobals: *mut *const c_char,
|
||||
pub vectorType: *const c_char,
|
||||
pub mutableGlobals: *const *const c_char,
|
||||
pub userdataTypes: *const *const c_char,
|
||||
}
|
||||
|
||||
impl Default for lua_CompileOptions {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
optimizationLevel: 1,
|
||||
debugLevel: 1,
|
||||
typeInfoLevel: 0,
|
||||
coverageLevel: 0,
|
||||
vectorLib: ptr::null(),
|
||||
vectorCtor: ptr::null(),
|
||||
vectorType: ptr::null(),
|
||||
mutableGlobals: ptr::null(),
|
||||
userdataTypes: ptr::null(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extern "C-unwind" {
|
||||
@@ -35,6 +55,7 @@ pub unsafe fn luau_compile(source: &[u8], mut options: lua_CompileOptions) -> Ve
|
||||
&mut options,
|
||||
&mut outsize,
|
||||
);
|
||||
assert!(!data_ptr.is_null(), "luau_compile failed");
|
||||
let data = slice::from_raw_parts(data_ptr as *mut u8, outsize).to_vec();
|
||||
free(data_ptr as *mut c_void);
|
||||
data
|
||||
|
||||
@@ -9,6 +9,7 @@ pub const LUA_TABLIBNAME: &str = "table";
|
||||
pub const LUA_OSLIBNAME: &str = "os";
|
||||
pub const LUA_STRLIBNAME: &str = "string";
|
||||
pub const LUA_BITLIBNAME: &str = "bit32";
|
||||
pub const LUA_BUFFERLIBNAME: &str = "buffer";
|
||||
pub const LUA_UTF8LIBNAME: &str = "utf8";
|
||||
pub const LUA_MATHLIBNAME: &str = "math";
|
||||
pub const LUA_DBLIBNAME: &str = "debug";
|
||||
@@ -20,6 +21,7 @@ extern "C-unwind" {
|
||||
pub fn luaopen_os(L: *mut lua_State) -> c_int;
|
||||
pub fn luaopen_string(L: *mut lua_State) -> c_int;
|
||||
pub fn luaopen_bit32(L: *mut lua_State) -> c_int;
|
||||
pub fn luaopen_buffer(L: *mut lua_State) -> c_int;
|
||||
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;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "mlua_derive"
|
||||
version = "0.9.0-rc.2"
|
||||
version = "0.9.3"
|
||||
authors = ["Aleksandr Orlenko <zxteam@pm.me>"]
|
||||
edition = "2021"
|
||||
description = "Procedural macros for the mlua crate."
|
||||
@@ -19,6 +19,6 @@ quote = "1.0"
|
||||
proc-macro2 = { version = "1.0", features = ["span-locations"] }
|
||||
proc-macro-error = { version = "1.0", optional = true }
|
||||
syn = { version = "2.0", features = ["full"] }
|
||||
itertools = { version = "0.11", optional = true }
|
||||
itertools = { version = "0.12", optional = true }
|
||||
regex = { version = "1.4", optional = true }
|
||||
once_cell = { version = "1.0", optional = true }
|
||||
|
||||
@@ -7,16 +7,17 @@ pub fn from_lua(input: TokenStream) -> TokenStream {
|
||||
ident, generics, ..
|
||||
} = parse_macro_input!(input as DeriveInput);
|
||||
|
||||
let ident_str = ident.to_string();
|
||||
let (impl_generics, ty_generics, _) = generics.split_for_impl();
|
||||
let where_clause = match &generics.where_clause {
|
||||
Some(where_clause) => quote! { #where_clause, Self: 'static + Clone },
|
||||
None => quote! { where Self: 'static + Clone },
|
||||
};
|
||||
let ident_str = ident.to_string();
|
||||
|
||||
quote! {
|
||||
impl #generics ::mlua::FromLua<'_> for #ident #generics #where_clause {
|
||||
impl #impl_generics ::mlua::FromLua<'_> for #ident #ty_generics #where_clause {
|
||||
#[inline]
|
||||
fn from_lua(value: ::mlua::Value<'_>, lua: &'_ ::mlua::Lua) -> ::mlua::Result<Self> {
|
||||
fn from_lua(value: ::mlua::Value<'_>, _: &'_ ::mlua::Lua) -> ::mlua::Result<Self> {
|
||||
match value {
|
||||
::mlua::Value::UserData(ud) => Ok(ud.borrow::<Self>()?.clone()),
|
||||
_ => Err(::mlua::Error::FromLuaConversionError {
|
||||
|
||||
+11
-11
@@ -51,22 +51,22 @@ pub fn lua_module(attr: TokenStream, item: TokenStream) -> TokenStream {
|
||||
let func_name = &func.sig.ident;
|
||||
let module_name = args.name.unwrap_or_else(|| func_name.clone());
|
||||
let ext_entrypoint_name = Ident::new(&format!("luaopen_{module_name}"), Span::call_site());
|
||||
let skip_memory_check = args.skip_memory_check;
|
||||
let skip_memory_check = if args.skip_memory_check {
|
||||
quote! { lua.skip_memory_check(true); }
|
||||
} else {
|
||||
quote! {}
|
||||
};
|
||||
|
||||
let wrapped = quote! {
|
||||
::mlua::require_module_feature!();
|
||||
mlua::require_module_feature!();
|
||||
|
||||
#func
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C-unwind" fn #ext_entrypoint_name(state: *mut ::mlua::lua_State) -> ::std::os::raw::c_int {
|
||||
let lua = ::mlua::Lua::init_from_ptr(state);
|
||||
if #skip_memory_check {
|
||||
lua.skip_memory_check(true);
|
||||
}
|
||||
lua
|
||||
.entrypoint1(#func_name)
|
||||
.expect("cannot initialize module")
|
||||
unsafe extern "C-unwind" fn #ext_entrypoint_name(state: *mut mlua::lua_State) -> ::std::os::raw::c_int {
|
||||
let lua = mlua::Lua::init_from_ptr(state);
|
||||
#skip_memory_check
|
||||
lua.entrypoint1(state, #func_name)
|
||||
}
|
||||
};
|
||||
|
||||
@@ -95,7 +95,7 @@ pub fn chunk(input: TokenStream) -> TokenStream {
|
||||
});
|
||||
|
||||
let wrapped_code = quote! {{
|
||||
use ::mlua::{AsChunk, ChunkMode, Lua, Result, Table};
|
||||
use mlua::{AsChunk, ChunkMode, Lua, Result, Table};
|
||||
use ::std::borrow::Cow;
|
||||
use ::std::cell::Cell;
|
||||
use ::std::io::Result as IoResult;
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
use std::{
|
||||
cmp::{Eq, PartialEq},
|
||||
fmt::{self, Display, Formatter},
|
||||
iter::IntoIterator,
|
||||
vec::IntoIter,
|
||||
};
|
||||
|
||||
|
||||
+66
-27
@@ -122,10 +122,13 @@ pub enum ChunkMode {
|
||||
pub struct Compiler {
|
||||
optimization_level: u8,
|
||||
debug_level: u8,
|
||||
type_info_level: u8,
|
||||
coverage_level: u8,
|
||||
vector_lib: Option<String>,
|
||||
vector_ctor: Option<String>,
|
||||
vector_type: Option<String>,
|
||||
mutable_globals: Vec<String>,
|
||||
userdata_types: Vec<String>,
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "luau", doc))]
|
||||
@@ -143,10 +146,13 @@ impl Compiler {
|
||||
Compiler {
|
||||
optimization_level: 1,
|
||||
debug_level: 1,
|
||||
type_info_level: 0,
|
||||
coverage_level: 0,
|
||||
vector_lib: None,
|
||||
vector_ctor: None,
|
||||
vector_type: None,
|
||||
mutable_globals: Vec::new(),
|
||||
userdata_types: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -174,6 +180,16 @@ impl Compiler {
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets Luau type information level used to guide native code generation decisions.
|
||||
///
|
||||
/// Possible values:
|
||||
/// * 0 - generate for native modules (default)
|
||||
/// * 1 - generate for all modules
|
||||
pub const fn set_type_info_level(mut self, level: u8) -> Self {
|
||||
self.type_info_level = level;
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets Luau compiler code coverage level.
|
||||
///
|
||||
/// Possible values:
|
||||
@@ -188,15 +204,22 @@ impl Compiler {
|
||||
|
||||
#[doc(hidden)]
|
||||
#[must_use]
|
||||
pub fn set_vector_lib(mut self, lib: Option<String>) -> Self {
|
||||
self.vector_lib = lib;
|
||||
pub fn set_vector_lib(mut self, lib: impl Into<String>) -> Self {
|
||||
self.vector_lib = Some(lib.into());
|
||||
self
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
#[must_use]
|
||||
pub fn set_vector_ctor(mut self, ctor: Option<String>) -> Self {
|
||||
self.vector_ctor = ctor;
|
||||
pub fn set_vector_ctor(mut self, ctor: impl Into<String>) -> Self {
|
||||
self.vector_ctor = Some(ctor.into());
|
||||
self
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
#[must_use]
|
||||
pub fn set_vector_type(mut self, r#type: impl Into<String>) -> Self {
|
||||
self.vector_type = Some(r#type.into());
|
||||
self
|
||||
}
|
||||
|
||||
@@ -209,6 +232,13 @@ impl Compiler {
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets a list of userdata types that will be included in the type information.
|
||||
#[must_use]
|
||||
pub fn set_userdata_types(mut self, types: Vec<String>) -> Self {
|
||||
self.userdata_types = types;
|
||||
self
|
||||
}
|
||||
|
||||
/// Compiles the `source` into bytecode.
|
||||
pub fn compile(&self, source: impl AsRef<[u8]>) -> Vec<u8> {
|
||||
use std::os::raw::c_int;
|
||||
@@ -220,32 +250,41 @@ impl Compiler {
|
||||
let vector_ctor = self.vector_ctor.clone();
|
||||
let vector_ctor = vector_ctor.and_then(|ctor| CString::new(ctor).ok());
|
||||
let vector_ctor = vector_ctor.as_ref();
|
||||
let vector_type = self.vector_type.clone();
|
||||
let vector_type = vector_type.and_then(|t| CString::new(t).ok());
|
||||
let vector_type = vector_type.as_ref();
|
||||
|
||||
let mutable_globals = self
|
||||
.mutable_globals
|
||||
.iter()
|
||||
.map(|name| CString::new(name.clone()).ok())
|
||||
.collect::<Option<Vec<_>>>()
|
||||
.unwrap_or_default();
|
||||
let mut mutable_globals = mutable_globals
|
||||
.iter()
|
||||
.map(|s| s.as_ptr())
|
||||
.collect::<Vec<_>>();
|
||||
let mut mutable_globals_ptr = ptr::null_mut();
|
||||
if !mutable_globals.is_empty() {
|
||||
mutable_globals.push(ptr::null());
|
||||
mutable_globals_ptr = mutable_globals.as_mut_ptr();
|
||||
macro_rules! vec2cstring_ptr {
|
||||
($name:ident, $name_ptr:ident) => {
|
||||
let $name = self
|
||||
.$name
|
||||
.iter()
|
||||
.map(|name| CString::new(name.clone()).ok())
|
||||
.collect::<Option<Vec<_>>>()
|
||||
.unwrap_or_default();
|
||||
let mut $name = $name.iter().map(|s| s.as_ptr()).collect::<Vec<_>>();
|
||||
let mut $name_ptr = ptr::null();
|
||||
if !$name.is_empty() {
|
||||
$name.push(ptr::null());
|
||||
$name_ptr = $name.as_ptr();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
vec2cstring_ptr!(mutable_globals, mutable_globals_ptr);
|
||||
vec2cstring_ptr!(userdata_types, userdata_types_ptr);
|
||||
|
||||
unsafe {
|
||||
let options = ffi::lua_CompileOptions {
|
||||
optimizationLevel: self.optimization_level as c_int,
|
||||
debugLevel: self.debug_level as c_int,
|
||||
coverageLevel: self.coverage_level as c_int,
|
||||
vectorLib: vector_lib.map_or(ptr::null(), |s| s.as_ptr()),
|
||||
vectorCtor: vector_ctor.map_or(ptr::null(), |s| s.as_ptr()),
|
||||
mutableGlobals: mutable_globals_ptr,
|
||||
};
|
||||
let mut options = ffi::lua_CompileOptions::default();
|
||||
options.optimizationLevel = self.optimization_level as c_int;
|
||||
options.debugLevel = self.debug_level as c_int;
|
||||
options.typeInfoLevel = self.type_info_level as c_int;
|
||||
options.coverageLevel = self.coverage_level as c_int;
|
||||
options.vectorLib = vector_lib.map_or(ptr::null(), |s| s.as_ptr());
|
||||
options.vectorCtor = vector_ctor.map_or(ptr::null(), |s| s.as_ptr());
|
||||
options.vectorType = vector_type.map_or(ptr::null(), |s| s.as_ptr());
|
||||
options.mutableGlobals = mutable_globals_ptr;
|
||||
options.userdataTypes = userdata_types_ptr;
|
||||
ffi::luau_compile(source.as_ref(), options)
|
||||
}
|
||||
}
|
||||
@@ -302,7 +341,7 @@ impl<'lua, 'a> Chunk<'lua, 'a> {
|
||||
///
|
||||
/// This is equivalent to calling the chunk function with no arguments and no return values.
|
||||
pub fn exec(self) -> Result<()> {
|
||||
self.call(())?;
|
||||
self.call::<_, ()>(())?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
+356
-9
@@ -1,9 +1,10 @@
|
||||
use std::borrow::Cow;
|
||||
use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet};
|
||||
use std::convert::TryInto;
|
||||
use std::ffi::{CStr, CString};
|
||||
use std::hash::{BuildHasher, Hash};
|
||||
use std::os::raw::c_int;
|
||||
use std::string::String as StdString;
|
||||
use std::{slice, str};
|
||||
|
||||
use bstr::{BStr, BString};
|
||||
use num_traits::cast;
|
||||
@@ -14,12 +15,15 @@ use crate::lua::Lua;
|
||||
use crate::string::String;
|
||||
use crate::table::Table;
|
||||
use crate::thread::Thread;
|
||||
use crate::types::{LightUserData, MaybeSend};
|
||||
use crate::types::{LightUserData, MaybeSend, RegistryKey};
|
||||
use crate::userdata::{AnyUserData, UserData, UserDataRef, UserDataRefMut};
|
||||
use crate::value::{FromLua, IntoLua, Nil, Value};
|
||||
|
||||
#[cfg(all(feature = "unstable", any(not(feature = "send"), doc)))]
|
||||
use crate::{function::OwnedFunction, table::OwnedTable, userdata::OwnedAnyUserData};
|
||||
use crate::{
|
||||
function::OwnedFunction, string::OwnedString, table::OwnedTable, thread::OwnedThread,
|
||||
userdata::OwnedAnyUserData,
|
||||
};
|
||||
|
||||
impl<'lua> IntoLua<'lua> for Value<'lua> {
|
||||
#[inline]
|
||||
@@ -28,6 +32,18 @@ impl<'lua> IntoLua<'lua> for Value<'lua> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> IntoLua<'lua> for &Value<'lua> {
|
||||
#[inline]
|
||||
fn into_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
|
||||
Ok(self.clone())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn push_into_stack(self, lua: &'lua Lua) -> Result<()> {
|
||||
lua.push_value_ref(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> FromLua<'lua> for Value<'lua> {
|
||||
#[inline]
|
||||
fn from_lua(lua_value: Value<'lua>, _: &'lua Lua) -> Result<Self> {
|
||||
@@ -42,6 +58,19 @@ impl<'lua> IntoLua<'lua> for String<'lua> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> IntoLua<'lua> for &String<'lua> {
|
||||
#[inline]
|
||||
fn into_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
|
||||
Ok(Value::String(self.clone()))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn push_into_stack(self, lua: &'lua Lua) -> Result<()> {
|
||||
lua.push_ref(&self.0);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> FromLua<'lua> for String<'lua> {
|
||||
#[inline]
|
||||
fn from_lua(value: Value<'lua>, lua: &'lua Lua) -> Result<String<'lua>> {
|
||||
@@ -55,6 +84,39 @@ impl<'lua> FromLua<'lua> for String<'lua> {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "unstable", any(not(feature = "send"), doc)))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all(feature = "unstable", not(feature = "send")))))]
|
||||
impl<'lua> IntoLua<'lua> for OwnedString {
|
||||
#[inline]
|
||||
fn into_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
|
||||
Ok(Value::String(String(lua.adopt_owned_ref(self.0))))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "unstable", any(not(feature = "send"), doc)))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all(feature = "unstable", not(feature = "send")))))]
|
||||
impl<'lua> IntoLua<'lua> for &OwnedString {
|
||||
#[inline]
|
||||
fn into_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
|
||||
OwnedString::into_lua(self.clone(), lua)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn push_into_stack(self, lua: &'lua Lua) -> Result<()> {
|
||||
lua.push_owned_ref(&self.0);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "unstable", any(not(feature = "send"), doc)))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all(feature = "unstable", not(feature = "send")))))]
|
||||
impl<'lua> FromLua<'lua> for OwnedString {
|
||||
#[inline]
|
||||
fn from_lua(value: Value<'lua>, lua: &'lua Lua) -> Result<OwnedString> {
|
||||
String::from_lua(value, lua).map(|s| s.into_owned())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> IntoLua<'lua> for Table<'lua> {
|
||||
#[inline]
|
||||
fn into_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
|
||||
@@ -62,6 +124,19 @@ impl<'lua> IntoLua<'lua> for Table<'lua> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> IntoLua<'lua> for &Table<'lua> {
|
||||
#[inline]
|
||||
fn into_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
|
||||
Ok(Value::Table(self.clone()))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn push_into_stack(self, lua: &'lua Lua) -> Result<()> {
|
||||
lua.push_ref(&self.0);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> FromLua<'lua> for Table<'lua> {
|
||||
#[inline]
|
||||
fn from_lua(value: Value<'lua>, _: &'lua Lua) -> Result<Table<'lua>> {
|
||||
@@ -85,6 +160,21 @@ impl<'lua> IntoLua<'lua> for OwnedTable {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "unstable", any(not(feature = "send"), doc)))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all(feature = "unstable", not(feature = "send")))))]
|
||||
impl<'lua> IntoLua<'lua> for &OwnedTable {
|
||||
#[inline]
|
||||
fn into_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
|
||||
OwnedTable::into_lua(self.clone(), lua)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn push_into_stack(self, lua: &'lua Lua) -> Result<()> {
|
||||
lua.push_owned_ref(&self.0);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "unstable", any(not(feature = "send"), doc)))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all(feature = "unstable", not(feature = "send")))))]
|
||||
impl<'lua> FromLua<'lua> for OwnedTable {
|
||||
@@ -101,6 +191,19 @@ impl<'lua> IntoLua<'lua> for Function<'lua> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> IntoLua<'lua> for &Function<'lua> {
|
||||
#[inline]
|
||||
fn into_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
|
||||
Ok(Value::Function(self.clone()))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn push_into_stack(self, lua: &'lua Lua) -> Result<()> {
|
||||
lua.push_ref(&self.0);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> FromLua<'lua> for Function<'lua> {
|
||||
#[inline]
|
||||
fn from_lua(value: Value<'lua>, _: &'lua Lua) -> Result<Function<'lua>> {
|
||||
@@ -124,6 +227,21 @@ impl<'lua> IntoLua<'lua> for OwnedFunction {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "unstable", any(not(feature = "send"), doc)))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all(feature = "unstable", not(feature = "send")))))]
|
||||
impl<'lua> IntoLua<'lua> for &OwnedFunction {
|
||||
#[inline]
|
||||
fn into_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
|
||||
OwnedFunction::into_lua(self.clone(), lua)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn push_into_stack(self, lua: &'lua Lua) -> Result<()> {
|
||||
lua.push_owned_ref(&self.0);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "unstable", any(not(feature = "send"), doc)))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all(feature = "unstable", not(feature = "send")))))]
|
||||
impl<'lua> FromLua<'lua> for OwnedFunction {
|
||||
@@ -140,6 +258,19 @@ impl<'lua> IntoLua<'lua> for Thread<'lua> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> IntoLua<'lua> for &Thread<'lua> {
|
||||
#[inline]
|
||||
fn into_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
|
||||
Ok(Value::Thread(self.clone()))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn push_into_stack(self, lua: &'lua Lua) -> Result<()> {
|
||||
lua.push_ref(&self.0);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> FromLua<'lua> for Thread<'lua> {
|
||||
#[inline]
|
||||
fn from_lua(value: Value<'lua>, _: &'lua Lua) -> Result<Thread<'lua>> {
|
||||
@@ -154,6 +285,39 @@ impl<'lua> FromLua<'lua> for Thread<'lua> {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "unstable", any(not(feature = "send"), doc)))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all(feature = "unstable", not(feature = "send")))))]
|
||||
impl<'lua> IntoLua<'lua> for OwnedThread {
|
||||
#[inline]
|
||||
fn into_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
|
||||
Ok(Value::Thread(Thread(lua.adopt_owned_ref(self.0), self.1)))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "unstable", any(not(feature = "send"), doc)))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all(feature = "unstable", not(feature = "send")))))]
|
||||
impl<'lua> IntoLua<'lua> for &OwnedThread {
|
||||
#[inline]
|
||||
fn into_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
|
||||
OwnedThread::into_lua(self.clone(), lua)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn push_into_stack(self, lua: &'lua Lua) -> Result<()> {
|
||||
lua.push_owned_ref(&self.0);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "unstable", any(not(feature = "send"), doc)))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all(feature = "unstable", not(feature = "send")))))]
|
||||
impl<'lua> FromLua<'lua> for OwnedThread {
|
||||
#[inline]
|
||||
fn from_lua(value: Value<'lua>, lua: &'lua Lua) -> Result<OwnedThread> {
|
||||
Thread::from_lua(value, lua).map(|s| s.into_owned())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> IntoLua<'lua> for AnyUserData<'lua> {
|
||||
#[inline]
|
||||
fn into_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
|
||||
@@ -161,6 +325,19 @@ impl<'lua> IntoLua<'lua> for AnyUserData<'lua> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> IntoLua<'lua> for &AnyUserData<'lua> {
|
||||
#[inline]
|
||||
fn into_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
|
||||
Ok(Value::UserData(self.clone()))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn push_into_stack(self, lua: &'lua Lua) -> Result<()> {
|
||||
lua.push_ref(&self.0);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> FromLua<'lua> for AnyUserData<'lua> {
|
||||
#[inline]
|
||||
fn from_lua(value: Value<'lua>, _: &'lua Lua) -> Result<AnyUserData<'lua>> {
|
||||
@@ -180,7 +357,25 @@ impl<'lua> FromLua<'lua> for AnyUserData<'lua> {
|
||||
impl<'lua> IntoLua<'lua> for OwnedAnyUserData {
|
||||
#[inline]
|
||||
fn into_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
|
||||
Ok(Value::UserData(AnyUserData(lua.adopt_owned_ref(self.0))))
|
||||
Ok(Value::UserData(AnyUserData(
|
||||
lua.adopt_owned_ref(self.0),
|
||||
self.1,
|
||||
)))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "unstable", any(not(feature = "send"), doc)))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all(feature = "unstable", not(feature = "send")))))]
|
||||
impl<'lua> IntoLua<'lua> for &OwnedAnyUserData {
|
||||
#[inline]
|
||||
fn into_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
|
||||
OwnedAnyUserData::into_lua(self.clone(), lua)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn push_into_stack(self, lua: &'lua Lua) -> Result<()> {
|
||||
lua.push_owned_ref(&self.0);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -235,11 +430,57 @@ impl<'lua> FromLua<'lua> for Error {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> IntoLua<'lua> for RegistryKey {
|
||||
#[inline]
|
||||
fn into_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
|
||||
lua.registry_value(&self)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn push_into_stack(self, lua: &'lua Lua) -> Result<()> {
|
||||
<&RegistryKey>::push_into_stack(&self, lua)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> IntoLua<'lua> for &RegistryKey {
|
||||
#[inline]
|
||||
fn into_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
|
||||
lua.registry_value(self)
|
||||
}
|
||||
|
||||
unsafe fn push_into_stack(self, lua: &'lua Lua) -> Result<()> {
|
||||
if !lua.owns_registry_value(self) {
|
||||
return Err(Error::MismatchedRegistryKey);
|
||||
}
|
||||
|
||||
match self.id() {
|
||||
ffi::LUA_REFNIL => ffi::lua_pushnil(lua.state()),
|
||||
id => {
|
||||
ffi::lua_rawgeti(lua.state(), ffi::LUA_REGISTRYINDEX, id as _);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> FromLua<'lua> for RegistryKey {
|
||||
#[inline]
|
||||
fn from_lua(value: Value<'lua>, lua: &'lua Lua) -> Result<RegistryKey> {
|
||||
lua.create_registry_value(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> IntoLua<'lua> for bool {
|
||||
#[inline]
|
||||
fn into_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
|
||||
Ok(Value::Boolean(self))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn push_into_stack(self, lua: &'lua Lua) -> Result<()> {
|
||||
ffi::lua_pushboolean(lua.state(), self as c_int);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> FromLua<'lua> for bool {
|
||||
@@ -251,6 +492,11 @@ impl<'lua> FromLua<'lua> for bool {
|
||||
_ => Ok(true),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn from_stack(idx: c_int, lua: &'lua Lua) -> Result<Self> {
|
||||
Ok(ffi::lua_toboolean(lua.state(), idx) != 0)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> IntoLua<'lua> for LightUserData {
|
||||
@@ -302,6 +548,11 @@ impl<'lua> IntoLua<'lua> for StdString {
|
||||
fn into_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
|
||||
Ok(Value::String(lua.create_string(&self)?))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn push_into_stack(self, lua: &'lua Lua) -> Result<()> {
|
||||
push_bytes_into_stack(self, lua)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> FromLua<'lua> for StdString {
|
||||
@@ -318,6 +569,25 @@ impl<'lua> FromLua<'lua> for StdString {
|
||||
.to_str()?
|
||||
.to_owned())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn from_stack(idx: c_int, lua: &'lua Lua) -> Result<Self> {
|
||||
let state = lua.state();
|
||||
if ffi::lua_type(state, idx) == ffi::LUA_TSTRING {
|
||||
let mut size = 0;
|
||||
let data = ffi::lua_tolstring(state, idx, &mut size);
|
||||
let bytes = slice::from_raw_parts(data as *const u8, size);
|
||||
return str::from_utf8(bytes).map(|s| s.to_owned()).map_err(|e| {
|
||||
Error::FromLuaConversionError {
|
||||
from: "string",
|
||||
to: "String",
|
||||
message: Some(e.to_string()),
|
||||
}
|
||||
});
|
||||
}
|
||||
// Fallback to default
|
||||
Self::from_lua(lua.stack_value(idx), lua)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> IntoLua<'lua> for &str {
|
||||
@@ -325,6 +595,11 @@ impl<'lua> IntoLua<'lua> for &str {
|
||||
fn into_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
|
||||
Ok(Value::String(lua.create_string(self)?))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn push_into_stack(self, lua: &'lua Lua) -> Result<()> {
|
||||
push_bytes_into_stack(self, lua)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> IntoLua<'lua> for Cow<'_, str> {
|
||||
@@ -410,19 +685,49 @@ impl<'lua> IntoLua<'lua> for BString {
|
||||
}
|
||||
|
||||
impl<'lua> FromLua<'lua> for BString {
|
||||
#[inline]
|
||||
fn from_lua(value: Value<'lua>, lua: &'lua Lua) -> Result<Self> {
|
||||
let ty = value.type_name();
|
||||
Ok(BString::from(
|
||||
lua.coerce_string(value)?
|
||||
match value {
|
||||
Value::String(s) => Ok(s.as_bytes().into()),
|
||||
#[cfg(feature = "luau")]
|
||||
Value::UserData(ud) if ud.1 == crate::types::SubtypeId::Buffer => unsafe {
|
||||
let mut size = 0usize;
|
||||
let buf = ffi::lua_tobuffer(ud.0.lua.ref_thread(), ud.0.index, &mut size);
|
||||
mlua_assert!(!buf.is_null(), "invalid Luau buffer");
|
||||
Ok(slice::from_raw_parts(buf as *const u8, size).into())
|
||||
},
|
||||
_ => Ok(lua
|
||||
.coerce_string(value)?
|
||||
.ok_or_else(|| Error::FromLuaConversionError {
|
||||
from: ty,
|
||||
to: "BString",
|
||||
message: Some("expected string or number".to_string()),
|
||||
})?
|
||||
.as_bytes()
|
||||
.to_vec(),
|
||||
))
|
||||
.into()),
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn from_stack(idx: c_int, lua: &'lua Lua) -> Result<Self> {
|
||||
let state = lua.state();
|
||||
match ffi::lua_type(state, idx) {
|
||||
ffi::LUA_TSTRING => {
|
||||
let mut size = 0;
|
||||
let data = ffi::lua_tolstring(state, idx, &mut size);
|
||||
Ok(slice::from_raw_parts(data as *const u8, size).into())
|
||||
}
|
||||
#[cfg(feature = "luau")]
|
||||
ffi::LUA_TBUFFER => {
|
||||
let mut size = 0;
|
||||
let buf = ffi::lua_tobuffer(state, idx, &mut size);
|
||||
mlua_assert!(!buf.is_null(), "invalid Luau buffer");
|
||||
Ok(slice::from_raw_parts(buf as *const u8, size).into())
|
||||
}
|
||||
_ => {
|
||||
// Fallback to default
|
||||
Self::from_lua(lua.stack_value(idx), lua)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -433,6 +738,21 @@ impl<'lua> IntoLua<'lua> for &BStr {
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn push_bytes_into_stack<'lua, T>(this: T, lua: &'lua Lua) -> Result<()>
|
||||
where
|
||||
T: IntoLua<'lua> + AsRef<[u8]>,
|
||||
{
|
||||
let bytes = this.as_ref();
|
||||
if lua.unlikely_memory_error() && bytes.len() < (1 << 30) {
|
||||
// Fast path: push directly into the Lua stack.
|
||||
ffi::lua_pushlstring(lua.state(), bytes.as_ptr() as *const _, bytes.len());
|
||||
return Ok(());
|
||||
}
|
||||
// Fallback to default
|
||||
lua.push_value(T::into_lua(this, lua)?)
|
||||
}
|
||||
|
||||
macro_rules! lua_convert_int {
|
||||
($x:ty) => {
|
||||
impl<'lua> IntoLua<'lua> for $x {
|
||||
@@ -448,6 +768,15 @@ macro_rules! lua_convert_int {
|
||||
message: Some("out of range".to_owned()),
|
||||
})
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn push_into_stack(self, lua: &'lua Lua) -> Result<()> {
|
||||
match cast(self) {
|
||||
Some(i) => ffi::lua_pushinteger(lua.state(), i),
|
||||
None => ffi::lua_pushnumber(lua.state(), self as ffi::lua_Number),
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> FromLua<'lua> for $x {
|
||||
@@ -740,6 +1069,15 @@ impl<'lua, T: IntoLua<'lua>> IntoLua<'lua> for Option<T> {
|
||||
None => Ok(Nil),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn push_into_stack(self, lua: &'lua Lua) -> Result<()> {
|
||||
match self {
|
||||
Some(val) => val.push_into_stack(lua)?,
|
||||
None => ffi::lua_pushnil(lua.state()),
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua, T: FromLua<'lua>> FromLua<'lua> for Option<T> {
|
||||
@@ -750,4 +1088,13 @@ impl<'lua, T: FromLua<'lua>> FromLua<'lua> for Option<T> {
|
||||
value => Ok(Some(T::from_lua(value, lua)?)),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn from_stack(idx: c_int, lua: &'lua Lua) -> Result<Self> {
|
||||
if ffi::lua_isnil(lua.state(), idx) != 0 {
|
||||
Ok(None)
|
||||
} else {
|
||||
Ok(Some(T::from_stack(idx, lua)?))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -101,7 +101,7 @@ pub enum Error {
|
||||
/// [`Thread::resume`] was called on an inactive coroutine.
|
||||
///
|
||||
/// A coroutine is inactive if its main function has returned or if an error has occurred inside
|
||||
/// the coroutine.
|
||||
/// the coroutine. Already running coroutines are also marked as inactive (unresumable).
|
||||
///
|
||||
/// [`Thread::status`] can be used to check if the coroutine can be resumed without causing this
|
||||
/// error.
|
||||
|
||||
+52
-29
@@ -6,12 +6,11 @@ use std::slice;
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
use crate::lua::Lua;
|
||||
use crate::memory::MemoryState;
|
||||
use crate::table::Table;
|
||||
use crate::types::{Callback, LuaRef, MaybeSend};
|
||||
use crate::util::{
|
||||
assert_stack, check_stack, error_traceback, linenumber_to_usize, pop_error, ptr_to_lossy_str,
|
||||
ptr_to_str, StackGuard,
|
||||
assert_stack, check_stack, linenumber_to_usize, pop_error, ptr_to_lossy_str, ptr_to_str,
|
||||
StackGuard,
|
||||
};
|
||||
use crate::value::{FromLuaMulti, IntoLua, IntoLuaMulti, Value};
|
||||
|
||||
@@ -126,34 +125,25 @@ impl<'lua> Function<'lua> {
|
||||
pub fn call<A: IntoLuaMulti<'lua>, R: FromLuaMulti<'lua>>(&self, args: A) -> Result<R> {
|
||||
let lua = self.0.lua;
|
||||
let state = lua.state();
|
||||
|
||||
let mut args = args.into_lua_multi(lua)?;
|
||||
let nargs = args.len() as c_int;
|
||||
|
||||
let results = unsafe {
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(state);
|
||||
check_stack(state, nargs + 3)?;
|
||||
check_stack(state, 2)?;
|
||||
|
||||
MemoryState::relax_limit_with(state, || ffi::lua_pushcfunction(state, error_traceback));
|
||||
// Push error handler
|
||||
lua.push_error_traceback();
|
||||
let stack_start = ffi::lua_gettop(state);
|
||||
// Push function and the arguments
|
||||
lua.push_ref(&self.0);
|
||||
for arg in args.drain_all() {
|
||||
lua.push_value(arg)?;
|
||||
}
|
||||
let nargs = args.push_into_stack_multi(lua)?;
|
||||
// Call the function
|
||||
let ret = ffi::lua_pcall(state, nargs, ffi::LUA_MULTRET, stack_start);
|
||||
if ret != ffi::LUA_OK {
|
||||
return Err(pop_error(state, ret));
|
||||
}
|
||||
// Get the results
|
||||
let nresults = ffi::lua_gettop(state) - stack_start;
|
||||
let mut results = args; // Reuse MultiValue container
|
||||
assert_stack(state, 2);
|
||||
for _ in 0..nresults {
|
||||
results.push_front(lua.pop_value());
|
||||
}
|
||||
ffi::lua_pop(state, 1);
|
||||
results
|
||||
};
|
||||
R::from_lua_multi(results, lua)
|
||||
R::from_stack_multi(nresults, lua)
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a future that, when polled, calls `self`, passing `args` as function arguments,
|
||||
@@ -504,6 +494,37 @@ impl<'lua> Function<'lua> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Converts this function to a generic C pointer.
|
||||
///
|
||||
/// There is no way to convert the pointer back to its original value.
|
||||
///
|
||||
/// Typically this function is used only for hashing and debug information.
|
||||
#[inline]
|
||||
pub fn to_pointer(&self) -> *const c_void {
|
||||
self.0.to_pointer()
|
||||
}
|
||||
|
||||
/// Creates a deep clone of the Lua 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_attr(docsrs, doc(cfg(feature = "luau")))]
|
||||
pub fn deep_clone(&self) -> Self {
|
||||
let ref_thread = self.0.lua.ref_thread();
|
||||
unsafe {
|
||||
if ffi::lua_iscfunction(ref_thread, self.0.index) != 0 {
|
||||
return self.clone();
|
||||
}
|
||||
|
||||
ffi::lua_clonefunction(ref_thread, self.0.index);
|
||||
Function(self.0.lua.pop_ref_thread())
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert this handle to owned version.
|
||||
#[cfg(all(feature = "unstable", any(not(feature = "send"), doc)))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all(feature = "unstable", not(feature = "send")))))]
|
||||
@@ -564,8 +585,9 @@ impl<'lua> Function<'lua> {
|
||||
R: IntoLuaMulti<'lua>,
|
||||
F: Fn(&'lua Lua, A) -> Result<R> + MaybeSend + 'static,
|
||||
{
|
||||
WrappedFunction(Box::new(move |lua, args| {
|
||||
func(lua, A::from_lua_multi(args, lua)?)?.into_lua_multi(lua)
|
||||
WrappedFunction(Box::new(move |lua, nargs| unsafe {
|
||||
let args = A::from_stack_args(nargs, 1, None, lua)?;
|
||||
func(lua, args)?.push_into_stack_multi(lua)
|
||||
}))
|
||||
}
|
||||
|
||||
@@ -578,11 +600,12 @@ impl<'lua> Function<'lua> {
|
||||
F: FnMut(&'lua Lua, A) -> Result<R> + MaybeSend + 'static,
|
||||
{
|
||||
let func = RefCell::new(func);
|
||||
WrappedFunction(Box::new(move |lua, args| {
|
||||
WrappedFunction(Box::new(move |lua, nargs| unsafe {
|
||||
let mut func = func
|
||||
.try_borrow_mut()
|
||||
.map_err(|_| Error::RecursiveMutCallback)?;
|
||||
func(lua, A::from_lua_multi(args, lua)?)?.into_lua_multi(lua)
|
||||
let args = A::from_stack_args(nargs, 1, None, lua)?;
|
||||
func(lua, args)?.push_into_stack_multi(lua)
|
||||
}))
|
||||
}
|
||||
|
||||
@@ -596,13 +619,13 @@ impl<'lua> Function<'lua> {
|
||||
F: Fn(&'lua Lua, A) -> FR + MaybeSend + 'static,
|
||||
FR: Future<Output = Result<R>> + 'lua,
|
||||
{
|
||||
WrappedAsyncFunction(Box::new(move |lua, args| {
|
||||
let args = match A::from_lua_multi(args, lua) {
|
||||
WrappedAsyncFunction(Box::new(move |lua, args| unsafe {
|
||||
let args = match A::from_lua_args(args, 1, None, lua) {
|
||||
Ok(args) => args,
|
||||
Err(e) => return Box::pin(future::err(e)),
|
||||
};
|
||||
let fut = func(lua, args);
|
||||
Box::pin(async move { fut.await?.into_lua_multi(lua) })
|
||||
Box::pin(async move { fut.await?.push_into_stack_multi(lua) })
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
+4
-2
@@ -353,8 +353,10 @@ impl HookTriggers {
|
||||
// Returns the `count` parameter to pass to `lua_sethook`, if applicable. Otherwise, zero is
|
||||
// returned.
|
||||
pub(crate) const fn count(&self) -> c_int {
|
||||
let Some(n) = self.every_nth_instruction else { return 0 };
|
||||
n as c_int
|
||||
match self.every_nth_instruction {
|
||||
Some(n) => n as c_int,
|
||||
None => 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+3
-2
@@ -73,7 +73,7 @@
|
||||
|
||||
// Deny warnings inside doc tests / examples. When this isn't present, rustdoc doesn't show *any*
|
||||
// warnings at all.
|
||||
#![doc(test(attr(deny(warnings))))]
|
||||
#![doc(test(attr(warn(warnings))))] // FIXME: Remove this when rust-lang/rust#123748 is fixed
|
||||
#![cfg_attr(docsrs, feature(doc_cfg))]
|
||||
|
||||
#[macro_use]
|
||||
@@ -157,7 +157,8 @@ extern crate mlua_derive;
|
||||
// Unstable features
|
||||
#[cfg(feature = "unstable")]
|
||||
pub use crate::{
|
||||
function::OwnedFunction, string::OwnedString, table::OwnedTable, userdata::OwnedAnyUserData,
|
||||
function::OwnedFunction, string::OwnedString, table::OwnedTable, thread::OwnedThread,
|
||||
userdata::OwnedAnyUserData,
|
||||
};
|
||||
|
||||
/// Create a type that implements [`AsChunk`] and can capture Rust variables.
|
||||
|
||||
+470
-286
File diff suppressed because it is too large
Load Diff
@@ -1,25 +1,19 @@
|
||||
use std::ffi::CStr;
|
||||
use std::os::raw::{c_float, c_int};
|
||||
use std::string::String as StdString;
|
||||
|
||||
use crate::chunk::ChunkMode;
|
||||
use crate::error::{Error, Result};
|
||||
use crate::error::Result;
|
||||
use crate::lua::Lua;
|
||||
use crate::table::Table;
|
||||
use crate::util::{check_stack, StackGuard};
|
||||
use crate::value::Value;
|
||||
|
||||
// Since Luau has some missing standard function, we re-implement them here
|
||||
// Since Luau has some missing standard functions, we re-implement them here
|
||||
|
||||
impl Lua {
|
||||
pub(crate) unsafe fn prepare_luau_state(&self) -> Result<()> {
|
||||
pub(crate) unsafe fn configure_luau(&self) -> Result<()> {
|
||||
let globals = self.globals();
|
||||
|
||||
globals.raw_set(
|
||||
"collectgarbage",
|
||||
self.create_c_function(lua_collectgarbage)?,
|
||||
)?;
|
||||
globals.raw_set("require", self.create_function(lua_require)?)?;
|
||||
globals.raw_set("vector", self.create_c_function(lua_vector)?)?;
|
||||
|
||||
// Set `_VERSION` global to include version number
|
||||
@@ -30,6 +24,11 @@ impl Lua {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn disable_c_modules(&self) -> Result<()> {
|
||||
package::disable_dylibs(self);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
unsafe extern "C-unwind" fn lua_collectgarbage(state: *mut ffi::lua_State) -> c_int {
|
||||
@@ -69,58 +68,6 @@ unsafe extern "C-unwind" fn lua_collectgarbage(state: *mut ffi::lua_State) -> c_
|
||||
}
|
||||
}
|
||||
|
||||
fn lua_require(lua: &Lua, name: Option<StdString>) -> Result<Value> {
|
||||
let name = name.ok_or_else(|| Error::runtime("invalid module name"))?;
|
||||
|
||||
// Find module in the cache
|
||||
let state = lua.state();
|
||||
let loaded = unsafe {
|
||||
let _sg = StackGuard::new(state);
|
||||
check_stack(state, 2)?;
|
||||
protect_lua!(state, 0, 1, fn(state) {
|
||||
ffi::luaL_getsubtable(state, ffi::LUA_REGISTRYINDEX, cstr!("_LOADED"));
|
||||
})?;
|
||||
Table(lua.pop_ref())
|
||||
};
|
||||
if let Some(v) = loaded.raw_get(name.clone())? {
|
||||
return Ok(v);
|
||||
}
|
||||
|
||||
// Load file from filesystem
|
||||
let mut search_path = std::env::var("LUAU_PATH").unwrap_or_default();
|
||||
if search_path.is_empty() {
|
||||
search_path = "?.luau;?.lua".into();
|
||||
}
|
||||
|
||||
let (mut source, mut source_name) = (None, String::new());
|
||||
for path in search_path.split(';') {
|
||||
let file_path = path.replacen('?', &name, 1);
|
||||
if let Ok(buf) = std::fs::read(&file_path) {
|
||||
source = Some(buf);
|
||||
source_name = file_path;
|
||||
break;
|
||||
}
|
||||
}
|
||||
let source = source.ok_or_else(|| Error::runtime(format!("cannot find '{name}'")))?;
|
||||
|
||||
let value = lua
|
||||
.load(&source)
|
||||
.set_name(&format!("={source_name}"))
|
||||
.set_mode(ChunkMode::Text)
|
||||
.call::<_, Value>(())?;
|
||||
|
||||
// Save in the cache
|
||||
loaded.raw_set(
|
||||
name,
|
||||
match value.clone() {
|
||||
Value::Nil => Value::Boolean(true),
|
||||
v => v,
|
||||
},
|
||||
)?;
|
||||
|
||||
Ok(value)
|
||||
}
|
||||
|
||||
// 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;
|
||||
@@ -135,3 +82,7 @@ unsafe extern "C-unwind" fn lua_vector(state: *mut ffi::lua_State) -> c_int {
|
||||
ffi::lua_pushvector(state, x, y, z, w);
|
||||
1
|
||||
}
|
||||
|
||||
pub(crate) use package::register_package_module;
|
||||
|
||||
mod package;
|
||||
@@ -0,0 +1,269 @@
|
||||
use std::ffi::CStr;
|
||||
use std::fmt::Write;
|
||||
use std::os::raw::c_int;
|
||||
use std::path::{PathBuf, MAIN_SEPARATOR_STR};
|
||||
use std::string::String as StdString;
|
||||
use std::{env, fs};
|
||||
|
||||
use crate::chunk::ChunkMode;
|
||||
use crate::error::Result;
|
||||
use crate::lua::Lua;
|
||||
use crate::table::Table;
|
||||
use crate::types::RegistryKey;
|
||||
use crate::value::{IntoLua, Value};
|
||||
|
||||
#[cfg(unix)]
|
||||
use {libloading::Library, rustc_hash::FxHashMap};
|
||||
|
||||
//
|
||||
// Luau package module
|
||||
//
|
||||
|
||||
#[cfg(unix)]
|
||||
const TARGET_MLUA_LUAU_ABI_VERSION: u32 = 1;
|
||||
|
||||
#[cfg(all(unix, feature = "module"))]
|
||||
#[no_mangle]
|
||||
#[used]
|
||||
pub static MLUA_LUAU_ABI_VERSION: u32 = TARGET_MLUA_LUAU_ABI_VERSION;
|
||||
|
||||
// We keep reference to the `package` table in registry under this key
|
||||
struct PackageKey(RegistryKey);
|
||||
|
||||
// We keep reference to the loaded dylibs in application data
|
||||
#[cfg(unix)]
|
||||
struct LoadedDylibs(FxHashMap<PathBuf, Library>);
|
||||
|
||||
#[cfg(unix)]
|
||||
impl std::ops::Deref for LoadedDylibs {
|
||||
type Target = FxHashMap<PathBuf, Library>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
impl std::ops::DerefMut for LoadedDylibs {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.0
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn register_package_module(lua: &Lua) -> Result<()> {
|
||||
// Create the package table and store it in app_data for later use (bypassing globals lookup)
|
||||
let package = lua.create_table()?;
|
||||
lua.set_app_data(PackageKey(lua.create_registry_value(package.clone())?));
|
||||
|
||||
// Set `package.path`
|
||||
let mut search_path = env::var("LUAU_PATH")
|
||||
.or_else(|_| env::var("LUA_PATH"))
|
||||
.unwrap_or_default();
|
||||
if search_path.is_empty() {
|
||||
search_path = "?.luau;?.lua".to_string();
|
||||
}
|
||||
package.raw_set("path", search_path)?;
|
||||
|
||||
// Set `package.cpath`
|
||||
#[cfg(unix)]
|
||||
{
|
||||
let mut search_cpath = env::var("LUAU_CPATH")
|
||||
.or_else(|_| env::var("LUA_CPATH"))
|
||||
.unwrap_or_default();
|
||||
if search_cpath.is_empty() {
|
||||
if cfg!(any(target_os = "macos", target_os = "ios")) {
|
||||
search_cpath = "?.dylib".to_string();
|
||||
} else {
|
||||
search_cpath = "?.so".to_string();
|
||||
}
|
||||
}
|
||||
package.raw_set("cpath", search_cpath)?;
|
||||
}
|
||||
|
||||
// Set `package.loaded` (table with a list of loaded modules)
|
||||
let loaded = lua.create_table()?;
|
||||
package.raw_set("loaded", loaded.clone())?;
|
||||
lua.set_named_registry_value("_LOADED", loaded)?;
|
||||
|
||||
// Set `package.loaders`
|
||||
let loaders = lua.create_sequence_from([lua.create_function(lua_loader)?])?;
|
||||
package.raw_set("loaders", loaders.clone())?;
|
||||
#[cfg(unix)]
|
||||
{
|
||||
loaders.push(lua.create_function(dylib_loader)?)?;
|
||||
lua.set_app_data(LoadedDylibs(FxHashMap::default()));
|
||||
}
|
||||
lua.set_named_registry_value("_LOADERS", loaders)?;
|
||||
|
||||
// Register the module and `require` function in globals
|
||||
let globals = lua.globals();
|
||||
globals.raw_set("package", package)?;
|
||||
globals.raw_set("require", unsafe { lua.create_c_function(lua_require)? })?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[allow(unused_variables)]
|
||||
pub(crate) fn disable_dylibs(lua: &Lua) {
|
||||
// Presence of `LoadedDylibs` in app data is used as a flag
|
||||
// to check whether binary modules are enabled
|
||||
#[cfg(unix)]
|
||||
lua.remove_app_data::<LoadedDylibs>();
|
||||
}
|
||||
|
||||
unsafe extern "C-unwind" fn lua_require(state: *mut ffi::lua_State) -> c_int {
|
||||
ffi::lua_settop(state, 1);
|
||||
let name = ffi::luaL_checkstring(state, 1);
|
||||
ffi::luaL_getsubtable(state, ffi::LUA_REGISTRYINDEX, cstr!("_LOADED")); // _LOADED is at index 2
|
||||
if ffi::lua_rawgetfield(state, 2, name) != ffi::LUA_TNIL {
|
||||
return 1; // module is already loaded
|
||||
}
|
||||
ffi::lua_pop(state, 1); // remove nil
|
||||
|
||||
// load the module
|
||||
let err_buf = ffi::lua_newuserdata_t::<StdString>(state);
|
||||
err_buf.write(StdString::new());
|
||||
ffi::luaL_getsubtable(state, ffi::LUA_REGISTRYINDEX, cstr!("_LOADERS")); // _LOADERS is at index 3
|
||||
for i in 1.. {
|
||||
if ffi::lua_rawgeti(state, -1, i) == ffi::LUA_TNIL {
|
||||
// no more loaders?
|
||||
if (*err_buf).is_empty() {
|
||||
ffi::luaL_error(state, cstr!("module '%s' not found"), name);
|
||||
} else {
|
||||
let bytes = (*err_buf).as_bytes();
|
||||
let extra = ffi::lua_pushlstring(state, bytes.as_ptr() as *const _, bytes.len());
|
||||
ffi::luaL_error(state, cstr!("module '%s' not found:%s"), name, extra);
|
||||
}
|
||||
}
|
||||
ffi::lua_pushvalue(state, 1); // name arg
|
||||
ffi::lua_call(state, 1, 2); // call loader
|
||||
match ffi::lua_type(state, -2) {
|
||||
ffi::LUA_TFUNCTION => break, // loader found
|
||||
ffi::LUA_TSTRING => {
|
||||
// error message
|
||||
let msg = ffi::lua_tostring(state, -2);
|
||||
let msg = CStr::from_ptr(msg).to_string_lossy();
|
||||
_ = write!(&mut *err_buf, "\n\t{msg}");
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
ffi::lua_pop(state, 2); // remove both results
|
||||
}
|
||||
ffi::lua_pushvalue(state, 1); // name is 1st argument to module loader
|
||||
ffi::lua_rotate(state, -2, 1); // loader data <-> name
|
||||
|
||||
// stack: ...; loader function; module name; loader data
|
||||
ffi::lua_call(state, 2, 1);
|
||||
// stack: ...; result from loader function
|
||||
if ffi::lua_isnil(state, -1) != 0 {
|
||||
ffi::lua_pop(state, 1);
|
||||
ffi::lua_pushboolean(state, 1); // use true as result
|
||||
}
|
||||
ffi::lua_pushvalue(state, -1); // make copy of entrypoint result
|
||||
ffi::lua_setfield(state, 2, name); /* _LOADED[name] = returned value */
|
||||
1
|
||||
}
|
||||
|
||||
/// Searches for the given `name` in the given `path`.
|
||||
///
|
||||
/// `path` is a string containing a sequence of templates separated by semicolons.
|
||||
fn package_searchpath(name: &str, search_path: &str, try_prefix: bool) -> Option<PathBuf> {
|
||||
let mut names = vec![name.replace('.', MAIN_SEPARATOR_STR)];
|
||||
if try_prefix && name.contains('.') {
|
||||
let prefix = name.split_once('.').map(|(prefix, _)| prefix).unwrap();
|
||||
names.push(prefix.to_string());
|
||||
}
|
||||
for path in search_path.split(';') {
|
||||
for name in &names {
|
||||
let file_path = PathBuf::from(path.replace('?', name));
|
||||
if let Ok(true) = fs::metadata(&file_path).map(|m| m.is_file()) {
|
||||
return Some(file_path);
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
//
|
||||
// Module loaders
|
||||
//
|
||||
|
||||
/// Tries to load a lua (text) file
|
||||
fn lua_loader(lua: &Lua, modname: StdString) -> Result<Value> {
|
||||
let package = {
|
||||
let key = lua.app_data_ref::<PackageKey>().unwrap();
|
||||
lua.registry_value::<Table>(&key.0)
|
||||
}?;
|
||||
let search_path = package.get::<_, StdString>("path").unwrap_or_default();
|
||||
|
||||
if let Some(file_path) = package_searchpath(&modname, &search_path, false) {
|
||||
match fs::read(&file_path) {
|
||||
Ok(buf) => {
|
||||
return lua
|
||||
.load(&buf)
|
||||
.set_name(&format!("={}", file_path.display()))
|
||||
.set_mode(ChunkMode::Text)
|
||||
.into_function()
|
||||
.map(Value::Function);
|
||||
}
|
||||
Err(err) => {
|
||||
return format!("cannot open '{}': {err}", file_path.display()).into_lua(lua);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Value::Nil)
|
||||
}
|
||||
|
||||
/// Tries to load a dynamic library
|
||||
#[cfg(unix)]
|
||||
fn dylib_loader(lua: &Lua, modname: StdString) -> Result<Value> {
|
||||
let package = {
|
||||
let key = lua.app_data_ref::<PackageKey>().unwrap();
|
||||
lua.registry_value::<Table>(&key.0)
|
||||
}?;
|
||||
let search_cpath = package.get::<_, StdString>("cpath").unwrap_or_default();
|
||||
|
||||
let find_symbol = |lib: &Library| unsafe {
|
||||
if let Ok(entry) = lib.get::<ffi::lua_CFunction>(format!("luaopen_{modname}\0").as_bytes())
|
||||
{
|
||||
return lua.create_c_function(*entry).map(Value::Function);
|
||||
}
|
||||
// Try all in one mode
|
||||
if let Ok(entry) = lib.get::<ffi::lua_CFunction>(
|
||||
format!("luaopen_{}\0", modname.replace('.', "_")).as_bytes(),
|
||||
) {
|
||||
return lua.create_c_function(*entry).map(Value::Function);
|
||||
}
|
||||
"cannot find module entrypoint".into_lua(lua)
|
||||
};
|
||||
|
||||
if let Some(file_path) = package_searchpath(&modname, &search_cpath, true) {
|
||||
let file_path = file_path.canonicalize()?;
|
||||
// Load the library and check for symbol
|
||||
unsafe {
|
||||
let mut loaded_dylibs = match lua.app_data_mut::<LoadedDylibs>() {
|
||||
Some(loaded_dylibs) => loaded_dylibs,
|
||||
None => return "dynamic libraries are disabled in safe mode".into_lua(lua),
|
||||
};
|
||||
// Check if it's already loaded
|
||||
if let Some(lib) = loaded_dylibs.get(&file_path) {
|
||||
return find_symbol(lib);
|
||||
}
|
||||
if let Ok(lib) = Library::new(&file_path) {
|
||||
// Check version
|
||||
let mod_version = lib.get::<*const u32>(b"MLUA_LUAU_ABI_VERSION");
|
||||
let mod_version = mod_version.map(|v| **v).unwrap_or_default();
|
||||
if mod_version != TARGET_MLUA_LUAU_ABI_VERSION {
|
||||
let err = format!("wrong module ABI version (expected {TARGET_MLUA_LUAU_ABI_VERSION}, got {mod_version})");
|
||||
return err.into_lua(lua);
|
||||
}
|
||||
let symbol = find_symbol(&lib);
|
||||
loaded_dylibs.insert(file_path, lib);
|
||||
return symbol;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Value::Nil)
|
||||
}
|
||||
+24
-29
@@ -2,11 +2,9 @@ use std::alloc::{self, Layout};
|
||||
use std::os::raw::c_void;
|
||||
use std::ptr;
|
||||
|
||||
#[cfg(feature = "luau")]
|
||||
use crate::lua::ExtraData;
|
||||
|
||||
pub(crate) static ALLOCATOR: ffi::lua_Alloc = allocator;
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Default)]
|
||||
pub(crate) struct MemoryState {
|
||||
used_memory: isize,
|
||||
@@ -20,6 +18,21 @@ pub(crate) struct MemoryState {
|
||||
}
|
||||
|
||||
impl MemoryState {
|
||||
#[inline]
|
||||
pub(crate) unsafe fn get(state: *mut ffi::lua_State) -> *mut Self {
|
||||
let mut mem_state = ptr::null_mut();
|
||||
#[cfg(feature = "luau")]
|
||||
{
|
||||
ffi::lua_getallocf(state, &mut mem_state);
|
||||
mlua_assert!(!mem_state.is_null(), "Luau state has no allocator userdata");
|
||||
}
|
||||
#[cfg(not(feature = "luau"))]
|
||||
if ffi::lua_getallocf(state, &mut mem_state) != ALLOCATOR {
|
||||
mem_state = ptr::null_mut();
|
||||
}
|
||||
mem_state as *mut MemoryState
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn used_memory(&self) -> usize {
|
||||
self.used_memory as usize
|
||||
@@ -37,36 +50,21 @@ impl MemoryState {
|
||||
prev_limit as usize
|
||||
}
|
||||
|
||||
// This function is used primarily for calling `lua_pushcfunction` in lua5.1/jit
|
||||
// This function is used primarily for calling `lua_pushcfunction` in lua5.1/jit/luau
|
||||
// to bypass the memory limit (if set).
|
||||
#[cfg(any(feature = "lua51", feature = "luajit"))]
|
||||
#[cfg(any(feature = "lua51", feature = "luajit", feature = "luau"))]
|
||||
#[inline]
|
||||
pub(crate) unsafe fn relax_limit_with(state: *mut ffi::lua_State, f: impl FnOnce()) {
|
||||
let mut mem_state: *mut c_void = ptr::null_mut();
|
||||
if ffi::lua_getallocf(state, &mut mem_state) == ALLOCATOR {
|
||||
(*(mem_state as *mut MemoryState)).ignore_limit = true;
|
||||
let mem_state = Self::get(state);
|
||||
if !mem_state.is_null() {
|
||||
(*mem_state).ignore_limit = true;
|
||||
f();
|
||||
(*(mem_state as *mut MemoryState)).ignore_limit = false;
|
||||
(*mem_state).ignore_limit = false;
|
||||
} else {
|
||||
f();
|
||||
}
|
||||
}
|
||||
|
||||
// Same as the above but for Luau
|
||||
// It does not have `lua_getallocf` function, so instead we use `lua_callbacks`
|
||||
#[cfg(feature = "luau")]
|
||||
#[inline]
|
||||
pub(crate) unsafe fn relax_limit_with(state: *mut ffi::lua_State, f: impl FnOnce()) {
|
||||
let extra = (*ffi::lua_callbacks(state)).userdata as *mut ExtraData;
|
||||
if extra.is_null() {
|
||||
return f();
|
||||
}
|
||||
let mem_state = (*extra).mem_state();
|
||||
(*mem_state.as_ptr()).ignore_limit = true;
|
||||
f();
|
||||
(*mem_state.as_ptr()).ignore_limit = false;
|
||||
}
|
||||
|
||||
// Does nothing apart from calling `f()`, we don't need to bypass any limits
|
||||
#[cfg(any(feature = "lua52", feature = "lua53", feature = "lua54"))]
|
||||
#[inline]
|
||||
@@ -76,12 +74,9 @@ impl MemoryState {
|
||||
|
||||
// Returns `true` if the memory limit was reached on the last memory operation
|
||||
#[cfg(feature = "luau")]
|
||||
#[inline]
|
||||
pub(crate) unsafe fn limit_reached(state: *mut ffi::lua_State) -> bool {
|
||||
let extra = (*ffi::lua_callbacks(state)).userdata as *mut ExtraData;
|
||||
if extra.is_null() {
|
||||
return false;
|
||||
}
|
||||
(*(*extra).mem_state().as_ptr()).limit_reached
|
||||
(*Self::get(state)).limit_reached
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+150
-28
@@ -1,9 +1,10 @@
|
||||
use std::iter::FromIterator;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use std::os::raw::c_int;
|
||||
use std::result::Result as StdResult;
|
||||
|
||||
use crate::error::Result;
|
||||
use crate::lua::Lua;
|
||||
use crate::util::check_stack;
|
||||
use crate::value::{FromLua, FromLuaMulti, IntoLua, IntoLuaMulti, MultiValue, Nil};
|
||||
|
||||
/// Result is convertible to `MultiValue` following the common Lua idiom of returning the result
|
||||
@@ -11,7 +12,7 @@ use crate::value::{FromLua, FromLuaMulti, IntoLua, IntoLuaMulti, MultiValue, Nil
|
||||
impl<'lua, T: IntoLua<'lua>, E: IntoLua<'lua>> IntoLuaMulti<'lua> for StdResult<T, E> {
|
||||
#[inline]
|
||||
fn into_lua_multi(self, lua: &'lua Lua) -> Result<MultiValue<'lua>> {
|
||||
let mut result = MultiValue::new_or_pooled(lua);
|
||||
let mut result = MultiValue::with_lua_and_capacity(lua, 2);
|
||||
match self {
|
||||
Ok(v) => result.push_front(v.into_lua(lua)?),
|
||||
Err(e) => {
|
||||
@@ -21,35 +22,101 @@ impl<'lua, T: IntoLua<'lua>, E: IntoLua<'lua>> IntoLuaMulti<'lua> for StdResult<
|
||||
}
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn push_into_stack_multi(self, lua: &'lua Lua) -> Result<c_int> {
|
||||
match self {
|
||||
Ok(v) => v.push_into_stack(lua).map(|_| 1),
|
||||
Err(e) => {
|
||||
let state = lua.state();
|
||||
check_stack(state, 3)?;
|
||||
ffi::lua_pushnil(state);
|
||||
e.push_into_stack(lua)?;
|
||||
Ok(2)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua, E: IntoLua<'lua>> IntoLuaMulti<'lua> for StdResult<(), E> {
|
||||
#[inline]
|
||||
fn into_lua_multi(self, lua: &'lua Lua) -> Result<MultiValue<'lua>> {
|
||||
match self {
|
||||
Ok(_) => return Ok(MultiValue::new()),
|
||||
Err(e) => {
|
||||
let mut result = MultiValue::with_lua_and_capacity(lua, 2);
|
||||
result.push_front(e.into_lua(lua)?);
|
||||
result.push_front(Nil);
|
||||
Ok(result)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn push_into_stack_multi(self, lua: &'lua Lua) -> Result<c_int> {
|
||||
match self {
|
||||
Ok(_) => Ok(0),
|
||||
Err(e) => {
|
||||
let state = lua.state();
|
||||
check_stack(state, 3)?;
|
||||
ffi::lua_pushnil(state);
|
||||
e.push_into_stack(lua)?;
|
||||
Ok(2)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua, T: IntoLua<'lua>> IntoLuaMulti<'lua> for T {
|
||||
#[inline]
|
||||
fn into_lua_multi(self, lua: &'lua Lua) -> Result<MultiValue<'lua>> {
|
||||
let mut v = MultiValue::new_or_pooled(lua);
|
||||
let mut v = MultiValue::with_lua_and_capacity(lua, 1);
|
||||
v.push_front(self.into_lua(lua)?);
|
||||
Ok(v)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn push_into_stack_multi(self, lua: &'lua Lua) -> Result<c_int> {
|
||||
self.push_into_stack(lua)?;
|
||||
Ok(1)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua, T: FromLua<'lua>> FromLuaMulti<'lua> for T {
|
||||
#[inline]
|
||||
fn from_lua_multi(mut values: MultiValue<'lua>, lua: &'lua Lua) -> Result<Self> {
|
||||
let res = T::from_lua(values.pop_front().unwrap_or(Nil), lua);
|
||||
MultiValue::return_to_pool(values, lua);
|
||||
res
|
||||
T::from_lua(values.pop_front().unwrap_or(Nil), lua)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn from_lua_multi_args(
|
||||
mut values: MultiValue<'lua>,
|
||||
fn from_lua_args(
|
||||
mut args: MultiValue<'lua>,
|
||||
i: usize,
|
||||
to: Option<&str>,
|
||||
lua: &'lua Lua,
|
||||
) -> Result<Self> {
|
||||
let res = T::from_lua_arg(values.pop_front().unwrap_or(Nil), i, to, lua);
|
||||
MultiValue::return_to_pool(values, lua);
|
||||
res
|
||||
T::from_lua_arg(args.pop_front().unwrap_or(Nil), i, to, lua)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn from_stack_multi(nvals: c_int, lua: &'lua Lua) -> Result<Self> {
|
||||
if nvals == 0 {
|
||||
return T::from_lua(Nil, lua);
|
||||
}
|
||||
T::from_stack(-nvals, lua)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn from_stack_args(
|
||||
nargs: c_int,
|
||||
i: usize,
|
||||
to: Option<&str>,
|
||||
lua: &'lua Lua,
|
||||
) -> Result<Self> {
|
||||
if nargs == 0 {
|
||||
return T::from_lua_arg(Nil, i, to, lua);
|
||||
}
|
||||
T::from_stack_arg(-nargs, i, to, lua)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -141,7 +208,7 @@ impl<T> DerefMut for Variadic<T> {
|
||||
impl<'lua, T: IntoLua<'lua>> IntoLuaMulti<'lua> for Variadic<T> {
|
||||
#[inline]
|
||||
fn into_lua_multi(self, lua: &'lua Lua) -> Result<MultiValue<'lua>> {
|
||||
let mut values = MultiValue::new_or_pooled(lua);
|
||||
let mut values = MultiValue::with_lua_and_capacity(lua, self.0.len());
|
||||
values.refill(self.0.into_iter().map(|e| e.into_lua(lua)))?;
|
||||
Ok(values)
|
||||
}
|
||||
@@ -150,13 +217,11 @@ impl<'lua, T: IntoLua<'lua>> IntoLuaMulti<'lua> for Variadic<T> {
|
||||
impl<'lua, T: FromLua<'lua>> FromLuaMulti<'lua> for Variadic<T> {
|
||||
#[inline]
|
||||
fn from_lua_multi(mut values: MultiValue<'lua>, lua: &'lua Lua) -> Result<Self> {
|
||||
let res = values
|
||||
values
|
||||
.drain_all()
|
||||
.map(|e| T::from_lua(e, lua))
|
||||
.collect::<Result<Vec<T>>>()
|
||||
.map(Variadic);
|
||||
MultiValue::return_to_pool(values, lua);
|
||||
res
|
||||
.map(Variadic)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -165,14 +230,26 @@ macro_rules! impl_tuple {
|
||||
impl<'lua> IntoLuaMulti<'lua> for () {
|
||||
#[inline]
|
||||
fn into_lua_multi(self, lua: &'lua Lua) -> Result<MultiValue<'lua>> {
|
||||
Ok(MultiValue::new_or_pooled(lua))
|
||||
Ok(MultiValue::with_lua_and_capacity(lua, 0))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn push_into_stack_multi(self, _lua: &'lua Lua) -> Result<c_int> {
|
||||
Ok(0)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> FromLuaMulti<'lua> for () {
|
||||
#[inline]
|
||||
fn from_lua_multi(values: MultiValue<'lua>, lua: &'lua Lua) -> Result<Self> {
|
||||
MultiValue::return_to_pool(values, lua);
|
||||
fn from_lua_multi(_values: MultiValue<'lua>, _lua: &'lua Lua) -> Result<Self> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn from_stack_multi(nvals: c_int, lua: &'lua Lua) -> Result<Self> {
|
||||
if nvals > 0 {
|
||||
ffi::lua_pop(lua.state(), nvals);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -183,8 +260,7 @@ macro_rules! impl_tuple {
|
||||
where $($name: IntoLua<'lua>,)*
|
||||
$last: IntoLuaMulti<'lua>
|
||||
{
|
||||
#[allow(unused_mut)]
|
||||
#[allow(non_snake_case)]
|
||||
#[allow(unused_mut, non_snake_case)]
|
||||
#[inline]
|
||||
fn into_lua_multi(self, lua: &'lua Lua) -> Result<MultiValue<'lua>> {
|
||||
let ($($name,)* $last,) = self;
|
||||
@@ -193,14 +269,30 @@ macro_rules! impl_tuple {
|
||||
push_reverse!(results, $($name.into_lua(lua)?,)*);
|
||||
Ok(results)
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
#[inline]
|
||||
unsafe fn push_into_stack_multi(self, lua: &'lua Lua) -> Result<c_int> {
|
||||
let ($($name,)* $last,) = self;
|
||||
let mut nresults = 0;
|
||||
$(
|
||||
_ = $name;
|
||||
nresults += 1;
|
||||
)*
|
||||
check_stack(lua.state(), nresults + 1)?;
|
||||
$(
|
||||
$name.push_into_stack(lua)?;
|
||||
)*
|
||||
nresults += $last.push_into_stack_multi(lua)?;
|
||||
Ok(nresults)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua, $($name,)* $last> FromLuaMulti<'lua> for ($($name,)* $last,)
|
||||
where $($name: FromLua<'lua>,)*
|
||||
$last: FromLuaMulti<'lua>
|
||||
{
|
||||
#[allow(unused_mut)]
|
||||
#[allow(non_snake_case)]
|
||||
#[allow(unused_mut, non_snake_case)]
|
||||
#[inline]
|
||||
fn from_lua_multi(mut values: MultiValue<'lua>, lua: &'lua Lua) -> Result<Self> {
|
||||
$(let $name = FromLua::from_lua(values.pop_front().unwrap_or(Nil), lua)?;)*
|
||||
@@ -208,15 +300,45 @@ macro_rules! impl_tuple {
|
||||
Ok(($($name,)* $last,))
|
||||
}
|
||||
|
||||
#[allow(unused_mut)]
|
||||
#[allow(non_snake_case)]
|
||||
#[allow(unused_mut, non_snake_case)]
|
||||
#[inline]
|
||||
fn from_lua_multi_args(mut values: MultiValue<'lua>, mut i: usize, to: Option<&str>, lua: &'lua Lua) -> Result<Self> {
|
||||
fn from_lua_args(mut args: MultiValue<'lua>, mut i: usize, to: Option<&str>, lua: &'lua Lua) -> Result<Self> {
|
||||
$(
|
||||
let $name = FromLua::from_lua_arg(values.pop_front().unwrap_or(Nil), i, to, lua)?;
|
||||
let $name = FromLua::from_lua_arg(args.pop_front().unwrap_or(Nil), i, to, lua)?;
|
||||
i += 1;
|
||||
)*
|
||||
let $last = FromLuaMulti::from_lua_multi_args(values, i, to, lua)?;
|
||||
let $last = FromLuaMulti::from_lua_args(args, i, to, lua)?;
|
||||
Ok(($($name,)* $last,))
|
||||
}
|
||||
|
||||
#[allow(unused_mut, non_snake_case)]
|
||||
#[inline]
|
||||
unsafe fn from_stack_multi(mut nvals: c_int, lua: &'lua Lua) -> Result<Self> {
|
||||
$(
|
||||
let $name = if nvals > 0 {
|
||||
nvals -= 1;
|
||||
FromLua::from_stack(-(nvals + 1), lua)
|
||||
} else {
|
||||
FromLua::from_lua(Nil, lua)
|
||||
}?;
|
||||
)*
|
||||
let $last = FromLuaMulti::from_stack_multi(nvals, lua)?;
|
||||
Ok(($($name,)* $last,))
|
||||
}
|
||||
|
||||
#[allow(unused_mut, non_snake_case)]
|
||||
#[inline]
|
||||
unsafe fn from_stack_args(mut nargs: c_int, mut i: usize, to: Option<&str>, lua: &'lua Lua) -> Result<Self> {
|
||||
$(
|
||||
let $name = if nargs > 0 {
|
||||
nargs -= 1;
|
||||
FromLua::from_stack_arg(-(nargs + 1), i, to, lua)
|
||||
} else {
|
||||
FromLua::from_lua_arg(Nil, i, to, lua)
|
||||
}?;
|
||||
i += 1;
|
||||
)*
|
||||
let $last = FromLuaMulti::from_stack_args(nargs, i, to, lua)?;
|
||||
Ok(($($name,)* $last,))
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -40,5 +40,5 @@ pub use crate::{
|
||||
#[doc(no_inline)]
|
||||
pub use crate::{
|
||||
OwnedAnyUserData as LuaOwnedAnyUserData, OwnedFunction as LuaOwnedFunction,
|
||||
OwnedString as LuaOwnedString, OwnedTable as LuaOwnedTable,
|
||||
OwnedString as LuaOwnedString, OwnedTable as LuaOwnedTable, OwnedThread as LuaOwnedThread,
|
||||
};
|
||||
|
||||
+253
-183
@@ -2,6 +2,7 @@ use std::any::Any;
|
||||
use std::cell::{Cell, RefCell};
|
||||
use std::marker::PhantomData;
|
||||
use std::mem;
|
||||
use std::os::raw::c_int;
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
use serde::Serialize;
|
||||
@@ -9,16 +10,16 @@ use serde::Serialize;
|
||||
use crate::error::{Error, Result};
|
||||
use crate::function::Function;
|
||||
use crate::lua::Lua;
|
||||
use crate::types::{Callback, CallbackUpvalue, LuaRef, MaybeSend};
|
||||
use crate::types::{Callback, CallbackUpvalue, LuaRef, MaybeSend, SubtypeId};
|
||||
use crate::userdata::{
|
||||
AnyUserData, MetaMethod, UserData, UserDataCell, UserDataFields, UserDataMethods,
|
||||
};
|
||||
use crate::userdata_impl::UserDataRegistry;
|
||||
use crate::util::{
|
||||
self, assert_stack, check_stack, get_userdata, init_userdata_metatable, push_table,
|
||||
rawset_field, take_userdata, StackGuard,
|
||||
self, assert_stack, check_stack, init_userdata_metatable, push_string, push_table,
|
||||
rawset_field, short_type_name, take_userdata, StackGuard,
|
||||
};
|
||||
use crate::value::{FromLua, FromLuaMulti, IntoLua, IntoLuaMulti, MultiValue, Value};
|
||||
use crate::value::{FromLua, FromLuaMulti, IntoLua, IntoLuaMulti};
|
||||
|
||||
#[cfg(feature = "lua54")]
|
||||
use crate::userdata::USER_VALUE_MAXSLOT;
|
||||
@@ -75,8 +76,9 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
|
||||
// to make sure callbacks can't capture handles with lifetime outside the scope, inside the
|
||||
// scope, and owned inside the callback itself.
|
||||
unsafe {
|
||||
self.create_callback(Box::new(move |lua, args| {
|
||||
func(lua, A::from_lua_multi(args, lua)?)?.into_lua_multi(lua)
|
||||
self.create_callback(Box::new(move |lua, nargs| {
|
||||
let args = A::from_stack_args(nargs, 1, None, lua)?;
|
||||
func(lua, args)?.push_into_stack_multi(lua)
|
||||
}))
|
||||
}
|
||||
}
|
||||
@@ -186,6 +188,23 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a Lua userdata object from a custom Rust type.
|
||||
///
|
||||
/// This is a version of [`Lua::create_any_userdata`] that creates a userdata which expires on
|
||||
/// scope drop and does not require that the userdata type be Send (but still requires that the
|
||||
/// UserData be 'static). See [`Lua::scope`] for more details.
|
||||
#[inline]
|
||||
pub fn create_any_userdata<T>(&self, data: T) -> Result<AnyUserData<'lua>>
|
||||
where
|
||||
T: 'static,
|
||||
{
|
||||
unsafe {
|
||||
let ud = self.lua.make_any_userdata(UserDataCell::new(data))?;
|
||||
self.seal_userdata::<T>(&ud)?;
|
||||
Ok(ud)
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a Lua userdata object from a reference to custom Rust type.
|
||||
///
|
||||
/// This is a version of [`Lua::create_any_userdata`] that creates a userdata which expires on
|
||||
@@ -293,9 +312,10 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
|
||||
// 'lua. This is safe though, because `UserData::add_methods` does not get to pick the 'lua
|
||||
// lifetime, so none of the static methods UserData types can add can possibly capture
|
||||
// parameters.
|
||||
fn wrap_method<'scope, 'lua, 'callback: 'scope, T: 'scope>(
|
||||
unsafe fn wrap_method<'scope, 'lua, 'callback: 'scope, T: 'scope>(
|
||||
scope: &Scope<'lua, 'scope>,
|
||||
ud_ptr: *const UserDataCell<T>,
|
||||
name: &str,
|
||||
method: NonStaticMethod<'callback, T>,
|
||||
) -> Result<Function<'lua>> {
|
||||
// On methods that actually receive the userdata, we fake a type check on the passed in
|
||||
@@ -305,61 +325,56 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
|
||||
// with a type mismatch, but here without this check would proceed as though you had
|
||||
// called the method on the original value (since we otherwise completely ignore the
|
||||
// first argument).
|
||||
let check_ud_type = move |lua: &Lua, value| -> Result<&UserDataCell<T>> {
|
||||
if let Some(Value::UserData(ud)) = value {
|
||||
let state = lua.state();
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(state);
|
||||
check_stack(state, 2)?;
|
||||
lua.push_userdata_ref(&ud.0)?;
|
||||
if get_userdata(state, -1) as *const _ == ud_ptr {
|
||||
return Ok(&*ud_ptr);
|
||||
}
|
||||
}
|
||||
};
|
||||
Err(Error::UserDataTypeMismatch)
|
||||
let func_name = format!("{}.{name}", short_type_name::<T>());
|
||||
let check_self_type = move |lua: &Lua, nargs: c_int| -> Result<&UserDataCell<T>> {
|
||||
let state = lua.state();
|
||||
if nargs > 0 && ffi::lua_touserdata(state, -nargs) as *const _ == ud_ptr {
|
||||
return Ok(&*ud_ptr);
|
||||
}
|
||||
Err(Error::bad_self_argument(
|
||||
&func_name,
|
||||
Error::UserDataTypeMismatch,
|
||||
))
|
||||
};
|
||||
|
||||
match method {
|
||||
NonStaticMethod::Method(method) => {
|
||||
let f = Box::new(move |lua, mut args: MultiValue<'callback>| {
|
||||
let data = check_ud_type(lua, args.pop_front())?;
|
||||
let f = Box::new(move |lua, nargs| {
|
||||
let data = check_self_type(lua, nargs)?;
|
||||
let data = data.try_borrow()?;
|
||||
method(lua, &*data, args)
|
||||
method(lua, &*data, nargs - 1)
|
||||
});
|
||||
unsafe { scope.create_callback(f) }
|
||||
scope.create_callback(f)
|
||||
}
|
||||
NonStaticMethod::MethodMut(method) => {
|
||||
let method = RefCell::new(method);
|
||||
let f = Box::new(move |lua, mut args: MultiValue<'callback>| {
|
||||
let data = check_ud_type(lua, args.pop_front())?;
|
||||
let f = Box::new(move |lua, nargs| {
|
||||
let data = check_self_type(lua, nargs)?;
|
||||
let mut method = method
|
||||
.try_borrow_mut()
|
||||
.map_err(|_| Error::RecursiveMutCallback)?;
|
||||
let mut data = data.try_borrow_mut()?;
|
||||
(*method)(lua, &mut *data, args)
|
||||
(*method)(lua, &mut *data, nargs - 1)
|
||||
});
|
||||
unsafe { scope.create_callback(f) }
|
||||
scope.create_callback(f)
|
||||
}
|
||||
NonStaticMethod::Function(function) => unsafe { scope.create_callback(function) },
|
||||
NonStaticMethod::Function(function) => scope.create_callback(function),
|
||||
NonStaticMethod::FunctionMut(function) => {
|
||||
let function = RefCell::new(function);
|
||||
let f = Box::new(move |lua, args| {
|
||||
(*function
|
||||
let f = Box::new(move |lua, nargs| {
|
||||
let mut func = function
|
||||
.try_borrow_mut()
|
||||
.map_err(|_| Error::RecursiveMutCallback)?)(
|
||||
lua, args
|
||||
)
|
||||
.map_err(|_| Error::RecursiveMutCallback)?;
|
||||
func(lua, nargs)
|
||||
});
|
||||
unsafe { scope.create_callback(f) }
|
||||
scope.create_callback(f)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut ud_fields = NonStaticUserDataFields::default();
|
||||
let mut ud_methods = NonStaticUserDataMethods::default();
|
||||
T::add_fields(&mut ud_fields);
|
||||
T::add_methods(&mut ud_methods);
|
||||
let mut registry = NonStaticUserDataRegistry::new();
|
||||
T::add_fields(&mut registry);
|
||||
T::add_methods(&mut registry);
|
||||
|
||||
let lua = self.lua;
|
||||
let state = lua.state();
|
||||
@@ -387,48 +402,83 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
|
||||
};
|
||||
|
||||
// Prepare metatable, add meta methods first and then meta fields
|
||||
let meta_methods_nrec = ud_methods.meta_methods.len() + ud_fields.meta_fields.len() + 1;
|
||||
let meta_methods_nrec = registry.meta_methods.len() + registry.meta_fields.len() + 1;
|
||||
push_table(state, 0, meta_methods_nrec, true)?;
|
||||
|
||||
for (k, m) in ud_methods.meta_methods {
|
||||
lua.push_value(Value::Function(wrap_method(self, ud_ptr, m)?))?;
|
||||
for (k, m) in registry.meta_methods {
|
||||
lua.push(wrap_method(self, ud_ptr, &k, m)?)?;
|
||||
rawset_field(state, -2, MetaMethod::validate(&k)?)?;
|
||||
}
|
||||
for (k, f) in ud_fields.meta_fields {
|
||||
lua.push_value(f(lua, MultiValue::new())?.pop_front().unwrap())?;
|
||||
let mut has_name = false;
|
||||
for (k, f) in registry.meta_fields {
|
||||
has_name = has_name || k == MetaMethod::Type;
|
||||
mlua_assert!(f(lua, 0)? == 1, "field function must return one value");
|
||||
rawset_field(state, -2, MetaMethod::validate(&k)?)?;
|
||||
}
|
||||
// Set `__name/__type` if not provided
|
||||
if !has_name {
|
||||
let type_name = short_type_name::<T>();
|
||||
push_string(state, type_name.as_bytes(), !lua.unlikely_memory_error())?;
|
||||
rawset_field(state, -2, MetaMethod::Type.name())?;
|
||||
}
|
||||
let metatable_index = ffi::lua_absindex(state, -1);
|
||||
|
||||
let fields_nrec = registry.fields.len();
|
||||
if fields_nrec > 0 {
|
||||
// If __index is a table then update it inplace
|
||||
let index_type = ffi::lua_getfield(state, metatable_index, cstr!("__index"));
|
||||
match index_type {
|
||||
ffi::LUA_TNIL | ffi::LUA_TTABLE => {
|
||||
if index_type == ffi::LUA_TNIL {
|
||||
// Create a new table
|
||||
ffi::lua_pop(state, 1);
|
||||
push_table(state, 0, fields_nrec, true)?;
|
||||
}
|
||||
for (k, f) in registry.fields {
|
||||
#[rustfmt::skip]
|
||||
let NonStaticMethod::Function(f) = f else { unreachable!() };
|
||||
mlua_assert!(f(lua, 0)? == 1, "field function must return one value");
|
||||
rawset_field(state, -2, &k)?;
|
||||
}
|
||||
rawset_field(state, metatable_index, "__index")?;
|
||||
}
|
||||
_ => {
|
||||
// Propagate fields to the field getters
|
||||
for (k, f) in registry.fields {
|
||||
registry.field_getters.push((k, f))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut field_getters_index = None;
|
||||
let field_getters_nrec = ud_fields.field_getters.len();
|
||||
let field_getters_nrec = registry.field_getters.len();
|
||||
if field_getters_nrec > 0 {
|
||||
push_table(state, 0, field_getters_nrec, true)?;
|
||||
for (k, m) in ud_fields.field_getters {
|
||||
lua.push_value(Value::Function(wrap_method(self, ud_ptr, m)?))?;
|
||||
for (k, m) in registry.field_getters {
|
||||
lua.push(wrap_method(self, ud_ptr, &k, m)?)?;
|
||||
rawset_field(state, -2, &k)?;
|
||||
}
|
||||
field_getters_index = Some(ffi::lua_absindex(state, -1));
|
||||
}
|
||||
|
||||
let mut field_setters_index = None;
|
||||
let field_setters_nrec = ud_fields.field_setters.len();
|
||||
let field_setters_nrec = registry.field_setters.len();
|
||||
if field_setters_nrec > 0 {
|
||||
push_table(state, 0, field_setters_nrec, true)?;
|
||||
for (k, m) in ud_fields.field_setters {
|
||||
lua.push_value(Value::Function(wrap_method(self, ud_ptr, m)?))?;
|
||||
for (k, m) in registry.field_setters {
|
||||
lua.push(wrap_method(self, ud_ptr, &k, m)?)?;
|
||||
rawset_field(state, -2, &k)?;
|
||||
}
|
||||
field_setters_index = Some(ffi::lua_absindex(state, -1));
|
||||
}
|
||||
|
||||
let mut methods_index = None;
|
||||
let methods_nrec = ud_methods.methods.len();
|
||||
let methods_nrec = registry.methods.len();
|
||||
if methods_nrec > 0 {
|
||||
// Create table used for methods lookup
|
||||
push_table(state, 0, methods_nrec, true)?;
|
||||
for (k, m) in ud_methods.methods {
|
||||
lua.push_value(Value::Function(wrap_method(self, ud_ptr, m)?))?;
|
||||
for (k, m) in registry.methods {
|
||||
lua.push(wrap_method(self, ud_ptr, &k, m)?)?;
|
||||
rawset_field(state, -2, &k)?;
|
||||
}
|
||||
methods_index = Some(ffi::lua_absindex(state, -1));
|
||||
@@ -461,7 +511,7 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
|
||||
#[cfg(not(feature = "luau"))]
|
||||
std::ptr::write(ud_ptr as _, UserDataCell::new(data));
|
||||
ffi::lua_setmetatable(state, -2);
|
||||
let ud = AnyUserData(lua.pop_ref());
|
||||
let ud = AnyUserData(lua.pop_ref(), SubtypeId::None);
|
||||
lua.register_raw_userdata_metatable(mt_ptr, None);
|
||||
|
||||
#[cfg(any(feature = "lua51", feature = "luajit"))]
|
||||
@@ -572,35 +622,144 @@ impl<'lua, 'scope> Drop for Scope<'lua, 'scope> {
|
||||
|
||||
#[allow(clippy::type_complexity)]
|
||||
enum NonStaticMethod<'lua, T> {
|
||||
Method(Box<dyn Fn(&'lua Lua, &T, MultiValue<'lua>) -> Result<MultiValue<'lua>>>),
|
||||
MethodMut(Box<dyn FnMut(&'lua Lua, &mut T, MultiValue<'lua>) -> Result<MultiValue<'lua>>>),
|
||||
Function(Box<dyn Fn(&'lua Lua, MultiValue<'lua>) -> Result<MultiValue<'lua>>>),
|
||||
FunctionMut(Box<dyn FnMut(&'lua Lua, MultiValue<'lua>) -> Result<MultiValue<'lua>>>),
|
||||
Method(Box<dyn Fn(&'lua Lua, &T, c_int) -> Result<c_int>>),
|
||||
MethodMut(Box<dyn FnMut(&'lua Lua, &mut T, c_int) -> Result<c_int>>),
|
||||
Function(Box<dyn Fn(&'lua Lua, c_int) -> Result<c_int>>),
|
||||
FunctionMut(Box<dyn FnMut(&'lua Lua, c_int) -> Result<c_int>>),
|
||||
}
|
||||
|
||||
struct NonStaticUserDataMethods<'lua, T: UserData> {
|
||||
struct NonStaticUserDataRegistry<'lua, T> {
|
||||
// Fields
|
||||
fields: Vec<(String, NonStaticMethod<'lua, T>)>,
|
||||
field_getters: Vec<(String, NonStaticMethod<'lua, T>)>,
|
||||
field_setters: Vec<(String, NonStaticMethod<'lua, T>)>,
|
||||
meta_fields: Vec<(String, Callback<'lua, 'static>)>,
|
||||
|
||||
// Methods
|
||||
methods: Vec<(String, NonStaticMethod<'lua, T>)>,
|
||||
meta_methods: Vec<(String, NonStaticMethod<'lua, T>)>,
|
||||
}
|
||||
|
||||
impl<'lua, T: UserData> Default for NonStaticUserDataMethods<'lua, T> {
|
||||
fn default() -> NonStaticUserDataMethods<'lua, T> {
|
||||
NonStaticUserDataMethods {
|
||||
impl<'lua, T> NonStaticUserDataRegistry<'lua, T> {
|
||||
const fn new() -> NonStaticUserDataRegistry<'lua, T> {
|
||||
NonStaticUserDataRegistry {
|
||||
fields: Vec::new(),
|
||||
field_getters: Vec::new(),
|
||||
field_setters: Vec::new(),
|
||||
meta_fields: Vec::new(),
|
||||
methods: Vec::new(),
|
||||
meta_methods: Vec::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua, T: UserData> UserDataMethods<'lua, T> for NonStaticUserDataMethods<'lua, T> {
|
||||
impl<'lua, T> UserDataFields<'lua, T> for NonStaticUserDataRegistry<'lua, T> {
|
||||
fn add_field<V>(&mut self, name: impl AsRef<str>, value: V)
|
||||
where
|
||||
V: IntoLua<'lua> + Clone + 'static,
|
||||
{
|
||||
let name = name.as_ref().to_string();
|
||||
self.fields.push((
|
||||
name,
|
||||
NonStaticMethod::Function(Box::new(move |lua, _| unsafe {
|
||||
value.clone().push_into_stack_multi(lua)
|
||||
})),
|
||||
));
|
||||
}
|
||||
|
||||
fn add_field_method_get<M, R>(&mut self, name: impl AsRef<str>, method: M)
|
||||
where
|
||||
M: Fn(&'lua Lua, &T) -> Result<R> + MaybeSend + 'static,
|
||||
R: IntoLua<'lua>,
|
||||
{
|
||||
let method = NonStaticMethod::Method(Box::new(move |lua, ud, _| unsafe {
|
||||
method(lua, ud)?.push_into_stack_multi(lua)
|
||||
}));
|
||||
self.field_getters.push((name.as_ref().into(), method));
|
||||
}
|
||||
|
||||
fn add_field_method_set<M, A>(&mut self, name: impl AsRef<str>, mut method: M)
|
||||
where
|
||||
M: FnMut(&'lua Lua, &mut T, A) -> Result<()> + MaybeSend + 'static,
|
||||
A: FromLua<'lua>,
|
||||
{
|
||||
let func_name = format!("{}.{}", short_type_name::<T>(), name.as_ref());
|
||||
let method = NonStaticMethod::MethodMut(Box::new(move |lua, ud, nargs| unsafe {
|
||||
let val = A::from_stack_args(nargs, 2, Some(&func_name), lua)?;
|
||||
method(lua, ud, val)?.push_into_stack_multi(lua)
|
||||
}));
|
||||
self.field_setters.push((name.as_ref().into(), method));
|
||||
}
|
||||
|
||||
fn add_field_function_get<F, R>(&mut self, name: impl AsRef<str>, function: F)
|
||||
where
|
||||
F: Fn(&'lua Lua, AnyUserData<'lua>) -> Result<R> + MaybeSend + 'static,
|
||||
R: IntoLua<'lua>,
|
||||
{
|
||||
let func_name = format!("{}.{}", short_type_name::<T>(), name.as_ref());
|
||||
let func = NonStaticMethod::Function(Box::new(move |lua, nargs| unsafe {
|
||||
let ud = AnyUserData::from_stack_args(nargs, 1, Some(&func_name), lua)?;
|
||||
function(lua, ud)?.push_into_stack_multi(lua)
|
||||
}));
|
||||
self.field_getters.push((name.as_ref().into(), func));
|
||||
}
|
||||
|
||||
fn add_field_function_set<F, A>(&mut self, name: impl AsRef<str>, mut function: F)
|
||||
where
|
||||
F: FnMut(&'lua Lua, AnyUserData<'lua>, A) -> Result<()> + MaybeSend + 'static,
|
||||
A: FromLua<'lua>,
|
||||
{
|
||||
let func_name = format!("{}.{}", short_type_name::<T>(), name.as_ref());
|
||||
let func = NonStaticMethod::FunctionMut(Box::new(move |lua, nargs| unsafe {
|
||||
let (ud, val) = <_>::from_stack_args(nargs, 1, Some(&func_name), lua)?;
|
||||
function(lua, ud, val)?.push_into_stack_multi(lua)
|
||||
}));
|
||||
self.field_setters.push((name.as_ref().into(), func));
|
||||
}
|
||||
|
||||
fn add_meta_field<V>(&mut self, name: impl AsRef<str>, value: V)
|
||||
where
|
||||
V: IntoLua<'lua> + Clone + 'static,
|
||||
{
|
||||
let name = name.as_ref().to_string();
|
||||
let name2 = name.clone();
|
||||
self.meta_fields.push((
|
||||
name,
|
||||
Box::new(move |lua, _| unsafe {
|
||||
UserDataRegistry::<()>::check_meta_field(lua, &name2, value.clone())?
|
||||
.push_into_stack_multi(lua)
|
||||
}),
|
||||
));
|
||||
}
|
||||
|
||||
fn add_meta_field_with<F, R>(&mut self, name: impl AsRef<str>, f: F)
|
||||
where
|
||||
F: Fn(&'lua Lua) -> Result<R> + MaybeSend + 'static,
|
||||
R: IntoLua<'lua>,
|
||||
{
|
||||
let name = name.as_ref().to_string();
|
||||
let name2 = name.clone();
|
||||
self.meta_fields.push((
|
||||
name,
|
||||
Box::new(move |lua, _| unsafe {
|
||||
UserDataRegistry::<()>::check_meta_field(lua, &name2, f(lua)?)?
|
||||
.push_into_stack_multi(lua)
|
||||
}),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua, T> UserDataMethods<'lua, T> for NonStaticUserDataRegistry<'lua, T> {
|
||||
fn add_method<M, A, R>(&mut self, name: impl AsRef<str>, method: M)
|
||||
where
|
||||
M: Fn(&'lua Lua, &T, A) -> Result<R> + MaybeSend + 'static,
|
||||
A: FromLuaMulti<'lua>,
|
||||
R: IntoLuaMulti<'lua>,
|
||||
{
|
||||
let method = NonStaticMethod::Method(Box::new(move |lua, ud, args| {
|
||||
method(lua, ud, A::from_lua_multi(args, lua)?)?.into_lua_multi(lua)
|
||||
let func_name = format!("{}.{}", short_type_name::<T>(), name.as_ref());
|
||||
let method = NonStaticMethod::Method(Box::new(move |lua, ud, nargs| unsafe {
|
||||
let args = A::from_stack_args(nargs, 2, Some(&func_name), lua)?;
|
||||
method(lua, ud, args)?.push_into_stack_multi(lua)
|
||||
}));
|
||||
self.methods.push((name.as_ref().into(), method));
|
||||
}
|
||||
@@ -611,8 +770,10 @@ impl<'lua, T: UserData> UserDataMethods<'lua, T> for NonStaticUserDataMethods<'l
|
||||
A: FromLuaMulti<'lua>,
|
||||
R: IntoLuaMulti<'lua>,
|
||||
{
|
||||
let method = NonStaticMethod::MethodMut(Box::new(move |lua, ud, args| {
|
||||
method(lua, ud, A::from_lua_multi(args, lua)?)?.into_lua_multi(lua)
|
||||
let func_name = format!("{}.{}", short_type_name::<T>(), name.as_ref());
|
||||
let method = NonStaticMethod::MethodMut(Box::new(move |lua, ud, nargs| unsafe {
|
||||
let args = A::from_stack_args(nargs, 2, Some(&func_name), lua)?;
|
||||
method(lua, ud, args)?.push_into_stack_multi(lua)
|
||||
}));
|
||||
self.methods.push((name.as_ref().into(), method));
|
||||
}
|
||||
@@ -653,8 +814,10 @@ impl<'lua, T: UserData> UserDataMethods<'lua, T> for NonStaticUserDataMethods<'l
|
||||
A: FromLuaMulti<'lua>,
|
||||
R: IntoLuaMulti<'lua>,
|
||||
{
|
||||
let func = NonStaticMethod::Function(Box::new(move |lua, args| {
|
||||
function(lua, A::from_lua_multi(args, lua)?)?.into_lua_multi(lua)
|
||||
let func_name = format!("{}.{}", short_type_name::<T>(), name.as_ref());
|
||||
let func = NonStaticMethod::Function(Box::new(move |lua, nargs| unsafe {
|
||||
let args = A::from_stack_args(nargs, 1, Some(&func_name), lua)?;
|
||||
function(lua, args)?.push_into_stack_multi(lua)
|
||||
}));
|
||||
self.methods.push((name.as_ref().into(), func));
|
||||
}
|
||||
@@ -665,8 +828,10 @@ impl<'lua, T: UserData> UserDataMethods<'lua, T> for NonStaticUserDataMethods<'l
|
||||
A: FromLuaMulti<'lua>,
|
||||
R: IntoLuaMulti<'lua>,
|
||||
{
|
||||
let func = NonStaticMethod::FunctionMut(Box::new(move |lua, args| {
|
||||
function(lua, A::from_lua_multi(args, lua)?)?.into_lua_multi(lua)
|
||||
let func_name = format!("{}.{}", short_type_name::<T>(), name.as_ref());
|
||||
let func = NonStaticMethod::FunctionMut(Box::new(move |lua, nargs| unsafe {
|
||||
let args = A::from_stack_args(nargs, 1, Some(&func_name), lua)?;
|
||||
function(lua, args)?.push_into_stack_multi(lua)
|
||||
}));
|
||||
self.methods.push((name.as_ref().into(), func));
|
||||
}
|
||||
@@ -690,8 +855,10 @@ impl<'lua, T: UserData> UserDataMethods<'lua, T> for NonStaticUserDataMethods<'l
|
||||
A: FromLuaMulti<'lua>,
|
||||
R: IntoLuaMulti<'lua>,
|
||||
{
|
||||
let method = NonStaticMethod::Method(Box::new(move |lua, ud, args| {
|
||||
method(lua, ud, A::from_lua_multi(args, lua)?)?.into_lua_multi(lua)
|
||||
let func_name = format!("{}.{}", short_type_name::<T>(), name.as_ref());
|
||||
let method = NonStaticMethod::Method(Box::new(move |lua, ud, nargs| unsafe {
|
||||
let args = A::from_stack_args(nargs, 2, Some(&func_name), lua)?;
|
||||
method(lua, ud, args)?.push_into_stack_multi(lua)
|
||||
}));
|
||||
self.meta_methods.push((name.as_ref().into(), method));
|
||||
}
|
||||
@@ -702,8 +869,10 @@ impl<'lua, T: UserData> UserDataMethods<'lua, T> for NonStaticUserDataMethods<'l
|
||||
A: FromLuaMulti<'lua>,
|
||||
R: IntoLuaMulti<'lua>,
|
||||
{
|
||||
let method = NonStaticMethod::MethodMut(Box::new(move |lua, ud, args| {
|
||||
method(lua, ud, A::from_lua_multi(args, lua)?)?.into_lua_multi(lua)
|
||||
let func_name = format!("{}.{}", short_type_name::<T>(), name.as_ref());
|
||||
let method = NonStaticMethod::MethodMut(Box::new(move |lua, ud, nargs| unsafe {
|
||||
let args = A::from_stack_args(nargs, 2, Some(&func_name), lua)?;
|
||||
method(lua, ud, args)?.push_into_stack_multi(lua)
|
||||
}));
|
||||
self.meta_methods.push((name.as_ref().into(), method));
|
||||
}
|
||||
@@ -744,8 +913,10 @@ impl<'lua, T: UserData> UserDataMethods<'lua, T> for NonStaticUserDataMethods<'l
|
||||
A: FromLuaMulti<'lua>,
|
||||
R: IntoLuaMulti<'lua>,
|
||||
{
|
||||
let func = NonStaticMethod::Function(Box::new(move |lua, args| {
|
||||
function(lua, A::from_lua_multi(args, lua)?)?.into_lua_multi(lua)
|
||||
let func_name = format!("{}.{}", short_type_name::<T>(), name.as_ref());
|
||||
let func = NonStaticMethod::Function(Box::new(move |lua, nargs| unsafe {
|
||||
let args = A::from_stack_args(nargs, 1, Some(&func_name), lua)?;
|
||||
function(lua, args)?.push_into_stack_multi(lua)
|
||||
}));
|
||||
self.meta_methods.push((name.as_ref().into(), func));
|
||||
}
|
||||
@@ -756,8 +927,10 @@ impl<'lua, T: UserData> UserDataMethods<'lua, T> for NonStaticUserDataMethods<'l
|
||||
A: FromLuaMulti<'lua>,
|
||||
R: IntoLuaMulti<'lua>,
|
||||
{
|
||||
let func = NonStaticMethod::FunctionMut(Box::new(move |lua, args| {
|
||||
function(lua, A::from_lua_multi(args, lua)?)?.into_lua_multi(lua)
|
||||
let func_name = format!("{}.{}", short_type_name::<T>(), name.as_ref());
|
||||
let func = NonStaticMethod::FunctionMut(Box::new(move |lua, nargs| unsafe {
|
||||
let args = A::from_stack_args(nargs, 1, Some(&func_name), lua)?;
|
||||
function(lua, args)?.push_into_stack_multi(lua)
|
||||
}));
|
||||
self.meta_methods.push((name.as_ref().into(), func));
|
||||
}
|
||||
@@ -775,106 +948,3 @@ impl<'lua, T: UserData> UserDataMethods<'lua, T> for NonStaticUserDataMethods<'l
|
||||
panic!("asynchronous meta functions are not supported for non-static userdata")
|
||||
}
|
||||
}
|
||||
|
||||
struct NonStaticUserDataFields<'lua, T: UserData> {
|
||||
fields: Vec<(String, Callback<'lua, 'static>)>,
|
||||
field_getters: Vec<(String, NonStaticMethod<'lua, T>)>,
|
||||
field_setters: Vec<(String, NonStaticMethod<'lua, T>)>,
|
||||
meta_fields: Vec<(String, Callback<'lua, 'static>)>,
|
||||
}
|
||||
|
||||
impl<'lua, T: UserData> Default for NonStaticUserDataFields<'lua, T> {
|
||||
fn default() -> NonStaticUserDataFields<'lua, T> {
|
||||
NonStaticUserDataFields {
|
||||
fields: Vec::new(),
|
||||
field_getters: Vec::new(),
|
||||
field_setters: Vec::new(),
|
||||
meta_fields: Vec::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua, T: UserData> UserDataFields<'lua, T> for NonStaticUserDataFields<'lua, T> {
|
||||
fn add_field<V>(&mut self, name: impl AsRef<str>, value: V)
|
||||
where
|
||||
V: IntoLua<'lua> + Clone + 'static,
|
||||
{
|
||||
let name = name.as_ref().to_string();
|
||||
self.fields.push((
|
||||
name,
|
||||
Box::new(move |lua, _| value.clone().into_lua_multi(lua)),
|
||||
));
|
||||
}
|
||||
|
||||
fn add_field_method_get<M, R>(&mut self, name: impl AsRef<str>, method: M)
|
||||
where
|
||||
M: Fn(&'lua Lua, &T) -> Result<R> + MaybeSend + 'static,
|
||||
R: IntoLua<'lua>,
|
||||
{
|
||||
let method = NonStaticMethod::Method(Box::new(move |lua, ud, _| {
|
||||
method(lua, ud)?.into_lua_multi(lua)
|
||||
}));
|
||||
self.field_getters.push((name.as_ref().into(), method));
|
||||
}
|
||||
|
||||
fn add_field_method_set<M, A>(&mut self, name: impl AsRef<str>, mut method: M)
|
||||
where
|
||||
M: FnMut(&'lua Lua, &mut T, A) -> Result<()> + MaybeSend + 'static,
|
||||
A: FromLua<'lua>,
|
||||
{
|
||||
let method = NonStaticMethod::MethodMut(Box::new(move |lua, ud, args| {
|
||||
method(lua, ud, A::from_lua_multi(args, lua)?)?.into_lua_multi(lua)
|
||||
}));
|
||||
self.field_setters.push((name.as_ref().into(), method));
|
||||
}
|
||||
|
||||
fn add_field_function_get<F, R>(&mut self, name: impl AsRef<str>, function: F)
|
||||
where
|
||||
F: Fn(&'lua Lua, AnyUserData<'lua>) -> Result<R> + MaybeSend + 'static,
|
||||
R: IntoLua<'lua>,
|
||||
{
|
||||
let func = NonStaticMethod::Function(Box::new(move |lua, args| {
|
||||
function(lua, AnyUserData::from_lua_multi(args, lua)?)?.into_lua_multi(lua)
|
||||
}));
|
||||
self.field_getters.push((name.as_ref().into(), func));
|
||||
}
|
||||
|
||||
fn add_field_function_set<F, A>(&mut self, name: impl AsRef<str>, mut function: F)
|
||||
where
|
||||
F: FnMut(&'lua Lua, AnyUserData<'lua>, A) -> Result<()> + MaybeSend + 'static,
|
||||
A: FromLua<'lua>,
|
||||
{
|
||||
let func = NonStaticMethod::FunctionMut(Box::new(move |lua, args| {
|
||||
let (ud, val) = <_>::from_lua_multi(args, lua)?;
|
||||
function(lua, ud, val)?.into_lua_multi(lua)
|
||||
}));
|
||||
self.field_setters.push((name.as_ref().into(), func));
|
||||
}
|
||||
|
||||
fn add_meta_field<V>(&mut self, name: impl AsRef<str>, value: V)
|
||||
where
|
||||
V: IntoLua<'lua> + Clone + 'static,
|
||||
{
|
||||
let name = name.as_ref().to_string();
|
||||
let name2 = name.clone();
|
||||
self.meta_fields.push((
|
||||
name,
|
||||
Box::new(move |lua, _| {
|
||||
UserDataRegistry::<()>::check_meta_field(lua, &name2, value.clone())
|
||||
}),
|
||||
));
|
||||
}
|
||||
|
||||
fn add_meta_field_with<F, R>(&mut self, name: impl AsRef<str>, f: F)
|
||||
where
|
||||
F: Fn(&'lua Lua) -> Result<R> + MaybeSend + 'static,
|
||||
R: IntoLua<'lua>,
|
||||
{
|
||||
let name = name.as_ref().to_string();
|
||||
let name2 = name.clone();
|
||||
self.meta_fields.push((
|
||||
name,
|
||||
Box::new(move |lua, _| UserDataRegistry::<()>::check_meta_field(lua, &name2, f(lua)?)),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
+90
-23
@@ -1,7 +1,7 @@
|
||||
use std::cell::RefCell;
|
||||
use std::convert::TryInto;
|
||||
use std::os::raw::c_void;
|
||||
use std::rc::Rc;
|
||||
use std::result::Result as StdResult;
|
||||
use std::string::String as StdString;
|
||||
|
||||
use rustc_hash::FxHashSet;
|
||||
@@ -24,14 +24,14 @@ pub struct Deserializer<'lua> {
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
#[non_exhaustive]
|
||||
pub struct Options {
|
||||
/// If true, an attempt to serialize types such as [`Thread`], [`UserData`], [`LightUserData`]
|
||||
/// If true, an attempt to serialize types such as [`Function`], [`Thread`], [`LightUserData`]
|
||||
/// and [`Error`] will cause an error.
|
||||
/// Otherwise these types skipped when iterating or serialized as unit type.
|
||||
///
|
||||
/// Default: **true**
|
||||
///
|
||||
/// [`Function`]: crate::Function
|
||||
/// [`Thread`]: crate::Thread
|
||||
/// [`UserData`]: crate::UserData
|
||||
/// [`LightUserData`]: crate::LightUserData
|
||||
/// [`Error`]: crate::Error
|
||||
pub deny_unsupported_types: bool,
|
||||
@@ -42,6 +42,11 @@ pub struct Options {
|
||||
///
|
||||
/// Default: **true**
|
||||
pub deny_recursive_tables: bool,
|
||||
|
||||
/// If true, keys in tables will be iterated in sorted order.
|
||||
///
|
||||
/// Default: **false**
|
||||
pub sort_keys: bool,
|
||||
}
|
||||
|
||||
impl Default for Options {
|
||||
@@ -56,6 +61,7 @@ impl Options {
|
||||
Options {
|
||||
deny_unsupported_types: true,
|
||||
deny_recursive_tables: true,
|
||||
sort_keys: false,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -76,6 +82,15 @@ impl Options {
|
||||
self.deny_recursive_tables = enabled;
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets [`sort_keys`] option.
|
||||
///
|
||||
/// [`sort_keys`]: #structfield.sort_keys
|
||||
#[must_use]
|
||||
pub const fn sort_keys(mut self, enabled: bool) -> Self {
|
||||
self.sort_keys = enabled;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> Deserializer<'lua> {
|
||||
@@ -118,9 +133,7 @@ impl<'lua, 'de> serde::Deserializer<'de> for Deserializer<'lua> {
|
||||
Value::Nil => visitor.visit_unit(),
|
||||
Value::Boolean(b) => visitor.visit_bool(b),
|
||||
#[allow(clippy::useless_conversion)]
|
||||
Value::Integer(i) => {
|
||||
visitor.visit_i64(i.try_into().expect("cannot convert lua_Integer to i64"))
|
||||
}
|
||||
Value::Integer(i) => visitor.visit_i64(i.into()),
|
||||
#[allow(clippy::useless_conversion)]
|
||||
Value::Number(n) => visitor.visit_f64(n.into()),
|
||||
#[cfg(feature = "luau")]
|
||||
@@ -135,16 +148,22 @@ impl<'lua, 'de> serde::Deserializer<'de> for Deserializer<'lua> {
|
||||
Value::UserData(ud) if ud.is_serializable() => {
|
||||
serde_userdata(ud, |value| value.deserialize_any(visitor))
|
||||
}
|
||||
#[cfg(feature = "luau")]
|
||||
Value::UserData(ud) if ud.1 == crate::types::SubtypeId::Buffer => unsafe {
|
||||
let mut size = 0usize;
|
||||
let buf = ffi::lua_tobuffer(ud.0.lua.ref_thread(), ud.0.index, &mut size);
|
||||
mlua_assert!(!buf.is_null(), "invalid Luau buffer");
|
||||
let buf = std::slice::from_raw_parts(buf as *const u8, size);
|
||||
visitor.visit_bytes(buf)
|
||||
},
|
||||
Value::Function(_)
|
||||
| Value::Thread(_)
|
||||
| Value::UserData(_)
|
||||
| Value::LightUserData(_)
|
||||
| Value::Error(_) => {
|
||||
if self.options.deny_unsupported_types {
|
||||
Err(de::Error::custom(format!(
|
||||
"unsupported value type `{}`",
|
||||
self.value.type_name()
|
||||
)))
|
||||
let msg = format!("unsupported value type `{}`", self.value.type_name());
|
||||
Err(de::Error::custom(msg))
|
||||
} else {
|
||||
visitor.visit_unit()
|
||||
}
|
||||
@@ -195,7 +214,9 @@ impl<'lua, 'de> serde::Deserializer<'de> for Deserializer<'lua> {
|
||||
&"map with a single key",
|
||||
));
|
||||
}
|
||||
if check_value_if_skip(&value, self.options, &self.visited)? {
|
||||
let skip = check_value_for_skip(&value, self.options, &self.visited)
|
||||
.map_err(|err| Error::DeserializeError(err.to_string()))?;
|
||||
if skip {
|
||||
return Err(de::Error::custom("bad enum value"));
|
||||
}
|
||||
|
||||
@@ -235,7 +256,7 @@ impl<'lua, 'de> serde::Deserializer<'de> for Deserializer<'lua> {
|
||||
Value::Table(t) => {
|
||||
let _guard = RecursionGuard::new(&t, &self.visited);
|
||||
|
||||
let len = t.raw_len() as usize;
|
||||
let len = t.raw_len();
|
||||
let mut deserializer = SeqDeserializer {
|
||||
seq: t.sequence_values(),
|
||||
options: self.options,
|
||||
@@ -292,7 +313,7 @@ impl<'lua, 'de> serde::Deserializer<'de> for Deserializer<'lua> {
|
||||
let _guard = RecursionGuard::new(&t, &self.visited);
|
||||
|
||||
let mut deserializer = MapDeserializer {
|
||||
pairs: t.pairs(),
|
||||
pairs: MapPairs::new(t, self.options.sort_keys)?,
|
||||
value: None,
|
||||
options: self.options,
|
||||
visited: self.visited,
|
||||
@@ -390,7 +411,9 @@ impl<'lua, 'de> de::SeqAccess<'de> for SeqDeserializer<'lua> {
|
||||
match self.seq.next() {
|
||||
Some(value) => {
|
||||
let value = value?;
|
||||
if check_value_if_skip(&value, self.options, &self.visited)? {
|
||||
let skip = check_value_for_skip(&value, self.options, &self.visited)
|
||||
.map_err(|err| Error::DeserializeError(err.to_string()))?;
|
||||
if skip {
|
||||
continue;
|
||||
}
|
||||
let visited = Rc::clone(&self.visited);
|
||||
@@ -443,8 +466,50 @@ impl<'de> de::SeqAccess<'de> for VecDeserializer {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) enum MapPairs<'lua> {
|
||||
Iter(TablePairs<'lua, Value<'lua>, Value<'lua>>),
|
||||
Vec(Vec<(Value<'lua>, Value<'lua>)>),
|
||||
}
|
||||
|
||||
impl<'lua> MapPairs<'lua> {
|
||||
pub(crate) fn new(t: Table<'lua>, sort_keys: bool) -> Result<Self> {
|
||||
if sort_keys {
|
||||
let mut pairs = t.pairs::<Value, Value>().collect::<Result<Vec<_>>>()?;
|
||||
pairs.sort_by(|(a, _), (b, _)| b.cmp(a)); // reverse order as we pop values from the end
|
||||
Ok(MapPairs::Vec(pairs))
|
||||
} else {
|
||||
Ok(MapPairs::Iter(t.pairs::<Value, Value>()))
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn count(self) -> usize {
|
||||
match self {
|
||||
MapPairs::Iter(iter) => iter.count(),
|
||||
MapPairs::Vec(vec) => vec.len(),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
match self {
|
||||
MapPairs::Iter(iter) => iter.size_hint(),
|
||||
MapPairs::Vec(vec) => (vec.len(), Some(vec.len())),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> Iterator for MapPairs<'lua> {
|
||||
type Item = Result<(Value<'lua>, Value<'lua>)>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
match self {
|
||||
MapPairs::Iter(iter) => iter.next(),
|
||||
MapPairs::Vec(vec) => vec.pop().map(Ok),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct MapDeserializer<'lua> {
|
||||
pairs: TablePairs<'lua, Value<'lua>, Value<'lua>>,
|
||||
pairs: MapPairs<'lua>,
|
||||
value: Option<Value<'lua>>,
|
||||
options: Options,
|
||||
visited: Rc<RefCell<FxHashSet<*const c_void>>>,
|
||||
@@ -462,9 +527,11 @@ impl<'lua, 'de> de::MapAccess<'de> for MapDeserializer<'lua> {
|
||||
match self.pairs.next() {
|
||||
Some(item) => {
|
||||
let (key, value) = item?;
|
||||
if check_value_if_skip(&key, self.options, &self.visited)?
|
||||
|| check_value_if_skip(&value, self.options, &self.visited)?
|
||||
{
|
||||
let skip_key = check_value_for_skip(&key, self.options, &self.visited)
|
||||
.map_err(|err| Error::DeserializeError(err.to_string()))?;
|
||||
let skip_value = check_value_for_skip(&value, self.options, &self.visited)
|
||||
.map_err(|err| Error::DeserializeError(err.to_string()))?;
|
||||
if skip_key || skip_value {
|
||||
continue;
|
||||
}
|
||||
self.processed += 1;
|
||||
@@ -593,14 +660,14 @@ impl<'lua, 'de> de::VariantAccess<'de> for VariantDeserializer<'lua> {
|
||||
|
||||
// Adds `ptr` to the `visited` map and removes on drop
|
||||
// Used to track recursive tables but allow to traverse same tables multiple times
|
||||
struct RecursionGuard {
|
||||
pub(crate) struct RecursionGuard {
|
||||
ptr: *const c_void,
|
||||
visited: Rc<RefCell<FxHashSet<*const c_void>>>,
|
||||
}
|
||||
|
||||
impl RecursionGuard {
|
||||
#[inline]
|
||||
fn new(table: &Table, visited: &Rc<RefCell<FxHashSet<*const c_void>>>) -> Self {
|
||||
pub(crate) fn new(table: &Table, visited: &Rc<RefCell<FxHashSet<*const c_void>>>) -> Self {
|
||||
let visited = Rc::clone(visited);
|
||||
let ptr = table.to_pointer();
|
||||
visited.borrow_mut().insert(ptr);
|
||||
@@ -615,17 +682,17 @@ impl Drop for RecursionGuard {
|
||||
}
|
||||
|
||||
// Checks `options` and decides should we emit an error or skip next element
|
||||
fn check_value_if_skip(
|
||||
pub(crate) fn check_value_for_skip(
|
||||
value: &Value,
|
||||
options: Options,
|
||||
visited: &RefCell<FxHashSet<*const c_void>>,
|
||||
) -> Result<bool> {
|
||||
) -> StdResult<bool, &'static str> {
|
||||
match value {
|
||||
Value::Table(table) => {
|
||||
let ptr = table.to_pointer();
|
||||
if visited.borrow().contains(&ptr) {
|
||||
if options.deny_recursive_tables {
|
||||
return Err(de::Error::custom("recursive table detected"));
|
||||
return Err("recursive table detected");
|
||||
}
|
||||
return Ok(true); // skip
|
||||
}
|
||||
|
||||
+83
-15
@@ -3,7 +3,6 @@ use serde::{ser, Serialize};
|
||||
use super::LuaSerdeExt;
|
||||
use crate::error::{Error, Result};
|
||||
use crate::lua::Lua;
|
||||
use crate::string::String;
|
||||
use crate::table::Table;
|
||||
use crate::value::{IntoLua, Value};
|
||||
|
||||
@@ -43,6 +42,12 @@ pub struct Options {
|
||||
/// [`null`]: crate::LuaSerdeExt::null
|
||||
/// [`Nil`]: crate::Value::Nil
|
||||
pub serialize_unit_to_null: bool,
|
||||
|
||||
/// If true, serialize `serde_json::Number` with arbitrary_precision to a Lua number.
|
||||
/// Otherwise it will be serialized as an object (what serde does).
|
||||
///
|
||||
/// Default: **false**
|
||||
pub detect_serde_json_arbitrary_precision: bool,
|
||||
}
|
||||
|
||||
impl Default for Options {
|
||||
@@ -58,6 +63,7 @@ impl Options {
|
||||
set_array_metatable: true,
|
||||
serialize_none_to_null: true,
|
||||
serialize_unit_to_null: true,
|
||||
detect_serde_json_arbitrary_precision: false,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -87,6 +93,20 @@ impl Options {
|
||||
self.serialize_unit_to_null = enabled;
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets [`detect_serde_json_arbitrary_precision`] option.
|
||||
///
|
||||
/// This option is used to serialize `serde_json::Number` with arbitrary precision to a Lua number.
|
||||
/// Otherwise it will be serialized as an object (what serde does).
|
||||
///
|
||||
/// This option is disabled by default.
|
||||
///
|
||||
/// [`detect_serde_json_arbitrary_precision`]: #structfield.detect_serde_json_arbitrary_precision
|
||||
#[must_use]
|
||||
pub const fn detect_serde_json_arbitrary_precision(mut self, enabled: bool) -> Self {
|
||||
self.detect_serde_json_arbitrary_precision = enabled;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> Serializer<'lua> {
|
||||
@@ -121,7 +141,7 @@ impl<'lua> ser::Serializer for Serializer<'lua> {
|
||||
type SerializeTupleStruct = SerializeSeq<'lua>;
|
||||
type SerializeTupleVariant = SerializeTupleVariant<'lua>;
|
||||
type SerializeMap = SerializeMap<'lua>;
|
||||
type SerializeStruct = SerializeMap<'lua>;
|
||||
type SerializeStruct = SerializeStruct<'lua>;
|
||||
type SerializeStructVariant = SerializeStructVariant<'lua>;
|
||||
|
||||
#[inline]
|
||||
@@ -266,7 +286,7 @@ impl<'lua> ser::Serializer for Serializer<'lua> {
|
||||
_len: usize,
|
||||
) -> Result<Self::SerializeTupleVariant> {
|
||||
Ok(SerializeTupleVariant {
|
||||
name: self.lua.create_string(variant)?,
|
||||
variant,
|
||||
table: self.lua.create_table()?,
|
||||
options: self.options,
|
||||
})
|
||||
@@ -282,8 +302,23 @@ impl<'lua> ser::Serializer for Serializer<'lua> {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn serialize_struct(self, _name: &'static str, len: usize) -> Result<Self::SerializeStruct> {
|
||||
self.serialize_map(Some(len))
|
||||
fn serialize_struct(self, name: &'static str, len: usize) -> Result<Self::SerializeStruct> {
|
||||
if self.options.detect_serde_json_arbitrary_precision
|
||||
&& name == "$serde_json::private::Number"
|
||||
&& len == 1
|
||||
{
|
||||
return Ok(SerializeStruct {
|
||||
lua: self.lua,
|
||||
inner: None,
|
||||
options: self.options,
|
||||
});
|
||||
}
|
||||
|
||||
Ok(SerializeStruct {
|
||||
lua: self.lua,
|
||||
inner: Some(Value::Table(self.lua.create_table_with_capacity(0, len)?)),
|
||||
options: self.options,
|
||||
})
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@@ -295,7 +330,7 @@ impl<'lua> ser::Serializer for Serializer<'lua> {
|
||||
len: usize,
|
||||
) -> Result<Self::SerializeStructVariant> {
|
||||
Ok(SerializeStructVariant {
|
||||
name: self.lua.create_string(variant)?,
|
||||
variant,
|
||||
table: self.lua.create_table_with_capacity(0, len)?,
|
||||
options: self.options,
|
||||
})
|
||||
@@ -402,7 +437,7 @@ impl<'lua> ser::SerializeTupleStruct for SerializeSeq<'lua> {
|
||||
|
||||
#[doc(hidden)]
|
||||
pub struct SerializeTupleVariant<'lua> {
|
||||
name: String<'lua>,
|
||||
variant: &'static str,
|
||||
table: Table<'lua>,
|
||||
options: Options,
|
||||
}
|
||||
@@ -422,7 +457,7 @@ impl<'lua> ser::SerializeTupleVariant for SerializeTupleVariant<'lua> {
|
||||
fn end(self) -> Result<Value<'lua>> {
|
||||
let lua = self.table.0.lua;
|
||||
let table = lua.create_table()?;
|
||||
table.raw_set(self.name, self.table)?;
|
||||
table.raw_set(self.variant, self.table)?;
|
||||
Ok(Value::Table(table))
|
||||
}
|
||||
}
|
||||
@@ -465,7 +500,14 @@ impl<'lua> ser::SerializeMap for SerializeMap<'lua> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> ser::SerializeStruct for SerializeMap<'lua> {
|
||||
#[doc(hidden)]
|
||||
pub struct SerializeStruct<'lua> {
|
||||
lua: &'lua Lua,
|
||||
inner: Option<Value<'lua>>,
|
||||
options: Options,
|
||||
}
|
||||
|
||||
impl<'lua> ser::SerializeStruct for SerializeStruct<'lua> {
|
||||
type Ok = Value<'lua>;
|
||||
type Error = Error;
|
||||
|
||||
@@ -473,18 +515,44 @@ impl<'lua> ser::SerializeStruct for SerializeMap<'lua> {
|
||||
where
|
||||
T: Serialize + ?Sized,
|
||||
{
|
||||
ser::SerializeMap::serialize_key(self, key)?;
|
||||
ser::SerializeMap::serialize_value(self, value)
|
||||
match self.inner {
|
||||
Some(Value::Table(ref table)) => {
|
||||
table.raw_set(key, self.lua.to_value_with(value, self.options)?)?;
|
||||
}
|
||||
None if self.options.detect_serde_json_arbitrary_precision => {
|
||||
// A special case for `serde_json::Number` with arbitrary precision.
|
||||
assert_eq!(key, "$serde_json::private::Number");
|
||||
self.inner = Some(self.lua.to_value_with(value, self.options)?);
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn end(self) -> Result<Value<'lua>> {
|
||||
ser::SerializeMap::end(self)
|
||||
match self.inner {
|
||||
Some(table @ Value::Table(_)) => Ok(table),
|
||||
Some(value) if self.options.detect_serde_json_arbitrary_precision => {
|
||||
let number_s = value.as_str().expect("not an arbitrary precision number");
|
||||
if number_s.contains(['.', 'e', 'E']) {
|
||||
if let Ok(number) = number_s.parse().map(Value::Number) {
|
||||
return Ok(number);
|
||||
}
|
||||
}
|
||||
Ok(number_s
|
||||
.parse()
|
||||
.map(Value::Integer)
|
||||
.or_else(|_| number_s.parse().map(Value::Number))
|
||||
.unwrap_or(value))
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub struct SerializeStructVariant<'lua> {
|
||||
name: String<'lua>,
|
||||
variant: &'static str,
|
||||
table: Table<'lua>,
|
||||
options: Options,
|
||||
}
|
||||
@@ -505,8 +573,8 @@ impl<'lua> ser::SerializeStructVariant for SerializeStructVariant<'lua> {
|
||||
|
||||
fn end(self) -> Result<Value<'lua>> {
|
||||
let lua = self.table.0.lua;
|
||||
let table = lua.create_table()?;
|
||||
table.raw_set(self.name, self.table)?;
|
||||
let table = lua.create_table_with_capacity(0, 1)?;
|
||||
table.raw_set(self.variant, self.table)?;
|
||||
Ok(Value::Table(table))
|
||||
}
|
||||
}
|
||||
|
||||
+15
-3
@@ -1,5 +1,4 @@
|
||||
use std::ops::{BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor, BitXorAssign};
|
||||
use std::u32;
|
||||
|
||||
/// Flags describing the set of lua standard libraries to load.
|
||||
#[derive(Copy, Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
|
||||
@@ -16,32 +15,44 @@ impl StdLib {
|
||||
feature = "luau"
|
||||
))]
|
||||
pub const COROUTINE: StdLib = StdLib(1);
|
||||
|
||||
/// [`table`](https://www.lua.org/manual/5.4/manual.html#6.6) library
|
||||
pub const TABLE: StdLib = StdLib(1 << 1);
|
||||
|
||||
/// [`io`](https://www.lua.org/manual/5.4/manual.html#6.8) library
|
||||
#[cfg(not(feature = "luau"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(not(feature = "luau"))))]
|
||||
pub const IO: StdLib = StdLib(1 << 2);
|
||||
|
||||
/// [`os`](https://www.lua.org/manual/5.4/manual.html#6.9) library
|
||||
pub const OS: StdLib = StdLib(1 << 3);
|
||||
|
||||
/// [`string`](https://www.lua.org/manual/5.4/manual.html#6.4) library
|
||||
pub const STRING: StdLib = StdLib(1 << 4);
|
||||
|
||||
/// [`utf8`](https://www.lua.org/manual/5.4/manual.html#6.5) library
|
||||
///
|
||||
/// Requires `feature = "lua54/lua53/luau"`
|
||||
#[cfg(any(feature = "lua54", feature = "lua53", feature = "luau"))]
|
||||
pub const UTF8: StdLib = StdLib(1 << 5);
|
||||
|
||||
/// [`bit`](https://www.lua.org/manual/5.2/manual.html#6.7) library
|
||||
///
|
||||
/// Requires `feature = "lua52/luajit/luau"`
|
||||
#[cfg(any(feature = "lua52", feature = "luajit", feature = "luau", doc))]
|
||||
pub const BIT: StdLib = StdLib(1 << 6);
|
||||
|
||||
/// [`math`](https://www.lua.org/manual/5.4/manual.html#6.7) library
|
||||
pub const MATH: StdLib = StdLib(1 << 7);
|
||||
|
||||
/// [`package`](https://www.lua.org/manual/5.4/manual.html#6.3) library
|
||||
#[cfg(not(feature = "luau"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(not(feature = "luau"))))]
|
||||
pub const PACKAGE: StdLib = StdLib(1 << 8);
|
||||
|
||||
/// [`buffer`](https://luau-lang.org/library#buffer-library) library
|
||||
#[cfg(any(feature = "luau", doc))]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
|
||||
pub const BUFFER: StdLib = StdLib(1 << 9);
|
||||
|
||||
/// [`jit`](http://luajit.org/ext_jit.html) library
|
||||
///
|
||||
/// Requires `feature = "luajit"`
|
||||
@@ -55,6 +66,7 @@ impl StdLib {
|
||||
#[cfg(any(feature = "luajit", doc))]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "luajit")))]
|
||||
pub const FFI: StdLib = StdLib(1 << 30);
|
||||
|
||||
/// (**unsafe**) [`debug`](https://www.lua.org/manual/5.4/manual.html#6.10) library
|
||||
pub const DEBUG: StdLib = StdLib(1 << 31);
|
||||
|
||||
|
||||
+1
-1
@@ -132,7 +132,7 @@ impl<'lua> String<'lua> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Converts the string to a generic C pointer.
|
||||
/// Converts this string to a generic C pointer.
|
||||
///
|
||||
/// There is no way to convert the pointer back to its original value.
|
||||
///
|
||||
|
||||
+205
-132
@@ -6,8 +6,8 @@ use std::os::raw::c_void;
|
||||
#[cfg(feature = "serialize")]
|
||||
use {
|
||||
rustc_hash::FxHashSet,
|
||||
serde::ser::{self, Serialize, SerializeMap, SerializeSeq, Serializer},
|
||||
std::{cell::RefCell, result::Result as StdResult},
|
||||
serde::ser::{Serialize, SerializeMap, SerializeSeq, Serializer},
|
||||
std::{cell::RefCell, rc::Rc, result::Result as StdResult},
|
||||
};
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
@@ -86,17 +86,14 @@ impl<'lua> Table<'lua> {
|
||||
}
|
||||
|
||||
let lua = self.0.lua;
|
||||
let key = key.into_lua(lua)?;
|
||||
let value = value.into_lua(lua)?;
|
||||
|
||||
let state = lua.state();
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(state);
|
||||
check_stack(state, 5)?;
|
||||
|
||||
lua.push_ref(&self.0);
|
||||
lua.push_value(key)?;
|
||||
lua.push_value(value)?;
|
||||
key.push_into_stack(lua)?;
|
||||
value.push_into_stack(lua)?;
|
||||
protect_lua!(state, 3, 0, fn(state) ffi::lua_settable(state, -3))
|
||||
}
|
||||
}
|
||||
@@ -133,19 +130,16 @@ impl<'lua> Table<'lua> {
|
||||
|
||||
let lua = self.0.lua;
|
||||
let state = lua.state();
|
||||
let key = key.into_lua(lua)?;
|
||||
|
||||
let value = unsafe {
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(state);
|
||||
check_stack(state, 4)?;
|
||||
|
||||
lua.push_ref(&self.0);
|
||||
lua.push_value(key)?;
|
||||
key.push_into_stack(lua)?;
|
||||
protect_lua!(state, 2, 1, fn(state) ffi::lua_gettable(state, -2))?;
|
||||
|
||||
lua.pop_value()
|
||||
};
|
||||
V::from_lua(value, lua)
|
||||
V::from_stack(-1, lua)
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks whether the table contains a non-nil value for `key`.
|
||||
@@ -166,13 +160,12 @@ impl<'lua> Table<'lua> {
|
||||
|
||||
let lua = self.0.lua;
|
||||
let state = lua.state();
|
||||
let value = value.into_lua(lua)?;
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(state);
|
||||
check_stack(state, 4)?;
|
||||
|
||||
lua.push_ref(&self.0);
|
||||
lua.push_value(value)?;
|
||||
value.push_into_stack(lua)?;
|
||||
protect_lua!(state, 2, 0, fn(state) {
|
||||
let len = ffi::luaL_len(state, -2) as Integer;
|
||||
ffi::lua_seti(state, -2, len + 1);
|
||||
@@ -192,7 +185,7 @@ impl<'lua> Table<'lua> {
|
||||
|
||||
let lua = self.0.lua;
|
||||
let state = lua.state();
|
||||
let value = unsafe {
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(state);
|
||||
check_stack(state, 4)?;
|
||||
|
||||
@@ -203,9 +196,8 @@ impl<'lua> Table<'lua> {
|
||||
ffi::lua_pushnil(state);
|
||||
ffi::lua_seti(state, -3, len);
|
||||
})?;
|
||||
lua.pop_value()
|
||||
};
|
||||
V::from_lua(value, lua)
|
||||
V::from_stack(-1, lua)
|
||||
}
|
||||
}
|
||||
|
||||
/// Compares two tables for equality.
|
||||
@@ -271,16 +263,13 @@ impl<'lua> Table<'lua> {
|
||||
|
||||
let lua = self.0.lua;
|
||||
let state = lua.state();
|
||||
let key = key.into_lua(lua)?;
|
||||
let value = value.into_lua(lua)?;
|
||||
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(state);
|
||||
check_stack(state, 5)?;
|
||||
|
||||
lua.push_ref(&self.0);
|
||||
lua.push_value(key)?;
|
||||
lua.push_value(value)?;
|
||||
key.push_into_stack(lua)?;
|
||||
value.push_into_stack(lua)?;
|
||||
|
||||
if lua.unlikely_memory_error() {
|
||||
ffi::lua_rawset(state, -3);
|
||||
@@ -296,39 +285,34 @@ impl<'lua> Table<'lua> {
|
||||
pub fn raw_get<K: IntoLua<'lua>, V: FromLua<'lua>>(&self, key: K) -> Result<V> {
|
||||
let lua = self.0.lua;
|
||||
let state = lua.state();
|
||||
let key = key.into_lua(lua)?;
|
||||
|
||||
let value = unsafe {
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(state);
|
||||
check_stack(state, 3)?;
|
||||
|
||||
lua.push_ref(&self.0);
|
||||
lua.push_value(key)?;
|
||||
key.push_into_stack(lua)?;
|
||||
ffi::lua_rawget(state, -2);
|
||||
|
||||
lua.pop_value()
|
||||
};
|
||||
V::from_lua(value, lua)
|
||||
V::from_stack(-1, lua)
|
||||
}
|
||||
}
|
||||
|
||||
/// 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.
|
||||
pub fn raw_insert<V: IntoLua<'lua>>(&self, idx: Integer, value: V) -> Result<()> {
|
||||
let lua = self.0.lua;
|
||||
let state = lua.state();
|
||||
|
||||
let size = self.raw_len();
|
||||
let size = self.raw_len() as Integer;
|
||||
if idx < 1 || idx > size + 1 {
|
||||
return Err(Error::runtime("index out of bounds"));
|
||||
}
|
||||
|
||||
let value = value.into_lua(lua)?;
|
||||
let lua = self.0.lua;
|
||||
let state = lua.state();
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(state);
|
||||
check_stack(state, 5)?;
|
||||
|
||||
lua.push_ref(&self.0);
|
||||
lua.push_value(value)?;
|
||||
value.push_into_stack(lua)?;
|
||||
protect_lua!(state, 2, 0, |state| {
|
||||
for i in (idx..=size).rev() {
|
||||
// table[i+1] = table[i]
|
||||
@@ -347,14 +331,12 @@ impl<'lua> Table<'lua> {
|
||||
|
||||
let lua = self.0.lua;
|
||||
let state = lua.state();
|
||||
let value = value.into_lua(lua)?;
|
||||
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(state);
|
||||
check_stack(state, 4)?;
|
||||
|
||||
lua.push_ref(&self.0);
|
||||
lua.push_value(value)?;
|
||||
value.push_into_stack(lua)?;
|
||||
|
||||
unsafe fn callback(state: *mut ffi::lua_State) {
|
||||
let len = ffi::lua_rawlen(state, -2) as Integer;
|
||||
@@ -377,7 +359,7 @@ impl<'lua> Table<'lua> {
|
||||
|
||||
let lua = self.0.lua;
|
||||
let state = lua.state();
|
||||
let value = unsafe {
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(state);
|
||||
check_stack(state, 3)?;
|
||||
|
||||
@@ -387,9 +369,9 @@ impl<'lua> Table<'lua> {
|
||||
// Set slot to nil (it must be safe to do)
|
||||
ffi::lua_pushnil(state);
|
||||
ffi::lua_rawseti(state, -3, len);
|
||||
lua.pop_value()
|
||||
};
|
||||
V::from_lua(value, lua)
|
||||
|
||||
V::from_stack(-1, lua)
|
||||
}
|
||||
}
|
||||
|
||||
/// Removes a key from the table.
|
||||
@@ -405,7 +387,7 @@ impl<'lua> Table<'lua> {
|
||||
let key = key.into_lua(lua)?;
|
||||
match key {
|
||||
Value::Integer(idx) => {
|
||||
let size = self.raw_len();
|
||||
let size = self.raw_len() as Integer;
|
||||
if idx < 1 || idx > size {
|
||||
return Err(Error::runtime("index out of bounds"));
|
||||
}
|
||||
@@ -477,7 +459,7 @@ impl<'lua> Table<'lua> {
|
||||
pub fn len(&self) -> Result<Integer> {
|
||||
// Fast track
|
||||
if !self.has_metatable() {
|
||||
return Ok(self.raw_len());
|
||||
return Ok(self.raw_len() as Integer);
|
||||
}
|
||||
|
||||
let lua = self.0.lua;
|
||||
@@ -492,9 +474,9 @@ impl<'lua> Table<'lua> {
|
||||
}
|
||||
|
||||
/// Returns the result of the Lua `#` operator, without invoking the `__len` metamethod.
|
||||
pub fn raw_len(&self) -> Integer {
|
||||
pub fn raw_len(&self) -> usize {
|
||||
let ref_thread = self.0.lua.ref_thread();
|
||||
unsafe { ffi::lua_rawlen(ref_thread, self.0.index) as Integer }
|
||||
unsafe { ffi::lua_rawlen(ref_thread, self.0.index) }
|
||||
}
|
||||
|
||||
/// Returns `true` if the table is empty, without invoking metamethods.
|
||||
@@ -609,7 +591,7 @@ impl<'lua> Table<'lua> {
|
||||
unsafe { ffi::lua_getreadonly(ref_thread, self.0.index) != 0 }
|
||||
}
|
||||
|
||||
/// Converts the table to a generic C pointer.
|
||||
/// Converts this table to a generic C pointer.
|
||||
///
|
||||
/// Different tables will give different pointers.
|
||||
/// There is no way to convert the pointer back to its original value.
|
||||
@@ -669,6 +651,34 @@ impl<'lua> Table<'lua> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Iterates over the pairs of the table, invoking the given closure on each pair.
|
||||
///
|
||||
/// This method is similar to [`Table::pairs`], but optimized for performance.
|
||||
/// It does not invoke the `__pairs` metamethod.
|
||||
pub fn for_each<K, V>(&self, mut f: impl FnMut(K, V) -> Result<()>) -> Result<()>
|
||||
where
|
||||
K: FromLua<'lua>,
|
||||
V: FromLua<'lua>,
|
||||
{
|
||||
let lua = self.0.lua;
|
||||
let state = lua.state();
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(state);
|
||||
check_stack(state, 5)?;
|
||||
|
||||
lua.push_ref(&self.0);
|
||||
ffi::lua_pushnil(state);
|
||||
while ffi::lua_next(state, -2) != 0 {
|
||||
let k = K::from_stack(-2, lua)?;
|
||||
let v = V::from_stack(-1, lua)?;
|
||||
f(k, v)?;
|
||||
// Keep key for next iteration
|
||||
ffi::lua_pop(state, 1);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Consume this table and return an iterator over all values in the sequence part of the table.
|
||||
///
|
||||
/// The iterator will yield all values `t[1]`, `t[2]` and so on, until a `nil` value is
|
||||
@@ -710,8 +720,7 @@ impl<'lua> Table<'lua> {
|
||||
pub fn sequence_values<V: FromLua<'lua>>(self) -> TableSequence<'lua, V> {
|
||||
TableSequence {
|
||||
table: self.0,
|
||||
index: Some(1),
|
||||
len: None,
|
||||
index: 1,
|
||||
_phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
@@ -723,35 +732,41 @@ impl<'lua> Table<'lua> {
|
||||
}
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
pub(crate) fn sequence_values_by_len<V: FromLua<'lua>>(
|
||||
self,
|
||||
len: Option<Integer>,
|
||||
) -> TableSequence<'lua, V> {
|
||||
let len = len.unwrap_or_else(|| self.raw_len());
|
||||
TableSequence {
|
||||
table: self.0,
|
||||
index: Some(1),
|
||||
len: Some(len),
|
||||
_phantom: PhantomData,
|
||||
pub(crate) fn for_each_value<V>(&self, mut f: impl FnMut(V) -> Result<()>) -> Result<()>
|
||||
where
|
||||
V: FromLua<'lua>,
|
||||
{
|
||||
let lua = self.0.lua;
|
||||
let state = lua.state();
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(state);
|
||||
check_stack(state, 4)?;
|
||||
|
||||
lua.push_ref(&self.0);
|
||||
let len = ffi::lua_rawlen(state, -1);
|
||||
for i in 1..=len {
|
||||
ffi::lua_rawgeti(state, -1, i as _);
|
||||
f(V::from_stack(-1, lua)?)?;
|
||||
ffi::lua_pop(state, 1);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Sets element value at position `idx` without invoking metamethods.
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn raw_seti<V: IntoLua<'lua>>(&self, idx: usize, value: V) -> Result<()> {
|
||||
#[doc(hidden)]
|
||||
pub fn raw_seti<V: IntoLua<'lua>>(&self, idx: usize, value: V) -> Result<()> {
|
||||
#[cfg(feature = "luau")]
|
||||
self.check_readonly_write()?;
|
||||
|
||||
let lua = self.0.lua;
|
||||
let state = lua.state();
|
||||
let value = value.into_lua(lua)?;
|
||||
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(state);
|
||||
check_stack(state, 5)?;
|
||||
|
||||
lua.push_ref(&self.0);
|
||||
lua.push_value(value)?;
|
||||
value.push_into_stack(lua)?;
|
||||
|
||||
let idx = idx.try_into().unwrap();
|
||||
if lua.unlikely_memory_error() {
|
||||
@@ -759,8 +774,8 @@ impl<'lua> Table<'lua> {
|
||||
} else {
|
||||
protect_lua!(state, 2, 0, |state| ffi::lua_rawseti(state, -2, idx))?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
@@ -1036,47 +1051,115 @@ impl<'lua> TableExt<'lua> for Table<'lua> {
|
||||
}
|
||||
}
|
||||
|
||||
/// A wrapped [`Table`] with customized serialization behavior.
|
||||
#[cfg(feature = "serialize")]
|
||||
pub(crate) struct SerializableTable<'a, 'lua> {
|
||||
table: &'a Table<'lua>,
|
||||
options: crate::serde::de::Options,
|
||||
visited: Rc<RefCell<FxHashSet<*const c_void>>>,
|
||||
}
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
impl<'lua> Serialize for Table<'lua> {
|
||||
#[inline]
|
||||
fn serialize<S: Serializer>(&self, serializer: S) -> StdResult<S::Ok, S::Error> {
|
||||
SerializableTable::new(self, Default::default(), Default::default()).serialize(serializer)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
impl<'a, 'lua> SerializableTable<'a, 'lua> {
|
||||
#[inline]
|
||||
pub(crate) fn new(
|
||||
table: &'a Table<'lua>,
|
||||
options: crate::serde::de::Options,
|
||||
visited: Rc<RefCell<FxHashSet<*const c_void>>>,
|
||||
) -> Self {
|
||||
Self {
|
||||
table,
|
||||
options,
|
||||
visited,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
impl<'a, 'lua> Serialize for SerializableTable<'a, 'lua> {
|
||||
fn serialize<S>(&self, serializer: S) -> StdResult<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
thread_local! {
|
||||
static VISITED: RefCell<FxHashSet<*const c_void>> = RefCell::new(FxHashSet::default());
|
||||
use crate::serde::de::{check_value_for_skip, MapPairs, RecursionGuard};
|
||||
use crate::value::SerializableValue;
|
||||
|
||||
let convert_result = |res: Result<()>, serialize_err: Option<S::Error>| match res {
|
||||
Ok(v) => Ok(v),
|
||||
Err(Error::SerializeError(_)) if serialize_err.is_some() => Err(serialize_err.unwrap()),
|
||||
Err(Error::SerializeError(msg)) => Err(serde::ser::Error::custom(msg)),
|
||||
Err(err) => Err(serde::ser::Error::custom(err.to_string())),
|
||||
};
|
||||
|
||||
let options = self.options;
|
||||
let visited = &self.visited;
|
||||
let _guard = RecursionGuard::new(self.table, visited);
|
||||
|
||||
// Array
|
||||
let len = self.table.raw_len();
|
||||
if len > 0 || self.table.is_array() {
|
||||
let mut seq = serializer.serialize_seq(Some(len))?;
|
||||
let mut serialize_err = None;
|
||||
let res = self.table.for_each_value::<Value>(|value| {
|
||||
let skip = check_value_for_skip(&value, self.options, visited)
|
||||
.map_err(|err| Error::SerializeError(err.to_string()))?;
|
||||
if skip {
|
||||
// continue iteration
|
||||
return Ok(());
|
||||
}
|
||||
seq.serialize_element(&SerializableValue::new(&value, options, Some(visited)))
|
||||
.map_err(|err| {
|
||||
serialize_err = Some(err);
|
||||
Error::SerializeError(String::new())
|
||||
})
|
||||
});
|
||||
convert_result(res, serialize_err)?;
|
||||
return seq.end();
|
||||
}
|
||||
|
||||
let ptr = self.to_pointer();
|
||||
let res = VISITED.with(|visited| {
|
||||
{
|
||||
let mut visited = visited.borrow_mut();
|
||||
if visited.contains(&ptr) {
|
||||
return Err(ser::Error::custom("recursive table detected"));
|
||||
}
|
||||
visited.insert(ptr);
|
||||
// HashMap
|
||||
let mut map = serializer.serialize_map(None)?;
|
||||
let mut serialize_err = None;
|
||||
let mut process_pair = |key, value| {
|
||||
let skip_key = check_value_for_skip(&key, self.options, visited)
|
||||
.map_err(|err| Error::SerializeError(err.to_string()))?;
|
||||
let skip_value = check_value_for_skip(&value, self.options, visited)
|
||||
.map_err(|err| Error::SerializeError(err.to_string()))?;
|
||||
if skip_key || skip_value {
|
||||
// continue iteration
|
||||
return Ok(());
|
||||
}
|
||||
map.serialize_entry(
|
||||
&SerializableValue::new(&key, options, Some(visited)),
|
||||
&SerializableValue::new(&value, options, Some(visited)),
|
||||
)
|
||||
.map_err(|err| {
|
||||
serialize_err = Some(err);
|
||||
Error::SerializeError(String::new())
|
||||
})
|
||||
};
|
||||
|
||||
let len = self.raw_len() as usize;
|
||||
if len > 0 || self.is_array() {
|
||||
let mut seq = serializer.serialize_seq(Some(len))?;
|
||||
for v in self.clone().sequence_values_by_len::<Value>(None) {
|
||||
let v = v.map_err(serde::ser::Error::custom)?;
|
||||
seq.serialize_element(&v)?;
|
||||
}
|
||||
return seq.end();
|
||||
}
|
||||
|
||||
let mut map = serializer.serialize_map(None)?;
|
||||
for kv in self.clone().pairs::<Value, Value>() {
|
||||
let (k, v) = kv.map_err(serde::ser::Error::custom)?;
|
||||
map.serialize_entry(&k, &v)?;
|
||||
}
|
||||
map.end()
|
||||
});
|
||||
VISITED.with(|visited| {
|
||||
visited.borrow_mut().remove(&ptr);
|
||||
});
|
||||
res
|
||||
let res = if !self.options.sort_keys {
|
||||
// Fast track
|
||||
self.table.for_each(process_pair)
|
||||
} else {
|
||||
MapPairs::new(self.table.clone(), self.options.sort_keys)
|
||||
.map_err(serde::ser::Error::custom)?
|
||||
.try_for_each(|kv| {
|
||||
let (key, value) = kv?;
|
||||
process_pair(key, value)
|
||||
})
|
||||
};
|
||||
convert_result(res, serialize_err)?;
|
||||
map.end()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1110,16 +1193,15 @@ where
|
||||
lua.push_ref(&self.table);
|
||||
lua.push_value(prev_key)?;
|
||||
|
||||
let next = protect_lua!(state, 2, ffi::LUA_MULTRET, |state| {
|
||||
ffi::lua_next(state, -2)
|
||||
})?;
|
||||
if next != 0 {
|
||||
let value = lua.pop_value();
|
||||
let key = lua.pop_value();
|
||||
// It must be safe to call `lua_next` unprotected as deleting a key from a table is
|
||||
// a permitted operation.
|
||||
// It fails only if the key is not found (never existed) which seems impossible scenario.
|
||||
if ffi::lua_next(state, -2) != 0 {
|
||||
let key = lua.stack_value(-2);
|
||||
Ok(Some((
|
||||
key.clone(),
|
||||
K::from_lua(key, lua)?,
|
||||
V::from_lua(value, lua)?,
|
||||
V::from_stack(-1, lua)?,
|
||||
)))
|
||||
} else {
|
||||
Ok(None)
|
||||
@@ -1146,9 +1228,9 @@ where
|
||||
///
|
||||
/// [`Table::sequence_values`]: crate::Table::sequence_values
|
||||
pub struct TableSequence<'lua, V> {
|
||||
// TODO: Use `&Table`
|
||||
table: LuaRef<'lua>,
|
||||
index: Option<Integer>,
|
||||
len: Option<Integer>,
|
||||
index: Integer,
|
||||
_phantom: PhantomData<V>,
|
||||
}
|
||||
|
||||
@@ -1159,31 +1241,22 @@ where
|
||||
type Item = Result<V>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
if let Some(index) = self.index.take() {
|
||||
let lua = self.table.lua;
|
||||
let state = lua.state();
|
||||
|
||||
let res = (|| unsafe {
|
||||
let _sg = StackGuard::new(state);
|
||||
check_stack(state, 1)?;
|
||||
|
||||
lua.push_ref(&self.table);
|
||||
match ffi::lua_rawgeti(state, -1, index) {
|
||||
ffi::LUA_TNIL if index > self.len.unwrap_or(0) => Ok(None),
|
||||
_ => Ok(Some((index, lua.pop_value()))),
|
||||
}
|
||||
})();
|
||||
|
||||
match res {
|
||||
Ok(Some((index, r))) => {
|
||||
self.index = Some(index + 1);
|
||||
Some(V::from_lua(r, lua))
|
||||
}
|
||||
Ok(None) => None,
|
||||
Err(err) => Some(Err(err)),
|
||||
let lua = self.table.lua;
|
||||
let state = lua.state();
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(state);
|
||||
if let Err(err) = check_stack(state, 1) {
|
||||
return Some(Err(err));
|
||||
}
|
||||
|
||||
lua.push_ref(&self.table);
|
||||
match ffi::lua_rawgeti(state, -1, self.index) {
|
||||
ffi::LUA_TNIL => None,
|
||||
_ => {
|
||||
self.index += 1;
|
||||
Some(V::from_stack(-1, lua))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+204
-133
@@ -1,5 +1,4 @@
|
||||
use std::cmp;
|
||||
use std::os::raw::c_int;
|
||||
use std::os::raw::{c_int, c_void};
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
#[allow(unused)]
|
||||
@@ -16,10 +15,7 @@ use crate::{
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
use {
|
||||
crate::{
|
||||
lua::ASYNC_POLL_PENDING,
|
||||
value::{MultiValue, Value},
|
||||
},
|
||||
crate::value::MultiValue,
|
||||
futures_util::stream::Stream,
|
||||
std::{
|
||||
future::Future,
|
||||
@@ -30,7 +26,7 @@ use {
|
||||
},
|
||||
};
|
||||
|
||||
/// Status of a Lua thread (or coroutine).
|
||||
/// Status of a Lua thread (coroutine).
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
pub enum ThreadStatus {
|
||||
/// The thread was just created, or is suspended because it has called `coroutine.yield`.
|
||||
@@ -45,9 +41,33 @@ pub enum ThreadStatus {
|
||||
Error,
|
||||
}
|
||||
|
||||
/// Handle to an internal Lua thread (or coroutine).
|
||||
/// Handle to an internal Lua thread (coroutine).
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Thread<'lua>(pub(crate) LuaRef<'lua>);
|
||||
pub struct Thread<'lua>(pub(crate) LuaRef<'lua>, pub(crate) *mut ffi::lua_State);
|
||||
|
||||
/// Owned handle to an internal Lua thread (coroutine).
|
||||
///
|
||||
/// The owned handle holds a *strong* reference to the current Lua instance.
|
||||
/// Be warned, if you place it into a Lua type (eg. [`UserData`] or a Rust callback), it is *very easy*
|
||||
/// to accidentally cause reference cycles that would prevent destroying Lua instance.
|
||||
///
|
||||
/// [`UserData`]: crate::UserData
|
||||
#[cfg(feature = "unstable")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "unstable")))]
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct OwnedThread(
|
||||
pub(crate) crate::types::LuaOwnedRef,
|
||||
pub(crate) *mut ffi::lua_State,
|
||||
);
|
||||
|
||||
#[cfg(feature = "unstable")]
|
||||
impl OwnedThread {
|
||||
/// Get borrowed handle to the underlying Lua table.
|
||||
#[cfg_attr(feature = "send", allow(unused))]
|
||||
pub const fn to_ref(&self) -> Thread {
|
||||
Thread(self.0.to_ref(), self.1)
|
||||
}
|
||||
}
|
||||
|
||||
/// Thread (coroutine) representation as an async [`Future`] or [`Stream`].
|
||||
///
|
||||
@@ -66,6 +86,16 @@ pub struct AsyncThread<'lua, R> {
|
||||
}
|
||||
|
||||
impl<'lua> Thread<'lua> {
|
||||
#[inline(always)]
|
||||
pub(crate) fn new(r#ref: LuaRef<'lua>) -> Self {
|
||||
let state = unsafe { ffi::lua_tothread(r#ref.lua.ref_thread(), r#ref.index) };
|
||||
Thread(r#ref, state)
|
||||
}
|
||||
|
||||
const fn state(&self) -> *mut ffi::lua_State {
|
||||
self.1
|
||||
}
|
||||
|
||||
/// Resumes execution of this thread.
|
||||
///
|
||||
/// Equivalent to `coroutine.resume`.
|
||||
@@ -112,62 +142,65 @@ impl<'lua> Thread<'lua> {
|
||||
A: IntoLuaMulti<'lua>,
|
||||
R: FromLuaMulti<'lua>,
|
||||
{
|
||||
if self.status() != ThreadStatus::Resumable {
|
||||
return Err(Error::CoroutineInactive);
|
||||
}
|
||||
|
||||
let lua = self.0.lua;
|
||||
let state = lua.state();
|
||||
|
||||
let mut args = args.into_lua_multi(lua)?;
|
||||
let nargs = args.len() as c_int;
|
||||
let results = unsafe {
|
||||
let thread_state = self.state();
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(state);
|
||||
check_stack(state, cmp::max(nargs + 1, 3))?;
|
||||
let _thread_sg = StackGuard::with_top(thread_state, 0);
|
||||
|
||||
let thread_state = ffi::lua_tothread(lua.ref_thread(), self.0.index);
|
||||
|
||||
let status = ffi::lua_status(thread_state);
|
||||
if status != ffi::LUA_YIELD && ffi::lua_gettop(thread_state) == 0 {
|
||||
return Err(Error::CoroutineInactive);
|
||||
}
|
||||
|
||||
check_stack(thread_state, nargs)?;
|
||||
for arg in args.drain_all() {
|
||||
lua.push_value(arg)?;
|
||||
}
|
||||
ffi::lua_xmove(state, thread_state, nargs);
|
||||
|
||||
let mut nresults = 0;
|
||||
|
||||
let ret = ffi::lua_resume(thread_state, state, nargs, &mut nresults as *mut c_int);
|
||||
if ret != ffi::LUA_OK && ret != ffi::LUA_YIELD {
|
||||
if ret == ffi::LUA_ERRMEM {
|
||||
// Don't call error handler for memory errors
|
||||
return Err(pop_error(thread_state, ret));
|
||||
}
|
||||
check_stack(state, 3)?;
|
||||
protect_lua!(state, 0, 1, |state| error_traceback_thread(
|
||||
state,
|
||||
thread_state
|
||||
))?;
|
||||
return Err(pop_error(state, ret));
|
||||
}
|
||||
|
||||
let mut results = args; // Reuse MultiValue container
|
||||
check_stack(state, nresults + 2)?; // 2 is extra for `lua.pop_value()` below
|
||||
let nresults = self.resume_inner(args)?;
|
||||
check_stack(state, nresults + 1)?;
|
||||
ffi::lua_xmove(thread_state, state, nresults);
|
||||
|
||||
for _ in 0..nresults {
|
||||
results.push_front(lua.pop_value());
|
||||
R::from_stack_multi(nresults, lua)
|
||||
}
|
||||
}
|
||||
|
||||
/// Resumes execution of this thread.
|
||||
///
|
||||
/// It's similar to `resume()` but leaves `nresults` values on the thread stack.
|
||||
unsafe fn resume_inner<A: IntoLuaMulti<'lua>>(&self, args: A) -> Result<c_int> {
|
||||
let lua = self.0.lua;
|
||||
let state = lua.state();
|
||||
let thread_state = self.state();
|
||||
|
||||
let nargs = args.push_into_stack_multi(lua)?;
|
||||
if nargs > 0 {
|
||||
check_stack(thread_state, nargs)?;
|
||||
ffi::lua_xmove(state, thread_state, nargs);
|
||||
}
|
||||
|
||||
let mut nresults = 0;
|
||||
let ret = ffi::lua_resume(thread_state, state, nargs, &mut nresults as *mut c_int);
|
||||
if ret != ffi::LUA_OK && ret != ffi::LUA_YIELD {
|
||||
if ret == ffi::LUA_ERRMEM {
|
||||
// Don't call error handler for memory errors
|
||||
return Err(pop_error(thread_state, ret));
|
||||
}
|
||||
results
|
||||
};
|
||||
R::from_lua_multi(results, lua)
|
||||
check_stack(state, 3)?;
|
||||
protect_lua!(state, 0, 1, |state| error_traceback_thread(
|
||||
state,
|
||||
thread_state
|
||||
))?;
|
||||
return Err(pop_error(state, ret));
|
||||
}
|
||||
|
||||
Ok(nresults)
|
||||
}
|
||||
|
||||
/// Gets the status of the thread.
|
||||
pub fn status(&self) -> ThreadStatus {
|
||||
let lua = self.0.lua;
|
||||
let thread_state = self.state();
|
||||
if thread_state == self.0.lua.state() {
|
||||
// The coroutine is currently running
|
||||
return ThreadStatus::Unresumable;
|
||||
}
|
||||
unsafe {
|
||||
let thread_state = ffi::lua_tothread(lua.ref_thread(), self.0.index);
|
||||
|
||||
let status = ffi::lua_status(thread_state);
|
||||
if status != ffi::LUA_OK && status != ffi::LUA_YIELD {
|
||||
ThreadStatus::Error
|
||||
@@ -191,8 +224,7 @@ impl<'lua> Thread<'lua> {
|
||||
{
|
||||
let lua = self.0.lua;
|
||||
unsafe {
|
||||
let thread_state = ffi::lua_tothread(lua.ref_thread(), self.0.index);
|
||||
lua.set_thread_hook(thread_state, triggers, callback);
|
||||
lua.set_thread_hook(self.state(), triggers, callback);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -211,21 +243,18 @@ impl<'lua> Thread<'lua> {
|
||||
///
|
||||
/// [Lua 5.4]: https://www.lua.org/manual/5.4/manual.html#lua_closethread
|
||||
#[cfg(any(feature = "lua54", feature = "luau"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(not(feature = "luau"))))]
|
||||
#[cfg_attr(docsrs, doc(cfg(any(feature = "lua54", feature = "luau"))))]
|
||||
pub fn reset(&self, func: crate::function::Function<'lua>) -> Result<()> {
|
||||
let lua = self.0.lua;
|
||||
let state = lua.state();
|
||||
let thread_state = self.state();
|
||||
if thread_state == lua.state() {
|
||||
return Err(Error::runtime("cannot reset a running thread"));
|
||||
}
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(state);
|
||||
check_stack(state, 2)?;
|
||||
|
||||
lua.push_ref(&self.0);
|
||||
let thread_state = ffi::lua_tothread(state, -1);
|
||||
|
||||
#[cfg(all(feature = "lua54", not(feature = "vendored")))]
|
||||
let status = ffi::lua_resetthread(thread_state);
|
||||
#[cfg(all(feature = "lua54", feature = "vendored"))]
|
||||
let status = ffi::lua_closethread(thread_state, state);
|
||||
let status = ffi::lua_closethread(thread_state, lua.state());
|
||||
#[cfg(feature = "lua54")]
|
||||
if status != ffi::LUA_OK {
|
||||
return Err(pop_error(thread_state, status));
|
||||
@@ -233,8 +262,8 @@ impl<'lua> Thread<'lua> {
|
||||
#[cfg(feature = "luau")]
|
||||
ffi::lua_resetthread(thread_state);
|
||||
|
||||
lua.push_ref(&func.0);
|
||||
ffi::lua_xmove(state, thread_state, 1);
|
||||
// Push function to the top of the thread stack
|
||||
ffi::lua_xpush(lua.ref_thread(), thread_state, func.0.index);
|
||||
|
||||
#[cfg(feature = "luau")]
|
||||
{
|
||||
@@ -345,13 +374,31 @@ impl<'lua> Thread<'lua> {
|
||||
pub fn sandbox(&self) -> Result<()> {
|
||||
let lua = self.0.lua;
|
||||
let state = lua.state();
|
||||
let thread_state = self.state();
|
||||
unsafe {
|
||||
let thread = ffi::lua_tothread(lua.ref_thread(), self.0.index);
|
||||
check_stack(thread, 3)?;
|
||||
check_stack(thread_state, 3)?;
|
||||
check_stack(state, 3)?;
|
||||
protect_lua!(state, 0, 0, |_| ffi::luaL_sandboxthread(thread))
|
||||
protect_lua!(state, 0, 0, |_| ffi::luaL_sandboxthread(thread_state))
|
||||
}
|
||||
}
|
||||
|
||||
/// Converts this thread to a generic C pointer.
|
||||
///
|
||||
/// There is no way to convert the pointer back to its original value.
|
||||
///
|
||||
/// Typically this function is used only for hashing and debug information.
|
||||
#[inline]
|
||||
pub fn to_pointer(&self) -> *const c_void {
|
||||
self.0.to_pointer()
|
||||
}
|
||||
|
||||
/// Convert this handle to owned version.
|
||||
#[cfg(all(feature = "unstable", any(not(feature = "send"), doc)))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all(feature = "unstable", not(feature = "send")))))]
|
||||
#[inline]
|
||||
pub fn into_owned(self) -> OwnedThread {
|
||||
OwnedThread(self.0.into_owned(), self.1)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> PartialEq for Thread<'lua> {
|
||||
@@ -360,6 +407,26 @@ impl<'lua> PartialEq for Thread<'lua> {
|
||||
}
|
||||
}
|
||||
|
||||
// Additional shortcuts
|
||||
#[cfg(feature = "unstable")]
|
||||
impl OwnedThread {
|
||||
/// Resumes execution of this thread.
|
||||
///
|
||||
/// See [`Thread::resume()`] for more details.
|
||||
pub fn resume<'lua, A, R>(&'lua self, args: A) -> Result<R>
|
||||
where
|
||||
A: IntoLuaMulti<'lua>,
|
||||
R: FromLuaMulti<'lua>,
|
||||
{
|
||||
self.to_ref().resume(args)
|
||||
}
|
||||
|
||||
/// Gets the status of the thread.
|
||||
pub fn status(&self) -> ThreadStatus {
|
||||
self.to_ref().status()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
impl<'lua, R> AsyncThread<'lua, R> {
|
||||
#[inline]
|
||||
@@ -379,11 +446,10 @@ impl<'lua, R> Drop for AsyncThread<'lua, R> {
|
||||
if !lua.recycle_thread(&mut self.thread) {
|
||||
#[cfg(feature = "lua54")]
|
||||
if self.thread.status() == ThreadStatus::Error {
|
||||
let thread_state = ffi::lua_tothread(lua.ref_thread(), self.thread.0.index);
|
||||
#[cfg(not(feature = "vendored"))]
|
||||
ffi::lua_resetthread(thread_state);
|
||||
ffi::lua_resetthread(self.thread.state());
|
||||
#[cfg(feature = "vendored")]
|
||||
ffi::lua_closethread(thread_state, lua.state());
|
||||
ffi::lua_closethread(self.thread.state(), lua.state());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -399,29 +465,36 @@ where
|
||||
type Item = Result<R>;
|
||||
|
||||
fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
|
||||
let lua = self.thread.0.lua;
|
||||
|
||||
match self.thread.status() {
|
||||
ThreadStatus::Resumable => {}
|
||||
_ => return Poll::Ready(None),
|
||||
};
|
||||
|
||||
let _wg = WakerGuard::new(lua, cx.waker());
|
||||
|
||||
// This is safe as we are not moving the whole struct
|
||||
let this = unsafe { self.get_unchecked_mut() };
|
||||
let ret: MultiValue = if let Some(args) = this.init_args.take() {
|
||||
this.thread.resume(args?)?
|
||||
} else {
|
||||
this.thread.resume(())?
|
||||
};
|
||||
|
||||
if is_poll_pending(&ret) {
|
||||
return Poll::Pending;
|
||||
if self.thread.status() != ThreadStatus::Resumable {
|
||||
return Poll::Ready(None);
|
||||
}
|
||||
|
||||
cx.waker().wake_by_ref();
|
||||
Poll::Ready(Some(R::from_lua_multi(ret, lua)))
|
||||
let lua = self.thread.0.lua;
|
||||
let state = lua.state();
|
||||
let thread_state = self.thread.state();
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(state);
|
||||
let _thread_sg = StackGuard::with_top(thread_state, 0);
|
||||
let _wg = WakerGuard::new(lua, cx.waker());
|
||||
|
||||
// This is safe as we are not moving the whole struct
|
||||
let this = self.get_unchecked_mut();
|
||||
let nresults = if let Some(args) = this.init_args.take() {
|
||||
this.thread.resume_inner(args?)?
|
||||
} else {
|
||||
this.thread.resume_inner(())?
|
||||
};
|
||||
|
||||
if nresults == 1 && is_poll_pending(thread_state) {
|
||||
return Poll::Pending;
|
||||
}
|
||||
|
||||
check_stack(state, nresults + 1)?;
|
||||
ffi::lua_xmove(thread_state, state, nresults);
|
||||
|
||||
cx.waker().wake_by_ref();
|
||||
Poll::Ready(Some(R::from_stack_multi(nresults, lua)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -433,46 +506,48 @@ where
|
||||
type Output = Result<R>;
|
||||
|
||||
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
if self.thread.status() != ThreadStatus::Resumable {
|
||||
return Poll::Ready(Err(Error::CoroutineInactive));
|
||||
}
|
||||
|
||||
let lua = self.thread.0.lua;
|
||||
let state = lua.state();
|
||||
let thread_state = self.thread.state();
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(state);
|
||||
let _thread_sg = StackGuard::with_top(thread_state, 0);
|
||||
let _wg = WakerGuard::new(lua, cx.waker());
|
||||
|
||||
match self.thread.status() {
|
||||
ThreadStatus::Resumable => {}
|
||||
_ => return Poll::Ready(Err(Error::CoroutineInactive)),
|
||||
};
|
||||
// This is safe as we are not moving the whole struct
|
||||
let this = self.get_unchecked_mut();
|
||||
let nresults = if let Some(args) = this.init_args.take() {
|
||||
this.thread.resume_inner(args?)?
|
||||
} else {
|
||||
this.thread.resume_inner(())?
|
||||
};
|
||||
|
||||
let _wg = WakerGuard::new(lua, cx.waker());
|
||||
if nresults == 1 && is_poll_pending(thread_state) {
|
||||
return Poll::Pending;
|
||||
}
|
||||
|
||||
// This is safe as we are not moving the whole struct
|
||||
let this = unsafe { self.get_unchecked_mut() };
|
||||
let ret: MultiValue = if let Some(args) = this.init_args.take() {
|
||||
this.thread.resume(args?)?
|
||||
} else {
|
||||
this.thread.resume(())?
|
||||
};
|
||||
if ffi::lua_status(thread_state) == ffi::LUA_YIELD {
|
||||
// Ignore value returned via yield()
|
||||
cx.waker().wake_by_ref();
|
||||
return Poll::Pending;
|
||||
}
|
||||
|
||||
if is_poll_pending(&ret) {
|
||||
return Poll::Pending;
|
||||
check_stack(state, nresults + 1)?;
|
||||
ffi::lua_xmove(thread_state, state, nresults);
|
||||
|
||||
Poll::Ready(R::from_stack_multi(nresults, lua))
|
||||
}
|
||||
|
||||
if let ThreadStatus::Resumable = this.thread.status() {
|
||||
// Ignore value returned via yield()
|
||||
cx.waker().wake_by_ref();
|
||||
return Poll::Pending;
|
||||
}
|
||||
|
||||
Poll::Ready(R::from_lua_multi(ret, lua))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
#[inline(always)]
|
||||
fn is_poll_pending(val: &MultiValue) -> bool {
|
||||
match val.iter().enumerate().last() {
|
||||
Some((0, Value::LightUserData(ud))) => {
|
||||
std::ptr::eq(ud.0 as *const u8, &ASYNC_POLL_PENDING as *const u8)
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
unsafe fn is_poll_pending(state: *mut ffi::lua_State) -> bool {
|
||||
ffi::lua_tolightuserdata(state, -1) == Lua::poll_pending().0
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
@@ -486,23 +561,19 @@ struct WakerGuard<'lua, 'a> {
|
||||
impl<'lua, 'a> WakerGuard<'lua, 'a> {
|
||||
#[inline]
|
||||
pub fn new(lua: &'lua Lua, waker: &'a Waker) -> Result<WakerGuard<'lua, 'a>> {
|
||||
unsafe {
|
||||
let prev = lua.set_waker(NonNull::from(waker));
|
||||
Ok(WakerGuard {
|
||||
lua,
|
||||
prev,
|
||||
_phantom: PhantomData,
|
||||
})
|
||||
}
|
||||
let prev = unsafe { lua.set_waker(NonNull::from(waker)) };
|
||||
Ok(WakerGuard {
|
||||
lua,
|
||||
prev,
|
||||
_phantom: PhantomData,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
impl<'lua, 'a> Drop for WakerGuard<'lua, 'a> {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
self.lua.set_waker(self.prev);
|
||||
}
|
||||
unsafe { self.lua.set_waker(self.prev) };
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+41
-37
@@ -4,20 +4,19 @@ use std::hash::{Hash, Hasher};
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use std::os::raw::{c_int, c_void};
|
||||
use std::result::Result as StdResult;
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::sync::atomic::{AtomicI32, Ordering};
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::{fmt, mem, ptr};
|
||||
|
||||
use rustc_hash::FxHashMap;
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
use futures_util::future::LocalBoxFuture;
|
||||
|
||||
use crate::error::Result;
|
||||
#[cfg(not(feature = "luau"))]
|
||||
use crate::hook::Debug;
|
||||
use crate::lua::{ExtraData, Lua};
|
||||
use crate::value::MultiValue;
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
use {crate::value::MultiValue, futures_util::future::LocalBoxFuture};
|
||||
|
||||
#[cfg(feature = "unstable")]
|
||||
use {crate::lua::LuaInner, std::marker::PhantomData};
|
||||
@@ -30,12 +29,21 @@ pub type Integer = ffi::lua_Integer;
|
||||
/// Type of Lua floating point numbers.
|
||||
pub type Number = ffi::lua_Number;
|
||||
|
||||
// Represents different subtypes wrapped to AnyUserData
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
pub(crate) enum SubtypeId {
|
||||
None,
|
||||
#[cfg(feature = "luau")]
|
||||
Buffer,
|
||||
#[cfg(feature = "luajit")]
|
||||
CData,
|
||||
}
|
||||
|
||||
/// A "light" userdata value. Equivalent to an unmanaged raw pointer.
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
pub struct LightUserData(pub *mut c_void);
|
||||
|
||||
pub(crate) type Callback<'lua, 'a> =
|
||||
Box<dyn Fn(&'lua Lua, MultiValue<'lua>) -> Result<MultiValue<'lua>> + 'a>;
|
||||
pub(crate) type Callback<'lua, 'a> = Box<dyn Fn(&'lua Lua, c_int) -> Result<c_int> + 'a>;
|
||||
|
||||
pub(crate) struct Upvalue<T> {
|
||||
pub(crate) data: T,
|
||||
@@ -46,13 +54,13 @@ pub(crate) type CallbackUpvalue = Upvalue<Callback<'static, 'static>>;
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
pub(crate) type AsyncCallback<'lua, 'a> =
|
||||
Box<dyn Fn(&'lua Lua, MultiValue<'lua>) -> LocalBoxFuture<'lua, Result<MultiValue<'lua>>> + 'a>;
|
||||
Box<dyn Fn(&'lua Lua, MultiValue<'lua>) -> LocalBoxFuture<'lua, Result<c_int>> + 'a>;
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
pub(crate) type AsyncCallbackUpvalue = Upvalue<AsyncCallback<'static, 'static>>;
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
pub(crate) type AsyncPollUpvalue = Upvalue<LocalBoxFuture<'static, Result<MultiValue<'static>>>>;
|
||||
pub(crate) type AsyncPollUpvalue = Upvalue<LocalBoxFuture<'static, Result<c_int>>>;
|
||||
|
||||
/// Type to set next Luau VM action after executing interrupt function.
|
||||
#[cfg(any(feature = "luau", doc))]
|
||||
@@ -196,26 +204,25 @@ pub(crate) struct DestructedUserdata;
|
||||
/// [`AnyUserData::set_user_value`]: crate::AnyUserData::set_user_value
|
||||
/// [`AnyUserData::user_value`]: crate::AnyUserData::user_value
|
||||
pub struct RegistryKey {
|
||||
pub(crate) registry_id: c_int,
|
||||
pub(crate) is_nil: AtomicBool,
|
||||
pub(crate) registry_id: AtomicI32,
|
||||
pub(crate) unref_list: Arc<Mutex<Option<Vec<c_int>>>>,
|
||||
}
|
||||
|
||||
impl fmt::Debug for RegistryKey {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "RegistryKey({})", self.registry_id)
|
||||
write!(f, "RegistryKey({})", self.id())
|
||||
}
|
||||
}
|
||||
|
||||
impl Hash for RegistryKey {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
self.registry_id.hash(state)
|
||||
self.id().hash(state)
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for RegistryKey {
|
||||
fn eq(&self, other: &RegistryKey) -> bool {
|
||||
self.registry_id == other.registry_id && Arc::ptr_eq(&self.unref_list, &other.unref_list)
|
||||
self.id() == other.id() && Arc::ptr_eq(&self.unref_list, &other.unref_list)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -223,50 +230,47 @@ impl Eq for RegistryKey {}
|
||||
|
||||
impl Drop for RegistryKey {
|
||||
fn drop(&mut self) {
|
||||
let registry_id = self.id();
|
||||
// We don't need to collect nil slot
|
||||
if self.registry_id > ffi::LUA_REFNIL {
|
||||
if registry_id > ffi::LUA_REFNIL {
|
||||
let mut unref_list = mlua_expect!(self.unref_list.lock(), "unref list poisoned");
|
||||
if let Some(list) = unref_list.as_mut() {
|
||||
list.push(self.registry_id);
|
||||
list.push(registry_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl RegistryKey {
|
||||
// Creates a new instance of `RegistryKey`
|
||||
/// Creates a new instance of `RegistryKey`
|
||||
pub(crate) const fn new(id: c_int, unref_list: Arc<Mutex<Option<Vec<c_int>>>>) -> Self {
|
||||
RegistryKey {
|
||||
registry_id: id,
|
||||
is_nil: AtomicBool::new(id == ffi::LUA_REFNIL),
|
||||
registry_id: AtomicI32::new(id),
|
||||
unref_list,
|
||||
}
|
||||
}
|
||||
|
||||
// Destroys the `RegistryKey` without adding to the unref list
|
||||
pub(crate) fn take(self) -> c_int {
|
||||
let registry_id = self.registry_id;
|
||||
/// Returns the underlying Lua reference of this `RegistryKey`
|
||||
#[inline(always)]
|
||||
pub fn id(&self) -> c_int {
|
||||
self.registry_id.load(Ordering::Relaxed)
|
||||
}
|
||||
|
||||
/// Sets the unique Lua reference key of this `RegistryKey`
|
||||
#[inline(always)]
|
||||
pub(crate) fn set_id(&self, id: c_int) {
|
||||
self.registry_id.store(id, Ordering::Relaxed);
|
||||
}
|
||||
|
||||
/// Destroys the `RegistryKey` without adding to the unref list
|
||||
pub(crate) fn take(self) -> i32 {
|
||||
let registry_id = self.id();
|
||||
unsafe {
|
||||
ptr::read(&self.unref_list);
|
||||
mem::forget(self);
|
||||
}
|
||||
registry_id
|
||||
}
|
||||
|
||||
// Returns true if this `RegistryKey` holds a nil value
|
||||
#[inline(always)]
|
||||
pub(crate) fn is_nil(&self) -> bool {
|
||||
self.is_nil.load(Ordering::Relaxed)
|
||||
}
|
||||
|
||||
// Marks value of this `RegistryKey` as `Nil`
|
||||
#[inline(always)]
|
||||
pub(crate) fn set_nil(&self, enabled: bool) {
|
||||
// We cannot replace previous value with nil in as this will break
|
||||
// Lua mechanism to find free keys.
|
||||
// Instead, we set a special flag to mark value as nil.
|
||||
self.is_nil.store(enabled, Ordering::Relaxed);
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct LuaRef<'lua> {
|
||||
|
||||
+54
-26
@@ -5,7 +5,7 @@ use std::fmt;
|
||||
use std::hash::Hash;
|
||||
use std::mem;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use std::os::raw::{c_char, c_int};
|
||||
use std::os::raw::{c_char, c_int, c_void};
|
||||
use std::string::String as StdString;
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
@@ -22,7 +22,7 @@ use crate::function::Function;
|
||||
use crate::lua::Lua;
|
||||
use crate::string::String;
|
||||
use crate::table::{Table, TablePairs};
|
||||
use crate::types::{LuaRef, MaybeSend};
|
||||
use crate::types::{LuaRef, MaybeSend, SubtypeId};
|
||||
use crate::util::{check_stack, get_userdata, take_userdata, StackGuard};
|
||||
use crate::value::{FromLua, FromLuaMulti, IntoLua, IntoLuaMulti, Value};
|
||||
use crate::UserDataRegistry;
|
||||
@@ -54,8 +54,8 @@ pub enum MetaMethod {
|
||||
/// The unary minus (`-`) operator.
|
||||
Unm,
|
||||
/// The floor division (//) operator.
|
||||
/// Requires `feature = "lua54/lua53"`
|
||||
#[cfg(any(feature = "lua54", feature = "lua53"))]
|
||||
/// Requires `feature = "lua54/lua53/luau"`
|
||||
#[cfg(any(feature = "lua54", feature = "lua53", feature = "luau"))]
|
||||
IDiv,
|
||||
/// The bitwise AND (&) operator.
|
||||
/// Requires `feature = "lua54/lua53"`
|
||||
@@ -180,7 +180,7 @@ impl MetaMethod {
|
||||
MetaMethod::Pow => "__pow",
|
||||
MetaMethod::Unm => "__unm",
|
||||
|
||||
#[cfg(any(feature = "lua54", feature = "lua53"))]
|
||||
#[cfg(any(feature = "lua54", feature = "lua53", feature = "luau"))]
|
||||
MetaMethod::IDiv => "__idiv",
|
||||
#[cfg(any(feature = "lua54", feature = "lua53"))]
|
||||
MetaMethod::BAnd => "__band",
|
||||
@@ -580,12 +580,12 @@ pub trait UserDataFields<'lua, T> {
|
||||
/// # use mlua::{Lua, Result, UserData};
|
||||
/// # fn main() -> Result<()> {
|
||||
/// # let lua = Lua::new();
|
||||
/// struct MyUserData(i32);
|
||||
/// struct MyUserData;
|
||||
///
|
||||
/// impl UserData for MyUserData {}
|
||||
///
|
||||
/// // `MyUserData` now implements `IntoLua`:
|
||||
/// lua.globals().set("myobject", MyUserData(123))?;
|
||||
/// lua.globals().set("myobject", MyUserData)?;
|
||||
///
|
||||
/// lua.load("assert(type(myobject) == 'userdata')").exec()?;
|
||||
/// # Ok(())
|
||||
@@ -791,7 +791,7 @@ impl<T> Deref for UserDataVariant<T> {
|
||||
/// [`is`]: crate::AnyUserData::is
|
||||
/// [`borrow`]: crate::AnyUserData::borrow
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct AnyUserData<'lua>(pub(crate) LuaRef<'lua>);
|
||||
pub struct AnyUserData<'lua>(pub(crate) LuaRef<'lua>, pub(crate) SubtypeId);
|
||||
|
||||
/// Owned handle to an internal Lua userdata.
|
||||
///
|
||||
@@ -801,25 +801,21 @@ pub struct AnyUserData<'lua>(pub(crate) LuaRef<'lua>);
|
||||
#[cfg(feature = "unstable")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "unstable")))]
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct OwnedAnyUserData(pub(crate) crate::types::LuaOwnedRef);
|
||||
pub struct OwnedAnyUserData(pub(crate) crate::types::LuaOwnedRef, pub(crate) SubtypeId);
|
||||
|
||||
#[cfg(feature = "unstable")]
|
||||
impl OwnedAnyUserData {
|
||||
/// Get borrowed handle to the underlying Lua userdata.
|
||||
#[cfg_attr(feature = "send", allow(unused))]
|
||||
pub const fn to_ref(&self) -> AnyUserData {
|
||||
AnyUserData(self.0.to_ref())
|
||||
AnyUserData(self.0.to_ref(), self.1)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> AnyUserData<'lua> {
|
||||
/// Checks whether the type of this userdata is `T`.
|
||||
pub fn is<T: 'static>(&self) -> bool {
|
||||
match self.inspect(|_: &UserDataCell<T>| Ok(())) {
|
||||
Ok(()) => true,
|
||||
Err(Error::UserDataTypeMismatch) => false,
|
||||
Err(_) => unreachable!(),
|
||||
}
|
||||
self.inspect(|_: &UserDataCell<T>| Ok(())).is_ok()
|
||||
}
|
||||
|
||||
/// Borrow this userdata immutably if it is of type `T`.
|
||||
@@ -919,7 +915,7 @@ impl<'lua> AnyUserData<'lua> {
|
||||
check_stack(state, 5)?;
|
||||
|
||||
lua.push_userdata_ref(&self.0)?;
|
||||
lua.push_value(v.into_lua(lua)?)?;
|
||||
lua.push(v)?;
|
||||
|
||||
#[cfg(feature = "lua54")]
|
||||
if n < USER_VALUE_MAXSLOT {
|
||||
@@ -1014,7 +1010,7 @@ impl<'lua> AnyUserData<'lua> {
|
||||
check_stack(state, 5)?;
|
||||
|
||||
lua.push_userdata_ref(&self.0)?;
|
||||
lua.push_value(v.into_lua(lua)?)?;
|
||||
lua.push(v)?;
|
||||
|
||||
// Multiple (extra) user values are emulated by storing them in a table
|
||||
protect_lua!(state, 2, 0, |state| {
|
||||
@@ -1096,21 +1092,40 @@ impl<'lua> AnyUserData<'lua> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Converts this userdata to a generic C pointer.
|
||||
///
|
||||
/// There is no way to convert the pointer back to its original value.
|
||||
///
|
||||
/// Typically this function is used only for hashing and debug information.
|
||||
#[inline]
|
||||
pub fn to_pointer(&self) -> *const c_void {
|
||||
self.0.to_pointer()
|
||||
}
|
||||
|
||||
/// Convert this handle to owned version.
|
||||
#[cfg(all(feature = "unstable", any(not(feature = "send"), doc)))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all(feature = "unstable", not(feature = "send")))))]
|
||||
#[inline]
|
||||
pub fn into_owned(self) -> OwnedAnyUserData {
|
||||
OwnedAnyUserData(self.0.into_owned())
|
||||
OwnedAnyUserData(self.0.into_owned(), self.1)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
#[cfg(feature = "async")]
|
||||
#[inline]
|
||||
pub(crate) fn type_id(&self) -> Result<Option<TypeId>> {
|
||||
unsafe { self.0.lua.get_userdata_type_id(&self.0) }
|
||||
unsafe { self.0.lua.get_userdata_ref_type_id(&self.0) }
|
||||
}
|
||||
|
||||
/// Returns a type name of this `UserData` (from a metatable field).
|
||||
pub(crate) fn type_name(&self) -> Result<Option<StdString>> {
|
||||
match self.1 {
|
||||
SubtypeId::None => {}
|
||||
#[cfg(feature = "luau")]
|
||||
SubtypeId::Buffer => return Ok(Some("buffer".to_owned())),
|
||||
#[cfg(feature = "luajit")]
|
||||
SubtypeId::CData => return Ok(Some("cdata".to_owned())),
|
||||
}
|
||||
|
||||
let lua = self.0.lua;
|
||||
let state = lua.state();
|
||||
unsafe {
|
||||
@@ -1154,13 +1169,13 @@ impl<'lua> AnyUserData<'lua> {
|
||||
Ok(false)
|
||||
}
|
||||
|
||||
/// Returns true if this `AnyUserData` is serializable (eg. was created using `create_ser_userdata`).
|
||||
/// Returns `true` if this `AnyUserData` is serializable (eg. was created using `create_ser_userdata`).
|
||||
#[cfg(feature = "serialize")]
|
||||
pub(crate) fn is_serializable(&self) -> bool {
|
||||
let lua = self.0.lua;
|
||||
let is_serializable = || unsafe {
|
||||
// Userdata can be unregistered or destructed
|
||||
let _ = lua.get_userdata_type_id(&self.0)?;
|
||||
let _ = lua.get_userdata_ref_type_id(&self.0)?;
|
||||
|
||||
let ud = &*get_userdata::<UserDataCell<()>>(lua.ref_thread(), self.0.index);
|
||||
match &*ud.0.try_borrow().map_err(|_| Error::UserDataBorrowError)? {
|
||||
@@ -1178,7 +1193,7 @@ impl<'lua> AnyUserData<'lua> {
|
||||
{
|
||||
let lua = self.0.lua;
|
||||
unsafe {
|
||||
let type_id = lua.get_userdata_type_id(&self.0)?;
|
||||
let type_id = lua.get_userdata_ref_type_id(&self.0)?;
|
||||
match type_id {
|
||||
Some(type_id) if type_id == TypeId::of::<T>() => {
|
||||
let ref_thread = lua.ref_thread();
|
||||
@@ -1325,9 +1340,22 @@ impl<'lua> Serialize for AnyUserData<'lua> {
|
||||
S: Serializer,
|
||||
{
|
||||
let lua = self.0.lua;
|
||||
|
||||
// Special case for Luau buffer type
|
||||
#[cfg(feature = "luau")]
|
||||
if self.1 == SubtypeId::Buffer {
|
||||
let buf = unsafe {
|
||||
let mut size = 0usize;
|
||||
let buf = ffi::lua_tobuffer(lua.ref_thread(), self.0.index, &mut size);
|
||||
mlua_assert!(!buf.is_null(), "invalid Luau buffer");
|
||||
std::slice::from_raw_parts(buf as *const u8, size)
|
||||
};
|
||||
return serializer.serialize_bytes(buf);
|
||||
}
|
||||
|
||||
let data = unsafe {
|
||||
let _ = lua
|
||||
.get_userdata_type_id(&self.0)
|
||||
.get_userdata_ref_type_id(&self.0)
|
||||
.map_err(ser::Error::custom)?;
|
||||
let ud = &*get_userdata::<UserDataCell<()>>(lua.ref_thread(), self.0.index);
|
||||
ud.0.try_borrow()
|
||||
@@ -1343,7 +1371,7 @@ impl<'lua> Serialize for AnyUserData<'lua> {
|
||||
/// A wrapper type for an immutably borrowed value from a `AnyUserData`.
|
||||
///
|
||||
/// It implements [`FromLua`] and can be used to receive a typed userdata from Lua.
|
||||
pub struct UserDataRef<'lua, T: 'static>(AnyUserData<'lua>, Ref<'lua, T>);
|
||||
pub struct UserDataRef<'lua, T: 'static>(#[allow(unused)] AnyUserData<'lua>, Ref<'lua, T>);
|
||||
|
||||
impl<'lua, T: 'static> Deref for UserDataRef<'lua, T> {
|
||||
type Target = T;
|
||||
@@ -1365,7 +1393,7 @@ impl<'lua, T: 'static> UserDataRef<'lua, T> {
|
||||
/// A wrapper type for a mutably borrowed value from a `AnyUserData`.
|
||||
///
|
||||
/// It implements [`FromLua`] and can be used to receive a typed userdata from Lua.
|
||||
pub struct UserDataRefMut<'lua, T: 'static>(AnyUserData<'lua>, RefMut<'lua, T>);
|
||||
pub struct UserDataRefMut<'lua, T: 'static>(#[allow(unused)] AnyUserData<'lua>, RefMut<'lua, T>);
|
||||
|
||||
impl<'lua, T: 'static> Deref for UserDataRefMut<'lua, T> {
|
||||
type Target = T;
|
||||
|
||||
+157
-178
@@ -14,17 +14,13 @@ use crate::userdata::{
|
||||
AnyUserData, MetaMethod, UserData, UserDataCell, UserDataFields, UserDataMethods,
|
||||
};
|
||||
use crate::util::{get_userdata, short_type_name};
|
||||
use crate::value::{FromLua, FromLuaMulti, IntoLua, IntoLuaMulti, MultiValue, Value};
|
||||
use crate::value::{FromLua, FromLuaMulti, IntoLua, IntoLuaMulti, Value};
|
||||
|
||||
#[cfg(not(feature = "send"))]
|
||||
use std::rc::Rc;
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
use {
|
||||
crate::types::AsyncCallback,
|
||||
futures_util::future::{self, TryFutureExt},
|
||||
std::future::Future,
|
||||
};
|
||||
use {crate::types::AsyncCallback, futures_util::future, std::future::Future};
|
||||
|
||||
/// Handle to registry for userdata methods and metamethods.
|
||||
pub struct UserDataRegistry<'lua, T: 'static> {
|
||||
@@ -78,63 +74,61 @@ impl<'lua, T: 'static> UserDataRegistry<'lua, T> {
|
||||
};
|
||||
}
|
||||
|
||||
Box::new(move |lua, mut args| {
|
||||
let front = args
|
||||
.pop_front()
|
||||
.ok_or_else(|| Error::from_lua_conversion("missing argument", "userdata", None));
|
||||
let front = try_self_arg!(front);
|
||||
let call = |ud| {
|
||||
// Self was at index 1, so we pass 2 here
|
||||
let args = A::from_lua_multi_args(args, 2, Some(&name), lua)?;
|
||||
method(lua, ud, args)?.into_lua_multi(lua)
|
||||
};
|
||||
Box::new(move |lua, nargs| unsafe {
|
||||
if nargs == 0 {
|
||||
let err = Error::from_lua_conversion("missing argument", "userdata", None);
|
||||
try_self_arg!(Err(err));
|
||||
}
|
||||
let state = lua.state();
|
||||
// Find absolute "self" index before processing args
|
||||
let index = ffi::lua_absindex(state, -nargs);
|
||||
// Self was at position 1, so we pass 2 here
|
||||
let args = A::from_stack_args(nargs - 1, 2, Some(&name), lua);
|
||||
|
||||
let userdata = try_self_arg!(AnyUserData::from_lua(front, lua));
|
||||
let (ref_thread, index) = (lua.ref_thread(), userdata.0.index);
|
||||
match try_self_arg!(userdata.type_id()) {
|
||||
Some(id) if id == TypeId::of::<T>() => unsafe {
|
||||
let ud = try_self_arg!(get_userdata_ref::<T>(ref_thread, index));
|
||||
call(&ud)
|
||||
},
|
||||
match try_self_arg!(lua.get_userdata_type_id(index)) {
|
||||
Some(id) if id == TypeId::of::<T>() => {
|
||||
let ud = try_self_arg!(get_userdata_ref::<T>(state, index));
|
||||
method(lua, &ud, args?)?.push_into_stack_multi(lua)
|
||||
}
|
||||
#[cfg(not(feature = "send"))]
|
||||
Some(id) if id == TypeId::of::<Rc<T>>() => unsafe {
|
||||
let ud = try_self_arg!(get_userdata_ref::<Rc<T>>(ref_thread, index));
|
||||
call(&ud)
|
||||
},
|
||||
Some(id) if id == TypeId::of::<Rc<T>>() => {
|
||||
let ud = try_self_arg!(get_userdata_ref::<Rc<T>>(state, index));
|
||||
method(lua, &ud, args?)?.push_into_stack_multi(lua)
|
||||
}
|
||||
#[cfg(not(feature = "send"))]
|
||||
Some(id) if id == TypeId::of::<Rc<RefCell<T>>>() => unsafe {
|
||||
let ud = try_self_arg!(get_userdata_ref::<Rc<RefCell<T>>>(ref_thread, index));
|
||||
Some(id) if id == TypeId::of::<Rc<RefCell<T>>>() => {
|
||||
let ud = try_self_arg!(get_userdata_ref::<Rc<RefCell<T>>>(state, index));
|
||||
let ud = try_self_arg!(ud.try_borrow(), Error::UserDataBorrowError);
|
||||
call(&ud)
|
||||
},
|
||||
Some(id) if id == TypeId::of::<Arc<T>>() => unsafe {
|
||||
let ud = try_self_arg!(get_userdata_ref::<Arc<T>>(ref_thread, index));
|
||||
call(&ud)
|
||||
},
|
||||
Some(id) if id == TypeId::of::<Arc<Mutex<T>>>() => unsafe {
|
||||
let ud = try_self_arg!(get_userdata_ref::<Arc<Mutex<T>>>(ref_thread, index));
|
||||
method(lua, &ud, args?)?.push_into_stack_multi(lua)
|
||||
}
|
||||
Some(id) if id == TypeId::of::<Arc<T>>() => {
|
||||
let ud = try_self_arg!(get_userdata_ref::<Arc<T>>(state, index));
|
||||
method(lua, &ud, args?)?.push_into_stack_multi(lua)
|
||||
}
|
||||
Some(id) if id == TypeId::of::<Arc<Mutex<T>>>() => {
|
||||
let ud = try_self_arg!(get_userdata_ref::<Arc<Mutex<T>>>(state, index));
|
||||
let ud = try_self_arg!(ud.try_lock(), Error::UserDataBorrowError);
|
||||
call(&ud)
|
||||
},
|
||||
method(lua, &ud, args?)?.push_into_stack_multi(lua)
|
||||
}
|
||||
#[cfg(feature = "parking_lot")]
|
||||
Some(id) if id == TypeId::of::<Arc<parking_lot::Mutex<T>>>() => unsafe {
|
||||
let ud = get_userdata_ref::<Arc<parking_lot::Mutex<T>>>(ref_thread, index);
|
||||
Some(id) if id == TypeId::of::<Arc<parking_lot::Mutex<T>>>() => {
|
||||
let ud = get_userdata_ref::<Arc<parking_lot::Mutex<T>>>(state, index);
|
||||
let ud = try_self_arg!(ud);
|
||||
let ud = try_self_arg!(ud.try_lock().ok_or(Error::UserDataBorrowError));
|
||||
call(&ud)
|
||||
},
|
||||
Some(id) if id == TypeId::of::<Arc<RwLock<T>>>() => unsafe {
|
||||
let ud = try_self_arg!(get_userdata_ref::<Arc<RwLock<T>>>(ref_thread, index));
|
||||
method(lua, &ud, args?)?.push_into_stack_multi(lua)
|
||||
}
|
||||
Some(id) if id == TypeId::of::<Arc<RwLock<T>>>() => {
|
||||
let ud = try_self_arg!(get_userdata_ref::<Arc<RwLock<T>>>(state, index));
|
||||
let ud = try_self_arg!(ud.try_read(), Error::UserDataBorrowError);
|
||||
call(&ud)
|
||||
},
|
||||
method(lua, &ud, args?)?.push_into_stack_multi(lua)
|
||||
}
|
||||
#[cfg(feature = "parking_lot")]
|
||||
Some(id) if id == TypeId::of::<Arc<parking_lot::RwLock<T>>>() => unsafe {
|
||||
let ud = get_userdata_ref::<Arc<parking_lot::RwLock<T>>>(ref_thread, index);
|
||||
Some(id) if id == TypeId::of::<Arc<parking_lot::RwLock<T>>>() => {
|
||||
let ud = get_userdata_ref::<Arc<parking_lot::RwLock<T>>>(state, index);
|
||||
let ud = try_self_arg!(ud);
|
||||
let ud = try_self_arg!(ud.try_read().ok_or(Error::UserDataBorrowError));
|
||||
call(&ud)
|
||||
},
|
||||
method(lua, &ud, args?)?.push_into_stack_multi(lua)
|
||||
}
|
||||
_ => Err(Error::bad_self_argument(&name, Error::UserDataTypeMismatch)),
|
||||
}
|
||||
})
|
||||
@@ -157,60 +151,58 @@ impl<'lua, T: 'static> UserDataRegistry<'lua, T> {
|
||||
}
|
||||
|
||||
let method = RefCell::new(method);
|
||||
Box::new(move |lua, mut args| {
|
||||
Box::new(move |lua, nargs| unsafe {
|
||||
let mut method = method
|
||||
.try_borrow_mut()
|
||||
.map_err(|_| Error::RecursiveMutCallback)?;
|
||||
let front = args
|
||||
.pop_front()
|
||||
.ok_or_else(|| Error::from_lua_conversion("missing argument", "userdata", None));
|
||||
let front = try_self_arg!(front);
|
||||
let call = |ud| {
|
||||
// Self was at index 1, so we pass 2 here
|
||||
let args = A::from_lua_multi_args(args, 2, Some(&name), lua)?;
|
||||
method(lua, ud, args)?.into_lua_multi(lua)
|
||||
};
|
||||
if nargs == 0 {
|
||||
let err = Error::from_lua_conversion("missing argument", "userdata", None);
|
||||
try_self_arg!(Err(err));
|
||||
}
|
||||
let state = lua.state();
|
||||
// Find absolute "self" index before processing args
|
||||
let index = ffi::lua_absindex(state, -nargs);
|
||||
// Self was at position 1, so we pass 2 here
|
||||
let args = A::from_stack_args(nargs - 1, 2, Some(&name), lua);
|
||||
|
||||
let userdata = try_self_arg!(AnyUserData::from_lua(front, lua));
|
||||
let (ref_thread, index) = (lua.ref_thread(), userdata.0.index);
|
||||
match try_self_arg!(userdata.type_id()) {
|
||||
Some(id) if id == TypeId::of::<T>() => unsafe {
|
||||
let mut ud = try_self_arg!(get_userdata_mut::<T>(ref_thread, index));
|
||||
call(&mut ud)
|
||||
},
|
||||
match try_self_arg!(lua.get_userdata_type_id(index)) {
|
||||
Some(id) if id == TypeId::of::<T>() => {
|
||||
let mut ud = try_self_arg!(get_userdata_mut::<T>(state, index));
|
||||
method(lua, &mut ud, args?)?.push_into_stack_multi(lua)
|
||||
}
|
||||
#[cfg(not(feature = "send"))]
|
||||
Some(id) if id == TypeId::of::<Rc<T>>() => Err(Error::UserDataBorrowMutError),
|
||||
#[cfg(not(feature = "send"))]
|
||||
Some(id) if id == TypeId::of::<Rc<RefCell<T>>>() => unsafe {
|
||||
let ud = try_self_arg!(get_userdata_mut::<Rc<RefCell<T>>>(ref_thread, index));
|
||||
Some(id) if id == TypeId::of::<Rc<RefCell<T>>>() => {
|
||||
let ud = try_self_arg!(get_userdata_mut::<Rc<RefCell<T>>>(state, index));
|
||||
let mut ud = try_self_arg!(ud.try_borrow_mut(), Error::UserDataBorrowMutError);
|
||||
call(&mut ud)
|
||||
},
|
||||
method(lua, &mut ud, args?)?.push_into_stack_multi(lua)
|
||||
}
|
||||
Some(id) if id == TypeId::of::<Arc<T>>() => Err(Error::UserDataBorrowMutError),
|
||||
Some(id) if id == TypeId::of::<Arc<Mutex<T>>>() => unsafe {
|
||||
let ud = try_self_arg!(get_userdata_mut::<Arc<Mutex<T>>>(ref_thread, index));
|
||||
Some(id) if id == TypeId::of::<Arc<Mutex<T>>>() => {
|
||||
let ud = try_self_arg!(get_userdata_mut::<Arc<Mutex<T>>>(state, index));
|
||||
let mut ud = try_self_arg!(ud.try_lock(), Error::UserDataBorrowMutError);
|
||||
call(&mut ud)
|
||||
},
|
||||
method(lua, &mut ud, args?)?.push_into_stack_multi(lua)
|
||||
}
|
||||
#[cfg(feature = "parking_lot")]
|
||||
Some(id) if id == TypeId::of::<Arc<parking_lot::Mutex<T>>>() => unsafe {
|
||||
let ud = get_userdata_mut::<Arc<parking_lot::Mutex<T>>>(ref_thread, index);
|
||||
Some(id) if id == TypeId::of::<Arc<parking_lot::Mutex<T>>>() => {
|
||||
let ud = get_userdata_mut::<Arc<parking_lot::Mutex<T>>>(state, index);
|
||||
let ud = try_self_arg!(ud);
|
||||
let mut ud = try_self_arg!(ud.try_lock().ok_or(Error::UserDataBorrowMutError));
|
||||
call(&mut ud)
|
||||
},
|
||||
Some(id) if id == TypeId::of::<Arc<RwLock<T>>>() => unsafe {
|
||||
let ud = try_self_arg!(get_userdata_mut::<Arc<RwLock<T>>>(ref_thread, index));
|
||||
method(lua, &mut ud, args?)?.push_into_stack_multi(lua)
|
||||
}
|
||||
Some(id) if id == TypeId::of::<Arc<RwLock<T>>>() => {
|
||||
let ud = try_self_arg!(get_userdata_mut::<Arc<RwLock<T>>>(state, index));
|
||||
let mut ud = try_self_arg!(ud.try_write(), Error::UserDataBorrowMutError);
|
||||
call(&mut ud)
|
||||
},
|
||||
method(lua, &mut ud, args?)?.push_into_stack_multi(lua)
|
||||
}
|
||||
#[cfg(feature = "parking_lot")]
|
||||
Some(id) if id == TypeId::of::<Arc<parking_lot::RwLock<T>>>() => unsafe {
|
||||
let ud = get_userdata_mut::<Arc<parking_lot::RwLock<T>>>(ref_thread, index);
|
||||
Some(id) if id == TypeId::of::<Arc<parking_lot::RwLock<T>>>() => {
|
||||
let ud = get_userdata_mut::<Arc<parking_lot::RwLock<T>>>(state, index);
|
||||
let ud = try_self_arg!(ud);
|
||||
let mut ud = try_self_arg!(ud.try_write().ok_or(Error::UserDataBorrowMutError));
|
||||
call(&mut ud)
|
||||
},
|
||||
method(lua, &mut ud, args?)?.push_into_stack_multi(lua)
|
||||
}
|
||||
_ => Err(Error::bad_self_argument(&name, Error::UserDataTypeMismatch)),
|
||||
}
|
||||
})
|
||||
@@ -226,10 +218,10 @@ impl<'lua, T: 'static> UserDataRegistry<'lua, T> {
|
||||
MR: Future<Output = Result<R>> + 's,
|
||||
R: IntoLuaMulti<'lua>,
|
||||
{
|
||||
let name = get_function_name::<T>(name);
|
||||
let name = Arc::new(get_function_name::<T>(name));
|
||||
let method = Arc::new(method);
|
||||
|
||||
Box::new(move |lua, mut args| {
|
||||
Box::new(move |lua, mut args| unsafe {
|
||||
let name = name.clone();
|
||||
let method = method.clone();
|
||||
macro_rules! try_self_arg {
|
||||
@@ -242,76 +234,68 @@ impl<'lua, T: 'static> UserDataRegistry<'lua, T> {
|
||||
}
|
||||
|
||||
Box::pin(async move {
|
||||
let front = args.pop_front().ok_or_else(|| {
|
||||
let this = args.pop_front().ok_or_else(|| {
|
||||
Error::from_lua_conversion("missing argument", "userdata", None)
|
||||
});
|
||||
let front = try_self_arg!(front);
|
||||
let userdata: AnyUserData = try_self_arg!(AnyUserData::from_lua(front, lua));
|
||||
let (ref_thread, index) = (lua.ref_thread(), userdata.0.index);
|
||||
match try_self_arg!(userdata.type_id()) {
|
||||
Some(id) if id == TypeId::of::<T>() => unsafe {
|
||||
let this = try_self_arg!(AnyUserData::from_lua(try_self_arg!(this), lua));
|
||||
let args = A::from_lua_args(args, 2, Some(&name), lua);
|
||||
|
||||
let (ref_thread, index) = (lua.ref_thread(), this.0.index);
|
||||
match try_self_arg!(this.type_id()) {
|
||||
Some(id) if id == TypeId::of::<T>() => {
|
||||
let ud = try_self_arg!(get_userdata_ref::<T>(ref_thread, index));
|
||||
let ud = std::mem::transmute::<&T, &T>(&ud);
|
||||
// Self was at index 1, so we pass 2 here
|
||||
let args = A::from_lua_multi_args(args, 2, Some(&name), lua)?;
|
||||
method(lua, ud, args).await?.into_lua_multi(lua)
|
||||
},
|
||||
method(lua, ud, args?).await?.push_into_stack_multi(lua)
|
||||
}
|
||||
#[cfg(not(feature = "send"))]
|
||||
Some(id) if id == TypeId::of::<Rc<T>>() => unsafe {
|
||||
Some(id) if id == TypeId::of::<Rc<T>>() => {
|
||||
let ud = try_self_arg!(get_userdata_ref::<Rc<T>>(ref_thread, index));
|
||||
let ud = std::mem::transmute::<&T, &T>(&ud);
|
||||
let args = A::from_lua_multi_args(args, 2, Some(&name), lua)?;
|
||||
method(lua, ud, args).await?.into_lua_multi(lua)
|
||||
},
|
||||
method(lua, ud, args?).await?.push_into_stack_multi(lua)
|
||||
}
|
||||
#[cfg(not(feature = "send"))]
|
||||
Some(id) if id == TypeId::of::<Rc<RefCell<T>>>() => unsafe {
|
||||
Some(id) if id == TypeId::of::<Rc<RefCell<T>>>() => {
|
||||
let ud =
|
||||
try_self_arg!(get_userdata_ref::<Rc<RefCell<T>>>(ref_thread, index));
|
||||
let ud = try_self_arg!(ud.try_borrow(), Error::UserDataBorrowError);
|
||||
let ud = std::mem::transmute::<&T, &T>(&ud);
|
||||
let args = A::from_lua_multi_args(args, 2, Some(&name), lua)?;
|
||||
method(lua, ud, args).await?.into_lua_multi(lua)
|
||||
},
|
||||
Some(id) if id == TypeId::of::<Arc<T>>() => unsafe {
|
||||
method(lua, ud, args?).await?.push_into_stack_multi(lua)
|
||||
}
|
||||
Some(id) if id == TypeId::of::<Arc<T>>() => {
|
||||
let ud = try_self_arg!(get_userdata_ref::<Arc<T>>(ref_thread, index));
|
||||
let ud = std::mem::transmute::<&T, &T>(&ud);
|
||||
let args = A::from_lua_multi_args(args, 2, Some(&name), lua)?;
|
||||
method(lua, ud, args).await?.into_lua_multi(lua)
|
||||
},
|
||||
Some(id) if id == TypeId::of::<Arc<Mutex<T>>>() => unsafe {
|
||||
method(lua, ud, args?).await?.push_into_stack_multi(lua)
|
||||
}
|
||||
Some(id) if id == TypeId::of::<Arc<Mutex<T>>>() => {
|
||||
let ud =
|
||||
try_self_arg!(get_userdata_ref::<Arc<Mutex<T>>>(ref_thread, index));
|
||||
let ud = try_self_arg!(ud.try_lock(), Error::UserDataBorrowError);
|
||||
let ud = std::mem::transmute::<&T, &T>(&ud);
|
||||
let args = A::from_lua_multi_args(args, 2, Some(&name), lua)?;
|
||||
method(lua, ud, args).await?.into_lua_multi(lua)
|
||||
},
|
||||
method(lua, ud, args?).await?.push_into_stack_multi(lua)
|
||||
}
|
||||
#[cfg(feature = "parking_lot")]
|
||||
Some(id) if id == TypeId::of::<Arc<parking_lot::Mutex<T>>>() => unsafe {
|
||||
Some(id) if id == TypeId::of::<Arc<parking_lot::Mutex<T>>>() => {
|
||||
let ud = get_userdata_ref::<Arc<parking_lot::Mutex<T>>>(ref_thread, index);
|
||||
let ud = try_self_arg!(ud);
|
||||
let ud = try_self_arg!(ud.try_lock().ok_or(Error::UserDataBorrowError));
|
||||
let ud = std::mem::transmute::<&T, &T>(&ud);
|
||||
let args = A::from_lua_multi_args(args, 2, Some(&name), lua)?;
|
||||
method(lua, ud, args).await?.into_lua_multi(lua)
|
||||
},
|
||||
Some(id) if id == TypeId::of::<Arc<RwLock<T>>>() => unsafe {
|
||||
method(lua, ud, args?).await?.push_into_stack_multi(lua)
|
||||
}
|
||||
Some(id) if id == TypeId::of::<Arc<RwLock<T>>>() => {
|
||||
let ud =
|
||||
try_self_arg!(get_userdata_ref::<Arc<RwLock<T>>>(ref_thread, index));
|
||||
let ud = try_self_arg!(ud.try_read(), Error::UserDataBorrowError);
|
||||
let ud = std::mem::transmute::<&T, &T>(&ud);
|
||||
let args = A::from_lua_multi_args(args, 2, Some(&name), lua)?;
|
||||
method(lua, ud, args).await?.into_lua_multi(lua)
|
||||
},
|
||||
method(lua, ud, args?).await?.push_into_stack_multi(lua)
|
||||
}
|
||||
#[cfg(feature = "parking_lot")]
|
||||
Some(id) if id == TypeId::of::<Arc<parking_lot::RwLock<T>>>() => unsafe {
|
||||
Some(id) if id == TypeId::of::<Arc<parking_lot::RwLock<T>>>() => {
|
||||
let ud = get_userdata_ref::<Arc<parking_lot::RwLock<T>>>(ref_thread, index);
|
||||
let ud = try_self_arg!(ud);
|
||||
let ud = try_self_arg!(ud.try_read().ok_or(Error::UserDataBorrowError));
|
||||
let ud = std::mem::transmute::<&T, &T>(&ud);
|
||||
let args = A::from_lua_multi_args(args, 2, Some(&name), lua)?;
|
||||
method(lua, ud, args).await?.into_lua_multi(lua)
|
||||
},
|
||||
method(lua, ud, args?).await?.push_into_stack_multi(lua)
|
||||
}
|
||||
_ => Err(Error::bad_self_argument(&name, Error::UserDataTypeMismatch)),
|
||||
}
|
||||
})
|
||||
@@ -328,10 +312,10 @@ impl<'lua, T: 'static> UserDataRegistry<'lua, T> {
|
||||
MR: Future<Output = Result<R>> + 's,
|
||||
R: IntoLuaMulti<'lua>,
|
||||
{
|
||||
let name = get_function_name::<T>(name);
|
||||
let name = Arc::new(get_function_name::<T>(name));
|
||||
let method = Arc::new(method);
|
||||
|
||||
Box::new(move |lua, mut args| {
|
||||
Box::new(move |lua, mut args| unsafe {
|
||||
let name = name.clone();
|
||||
let method = method.clone();
|
||||
macro_rules! try_self_arg {
|
||||
@@ -344,72 +328,66 @@ impl<'lua, T: 'static> UserDataRegistry<'lua, T> {
|
||||
}
|
||||
|
||||
Box::pin(async move {
|
||||
let front = args.pop_front().ok_or_else(|| {
|
||||
let this = args.pop_front().ok_or_else(|| {
|
||||
Error::from_lua_conversion("missing argument", "userdata", None)
|
||||
});
|
||||
let front = try_self_arg!(front);
|
||||
let userdata: AnyUserData = try_self_arg!(AnyUserData::from_lua(front, lua));
|
||||
let (ref_thread, index) = (lua.ref_thread(), userdata.0.index);
|
||||
match try_self_arg!(userdata.type_id()) {
|
||||
Some(id) if id == TypeId::of::<T>() => unsafe {
|
||||
let this = try_self_arg!(AnyUserData::from_lua(try_self_arg!(this), lua));
|
||||
let args = A::from_lua_args(args, 2, Some(&name), lua);
|
||||
|
||||
let (ref_thread, index) = (lua.ref_thread(), this.0.index);
|
||||
match try_self_arg!(this.type_id()) {
|
||||
Some(id) if id == TypeId::of::<T>() => {
|
||||
let mut ud = try_self_arg!(get_userdata_mut::<T>(ref_thread, index));
|
||||
let ud = std::mem::transmute::<&mut T, &mut T>(&mut ud);
|
||||
// Self was at index 1, so we pass 2 here
|
||||
let args = A::from_lua_multi_args(args, 2, Some(&name), lua)?;
|
||||
method(lua, ud, args).await?.into_lua_multi(lua)
|
||||
},
|
||||
method(lua, ud, args?).await?.push_into_stack_multi(lua)
|
||||
}
|
||||
#[cfg(not(feature = "send"))]
|
||||
Some(id) if id == TypeId::of::<Rc<RefCell<T>>>() => {
|
||||
Err(Error::UserDataBorrowMutError)
|
||||
}
|
||||
#[cfg(not(feature = "send"))]
|
||||
Some(id) if id == TypeId::of::<Rc<RefCell<T>>>() => unsafe {
|
||||
Some(id) if id == TypeId::of::<Rc<RefCell<T>>>() => {
|
||||
let ud =
|
||||
try_self_arg!(get_userdata_mut::<Rc<RefCell<T>>>(ref_thread, index));
|
||||
let mut ud =
|
||||
try_self_arg!(ud.try_borrow_mut(), Error::UserDataBorrowMutError);
|
||||
let ud = std::mem::transmute::<&mut T, &mut T>(&mut ud);
|
||||
let args = A::from_lua_multi_args(args, 2, Some(&name), lua)?;
|
||||
method(lua, ud, args).await?.into_lua_multi(lua)
|
||||
},
|
||||
method(lua, ud, args?).await?.push_into_stack_multi(lua)
|
||||
}
|
||||
#[cfg(not(feature = "send"))]
|
||||
Some(id) if id == TypeId::of::<Arc<T>>() => Err(Error::UserDataBorrowMutError),
|
||||
Some(id) if id == TypeId::of::<Arc<Mutex<T>>>() => unsafe {
|
||||
Some(id) if id == TypeId::of::<Arc<Mutex<T>>>() => {
|
||||
let ud =
|
||||
try_self_arg!(get_userdata_mut::<Arc<Mutex<T>>>(ref_thread, index));
|
||||
let mut ud = try_self_arg!(ud.try_lock(), Error::UserDataBorrowMutError);
|
||||
let ud = std::mem::transmute::<&mut T, &mut T>(&mut ud);
|
||||
let args = A::from_lua_multi_args(args, 2, Some(&name), lua)?;
|
||||
method(lua, ud, args).await?.into_lua_multi(lua)
|
||||
},
|
||||
method(lua, ud, args?).await?.push_into_stack_multi(lua)
|
||||
}
|
||||
#[cfg(feature = "parking_lot")]
|
||||
Some(id) if id == TypeId::of::<Arc<parking_lot::Mutex<T>>>() => unsafe {
|
||||
Some(id) if id == TypeId::of::<Arc<parking_lot::Mutex<T>>>() => {
|
||||
let ud = get_userdata_mut::<Arc<parking_lot::Mutex<T>>>(ref_thread, index);
|
||||
let ud = try_self_arg!(ud);
|
||||
let mut ud =
|
||||
try_self_arg!(ud.try_lock().ok_or(Error::UserDataBorrowMutError));
|
||||
let ud = std::mem::transmute::<&mut T, &mut T>(&mut ud);
|
||||
let args = A::from_lua_multi_args(args, 2, Some(&name), lua)?;
|
||||
method(lua, ud, args).await?.into_lua_multi(lua)
|
||||
},
|
||||
Some(id) if id == TypeId::of::<Arc<RwLock<T>>>() => unsafe {
|
||||
method(lua, ud, args?).await?.push_into_stack_multi(lua)
|
||||
}
|
||||
Some(id) if id == TypeId::of::<Arc<RwLock<T>>>() => {
|
||||
let ud =
|
||||
try_self_arg!(get_userdata_mut::<Arc<RwLock<T>>>(ref_thread, index));
|
||||
let mut ud = try_self_arg!(ud.try_write(), Error::UserDataBorrowMutError);
|
||||
let ud = std::mem::transmute::<&mut T, &mut T>(&mut ud);
|
||||
let args = A::from_lua_multi_args(args, 2, Some(&name), lua)?;
|
||||
method(lua, ud, args).await?.into_lua_multi(lua)
|
||||
},
|
||||
method(lua, ud, args?).await?.push_into_stack_multi(lua)
|
||||
}
|
||||
#[cfg(feature = "parking_lot")]
|
||||
Some(id) if id == TypeId::of::<Arc<parking_lot::RwLock<T>>>() => unsafe {
|
||||
Some(id) if id == TypeId::of::<Arc<parking_lot::RwLock<T>>>() => {
|
||||
let ud = get_userdata_mut::<Arc<parking_lot::RwLock<T>>>(ref_thread, index);
|
||||
let ud = try_self_arg!(ud);
|
||||
let mut ud =
|
||||
try_self_arg!(ud.try_write().ok_or(Error::UserDataBorrowMutError));
|
||||
let ud = std::mem::transmute::<&mut T, &mut T>(&mut ud);
|
||||
let args = A::from_lua_multi_args(args, 2, Some(&name), lua)?;
|
||||
method(lua, ud, args).await?.into_lua_multi(lua)
|
||||
},
|
||||
method(lua, ud, args?).await?.push_into_stack_multi(lua)
|
||||
}
|
||||
_ => Err(Error::bad_self_argument(&name, Error::UserDataTypeMismatch)),
|
||||
}
|
||||
})
|
||||
@@ -423,8 +401,9 @@ impl<'lua, T: 'static> UserDataRegistry<'lua, T> {
|
||||
R: IntoLuaMulti<'lua>,
|
||||
{
|
||||
let name = get_function_name::<T>(name);
|
||||
Box::new(move |lua, args| {
|
||||
function(lua, A::from_lua_multi_args(args, 1, Some(&name), lua)?)?.into_lua_multi(lua)
|
||||
Box::new(move |lua, nargs| unsafe {
|
||||
let args = A::from_stack_args(nargs, 1, Some(&name), lua)?;
|
||||
function(lua, args)?.push_into_stack_multi(lua)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -436,11 +415,12 @@ impl<'lua, T: 'static> UserDataRegistry<'lua, T> {
|
||||
{
|
||||
let name = get_function_name::<T>(name);
|
||||
let function = RefCell::new(function);
|
||||
Box::new(move |lua, args| {
|
||||
Box::new(move |lua, nargs| unsafe {
|
||||
let function = &mut *function
|
||||
.try_borrow_mut()
|
||||
.map_err(|_| Error::RecursiveMutCallback)?;
|
||||
function(lua, A::from_lua_multi_args(args, 1, Some(&name), lua)?)?.into_lua_multi(lua)
|
||||
let args = A::from_stack_args(nargs, 1, Some(&name), lua)?;
|
||||
function(lua, args)?.push_into_stack_multi(lua)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -453,22 +433,17 @@ impl<'lua, T: 'static> UserDataRegistry<'lua, T> {
|
||||
R: IntoLuaMulti<'lua>,
|
||||
{
|
||||
let name = get_function_name::<T>(name);
|
||||
Box::new(move |lua, args| {
|
||||
let args = match A::from_lua_multi_args(args, 1, Some(&name), lua) {
|
||||
Box::new(move |lua, args| unsafe {
|
||||
let args = match A::from_lua_args(args, 1, Some(&name), lua) {
|
||||
Ok(args) => args,
|
||||
Err(e) => return Box::pin(future::err(e)),
|
||||
};
|
||||
Box::pin(
|
||||
function(lua, args).and_then(move |ret| future::ready(ret.into_lua_multi(lua))),
|
||||
)
|
||||
let fut = function(lua, args);
|
||||
Box::pin(async move { fut.await?.push_into_stack_multi(lua) })
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn check_meta_field<V>(
|
||||
lua: &'lua Lua,
|
||||
name: &str,
|
||||
value: V,
|
||||
) -> Result<MultiValue<'lua>>
|
||||
pub(crate) fn check_meta_field<V>(lua: &'lua Lua, name: &str, value: V) -> Result<Value<'lua>>
|
||||
where
|
||||
V: IntoLua<'lua>,
|
||||
{
|
||||
@@ -485,7 +460,7 @@ impl<'lua, T: 'static> UserDataRegistry<'lua, T> {
|
||||
}
|
||||
}
|
||||
}
|
||||
value.into_lua_multi(lua)
|
||||
value.into_lua(lua)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -502,7 +477,7 @@ impl<'lua, T: 'static> UserDataFields<'lua, T> for UserDataRegistry<'lua, T> {
|
||||
let name = name.as_ref().to_string();
|
||||
self.fields.push((
|
||||
name,
|
||||
Box::new(move |lua, _| value.clone().into_lua_multi(lua)),
|
||||
Box::new(move |lua, _| unsafe { value.clone().push_into_stack_multi(lua) }),
|
||||
));
|
||||
}
|
||||
|
||||
@@ -554,7 +529,9 @@ impl<'lua, T: 'static> UserDataFields<'lua, T> for UserDataRegistry<'lua, T> {
|
||||
let name2 = name.clone();
|
||||
self.meta_fields.push((
|
||||
name,
|
||||
Box::new(move |lua, _| Self::check_meta_field(lua, &name2, value.clone())),
|
||||
Box::new(move |lua, _| unsafe {
|
||||
Self::check_meta_field(lua, &name2, value.clone())?.push_into_stack_multi(lua)
|
||||
}),
|
||||
));
|
||||
}
|
||||
|
||||
@@ -567,7 +544,9 @@ impl<'lua, T: 'static> UserDataFields<'lua, T> for UserDataRegistry<'lua, T> {
|
||||
let name2 = name.clone();
|
||||
self.meta_fields.push((
|
||||
name,
|
||||
Box::new(move |lua, _| Self::check_meta_field(lua, &name2, f(lua)?)),
|
||||
Box::new(move |lua, _| unsafe {
|
||||
Self::check_meta_field(lua, &name2, f(lua)?)?.push_into_stack_multi(lua)
|
||||
}),
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
+35
-33
@@ -6,7 +6,7 @@ use std::mem::MaybeUninit;
|
||||
use std::os::raw::{c_char, c_int, c_void};
|
||||
use std::panic::{catch_unwind, resume_unwind, AssertUnwindSafe};
|
||||
use std::sync::Arc;
|
||||
use std::{mem, ptr, slice, str};
|
||||
use std::{ptr, slice, str};
|
||||
|
||||
use once_cell::sync::Lazy;
|
||||
use rustc_hash::FxHashMap;
|
||||
@@ -63,6 +63,11 @@ impl StackGuard {
|
||||
top: ffi::lua_gettop(state),
|
||||
}
|
||||
}
|
||||
|
||||
// Same as `new()`, but allows specifying the expected stack size at the end of the scope.
|
||||
pub const fn with_top(state: *mut ffi::lua_State, top: c_int) -> StackGuard {
|
||||
StackGuard { state, top }
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for StackGuard {
|
||||
@@ -248,6 +253,20 @@ pub unsafe fn push_string(state: *mut ffi::lua_State, s: &[u8], protect: bool) -
|
||||
}
|
||||
}
|
||||
|
||||
// Uses 3 stack spaces (when protect), does not call checkstack.
|
||||
#[cfg(feature = "luau")]
|
||||
#[inline(always)]
|
||||
pub unsafe fn push_buffer(state: *mut ffi::lua_State, b: &[u8], protect: bool) -> Result<()> {
|
||||
let data = if protect {
|
||||
protect_lua!(state, 0, 1, |state| ffi::lua_newbuffer(state, b.len()))?
|
||||
} else {
|
||||
ffi::lua_newbuffer(state, b.len())
|
||||
};
|
||||
let buf = slice::from_raw_parts_mut(data as *mut u8, b.len());
|
||||
buf.copy_from_slice(b);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Uses 3 stack spaces, does not call checkstack.
|
||||
#[inline]
|
||||
pub unsafe fn push_table(
|
||||
@@ -277,38 +296,23 @@ pub unsafe fn rawset_field(state: *mut ffi::lua_State, table: c_int, field: &str
|
||||
}
|
||||
|
||||
// Internally uses 3 stack spaces, does not call checkstack.
|
||||
#[cfg(not(feature = "luau"))]
|
||||
#[inline]
|
||||
pub unsafe fn push_userdata<T>(state: *mut ffi::lua_State, t: T, protect: bool) -> Result<()> {
|
||||
#[cfg(not(feature = "luau"))]
|
||||
let ud = if protect {
|
||||
protect_lua!(state, 0, 1, |state| {
|
||||
ffi::lua_newuserdata(state, mem::size_of::<T>()) as *mut T
|
||||
ffi::lua_newuserdata(state, std::mem::size_of::<T>()) as *mut T
|
||||
})?
|
||||
} else {
|
||||
ffi::lua_newuserdata(state, mem::size_of::<T>()) as *mut T
|
||||
ffi::lua_newuserdata(state, std::mem::size_of::<T>()) as *mut T
|
||||
};
|
||||
ptr::write(ud, t);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Internally uses 3 stack spaces, does not call checkstack.
|
||||
#[cfg(feature = "luau")]
|
||||
#[inline]
|
||||
pub unsafe fn push_userdata<T>(state: *mut ffi::lua_State, t: T, protect: bool) -> Result<()> {
|
||||
unsafe extern "C-unwind" fn destructor<T>(ud: *mut c_void) {
|
||||
ptr::drop_in_place(ud as *mut T);
|
||||
}
|
||||
|
||||
let size = mem::size_of::<T>();
|
||||
#[cfg(feature = "luau")]
|
||||
let ud = if protect {
|
||||
protect_lua!(state, 0, 1, |state| {
|
||||
ffi::lua_newuserdatadtor(state, size, destructor::<T>) as *mut T
|
||||
})?
|
||||
protect_lua!(state, 0, 1, |state| { ffi::lua_newuserdata_t::<T>(state) })?
|
||||
} else {
|
||||
ffi::lua_newuserdatadtor(state, size, destructor::<T>) as *mut T
|
||||
ffi::lua_newuserdata_t::<T>(state)
|
||||
};
|
||||
ptr::write(ud, t);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -323,10 +327,10 @@ pub unsafe fn push_userdata_uv<T>(
|
||||
) -> Result<()> {
|
||||
let ud = if protect {
|
||||
protect_lua!(state, 0, 1, |state| {
|
||||
ffi::lua_newuserdatauv(state, mem::size_of::<T>(), nuvalue) as *mut T
|
||||
ffi::lua_newuserdatauv(state, std::mem::size_of::<T>(), nuvalue) as *mut T
|
||||
})?
|
||||
} else {
|
||||
ffi::lua_newuserdatauv(state, mem::size_of::<T>(), nuvalue) as *mut T
|
||||
ffi::lua_newuserdatauv(state, std::mem::size_of::<T>(), nuvalue) as *mut T
|
||||
};
|
||||
ptr::write(ud, t);
|
||||
Ok(())
|
||||
@@ -938,7 +942,7 @@ pub unsafe fn init_error_registry(state: *mut ffi::lua_State) -> Result<()> {
|
||||
"__mod",
|
||||
"__pow",
|
||||
"__unm",
|
||||
#[cfg(any(feature = "lua54", feature = "lua53"))]
|
||||
#[cfg(any(feature = "lua54", feature = "lua53", feature = "luau"))]
|
||||
"__idiv",
|
||||
#[cfg(any(feature = "lua54", feature = "lua53"))]
|
||||
"__band",
|
||||
@@ -1004,16 +1008,10 @@ pub(crate) enum WrappedFailure {
|
||||
|
||||
impl WrappedFailure {
|
||||
pub(crate) unsafe fn new_userdata(state: *mut ffi::lua_State) -> *mut Self {
|
||||
let size = mem::size_of::<WrappedFailure>();
|
||||
#[cfg(feature = "luau")]
|
||||
let ud = {
|
||||
unsafe extern "C-unwind" fn destructor(p: *mut c_void) {
|
||||
ptr::drop_in_place(p as *mut WrappedFailure);
|
||||
}
|
||||
ffi::lua_newuserdatadtor(state, size, destructor) as *mut Self
|
||||
};
|
||||
let ud = ffi::lua_newuserdata_t::<Self>(state);
|
||||
#[cfg(not(feature = "luau"))]
|
||||
let ud = ffi::lua_newuserdata(state, size) as *mut Self;
|
||||
let ud = ffi::lua_newuserdata(state, std::mem::size_of::<Self>()) as *mut Self;
|
||||
ptr::write(ud, WrappedFailure::None);
|
||||
ud
|
||||
}
|
||||
@@ -1059,6 +1057,10 @@ pub(crate) unsafe fn to_string(state: *mut ffi::lua_State, index: c_int) -> Stri
|
||||
ffi::LUA_TFUNCTION => format!("<function {:?}>", ffi::lua_topointer(state, index)),
|
||||
ffi::LUA_TUSERDATA => format!("<userdata {:?}>", ffi::lua_topointer(state, index)),
|
||||
ffi::LUA_TTHREAD => format!("<thread {:?}>", ffi::lua_topointer(state, index)),
|
||||
#[cfg(feature = "luau")]
|
||||
ffi::LUA_TBUFFER => format!("<buffer {:?}>", ffi::lua_topointer(state, index)),
|
||||
#[cfg(feature = "luajit")]
|
||||
ffi::LUA_TCDATA => format!("<cdata {:?}>", ffi::lua_topointer(state, index)),
|
||||
_ => "<unknown>".to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
+527
-64
@@ -1,17 +1,21 @@
|
||||
use std::borrow::Cow;
|
||||
use std::cmp::Ordering;
|
||||
use std::collections::HashSet;
|
||||
use std::iter::{self, FromIterator};
|
||||
use std::iter;
|
||||
use std::ops::Index;
|
||||
use std::os::raw::c_void;
|
||||
use std::os::raw::{c_int, c_void};
|
||||
use std::string::String as StdString;
|
||||
use std::sync::Arc;
|
||||
use std::{fmt, ptr, slice, str, vec};
|
||||
use std::{fmt, mem, ptr, slice, str, vec};
|
||||
|
||||
use num_traits::FromPrimitive;
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
use {
|
||||
crate::table::SerializableTable,
|
||||
rustc_hash::FxHashSet,
|
||||
serde::ser::{self, Serialize, Serializer},
|
||||
std::convert::TryInto,
|
||||
std::result::Result as StdResult,
|
||||
std::{cell::RefCell, rc::Rc, result::Result as StdResult},
|
||||
};
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
@@ -20,7 +24,7 @@ use crate::lua::Lua;
|
||||
use crate::string::String;
|
||||
use crate::table::Table;
|
||||
use crate::thread::Thread;
|
||||
use crate::types::{Integer, LightUserData, Number};
|
||||
use crate::types::{Integer, LightUserData, Number, SubtypeId};
|
||||
use crate::userdata::AnyUserData;
|
||||
use crate::util::{check_stack, StackGuard};
|
||||
|
||||
@@ -84,7 +88,11 @@ impl<'lua> Value<'lua> {
|
||||
Value::Table(_) => "table",
|
||||
Value::Function(_) => "function",
|
||||
Value::Thread(_) => "thread",
|
||||
Value::UserData(_) => "userdata",
|
||||
Value::UserData(AnyUserData(_, SubtypeId::None)) => "userdata",
|
||||
#[cfg(feature = "luau")]
|
||||
Value::UserData(AnyUserData(_, SubtypeId::Buffer)) => "buffer",
|
||||
#[cfg(feature = "luajit")]
|
||||
Value::UserData(AnyUserData(_, SubtypeId::CData)) => "cdata",
|
||||
Value::Error(_) => "error",
|
||||
}
|
||||
}
|
||||
@@ -121,8 +129,8 @@ impl<'lua> Value<'lua> {
|
||||
Value::String(String(r))
|
||||
| Value::Table(Table(r))
|
||||
| Value::Function(Function(r))
|
||||
| Value::Thread(Thread(r))
|
||||
| Value::UserData(AnyUserData(r)) => r.to_pointer(),
|
||||
| Value::Thread(Thread(r, ..))
|
||||
| Value::UserData(AnyUserData(r, ..)) => r.to_pointer(),
|
||||
_ => ptr::null(),
|
||||
}
|
||||
}
|
||||
@@ -143,8 +151,8 @@ impl<'lua> Value<'lua> {
|
||||
Value::String(s) => Ok(s.to_str()?.to_string()),
|
||||
Value::Table(Table(r))
|
||||
| Value::Function(Function(r))
|
||||
| Value::Thread(Thread(r))
|
||||
| Value::UserData(AnyUserData(r)) => unsafe {
|
||||
| Value::Thread(Thread(r, ..))
|
||||
| Value::UserData(AnyUserData(r, ..)) => unsafe {
|
||||
let state = r.lua.state();
|
||||
let _guard = StackGuard::new(state);
|
||||
check_stack(state, 3)?;
|
||||
@@ -159,6 +167,284 @@ impl<'lua> Value<'lua> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `true` if the value is a [`Nil`].
|
||||
#[inline]
|
||||
pub fn is_nil(&self) -> bool {
|
||||
self == &Nil
|
||||
}
|
||||
|
||||
/// Returns `true` if the value is a [`NULL`].
|
||||
#[inline]
|
||||
pub fn is_null(&self) -> bool {
|
||||
self == &Self::NULL
|
||||
}
|
||||
|
||||
/// Returns `true` if the value is a boolean.
|
||||
#[inline]
|
||||
pub fn is_boolean(&self) -> bool {
|
||||
self.as_boolean().is_some()
|
||||
}
|
||||
|
||||
/// Cast the value to boolean.
|
||||
///
|
||||
/// If the value is a Boolean, returns it or `None` otherwise.
|
||||
#[inline]
|
||||
pub fn as_boolean(&self) -> Option<bool> {
|
||||
match *self {
|
||||
Value::Boolean(b) => Some(b),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `true` if the value is a [`LightUserData`].
|
||||
#[inline]
|
||||
pub fn is_light_userdata(&self) -> bool {
|
||||
self.as_light_userdata().is_some()
|
||||
}
|
||||
|
||||
/// Cast the value to [`LightUserData`].
|
||||
///
|
||||
/// If the value is a [`LightUserData`], returns it or `None` otherwise.
|
||||
#[inline]
|
||||
pub fn as_light_userdata(&self) -> Option<LightUserData> {
|
||||
match *self {
|
||||
Value::LightUserData(l) => Some(l),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `true` if the value is an [`Integer`].
|
||||
#[inline]
|
||||
pub fn is_integer(&self) -> bool {
|
||||
self.as_integer().is_some()
|
||||
}
|
||||
|
||||
/// Cast the value to [`Integer`].
|
||||
///
|
||||
/// If the value is a Lua [`Integer`], returns it or `None` otherwise.
|
||||
#[inline]
|
||||
pub fn as_integer(&self) -> Option<Integer> {
|
||||
match *self {
|
||||
Value::Integer(i) => Some(i),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Cast the value to `i32`.
|
||||
///
|
||||
/// If the value is a Lua [`Integer`], try to convert it to `i32` or return `None` otherwise.
|
||||
#[inline]
|
||||
pub fn as_i32(&self) -> Option<i32> {
|
||||
#[allow(clippy::useless_conversion)]
|
||||
self.as_integer().and_then(|i| i32::try_from(i).ok())
|
||||
}
|
||||
|
||||
/// Cast the value to `u32`.
|
||||
///
|
||||
/// If the value is a Lua [`Integer`], try to convert it to `u32` or return `None` otherwise.
|
||||
#[inline]
|
||||
pub fn as_u32(&self) -> Option<u32> {
|
||||
self.as_integer().and_then(|i| u32::try_from(i).ok())
|
||||
}
|
||||
|
||||
/// Cast the value to `i64`.
|
||||
///
|
||||
/// If the value is a Lua [`Integer`], try to convert it to `i64` or return `None` otherwise.
|
||||
#[inline]
|
||||
pub fn as_i64(&self) -> Option<i64> {
|
||||
self.as_integer().map(i64::from)
|
||||
}
|
||||
|
||||
/// Cast the value to `u64`.
|
||||
///
|
||||
/// If the value is a Lua [`Integer`], try to convert it to `u64` or return `None` otherwise.
|
||||
#[inline]
|
||||
pub fn as_u64(&self) -> Option<u64> {
|
||||
self.as_integer().and_then(|i| u64::try_from(i).ok())
|
||||
}
|
||||
|
||||
/// Cast the value to `isize`.
|
||||
///
|
||||
/// If the value is a Lua [`Integer`], try to convert it to `isize` or return `None` otherwise.
|
||||
#[inline]
|
||||
pub fn as_isize(&self) -> Option<isize> {
|
||||
self.as_integer().and_then(|i| isize::try_from(i).ok())
|
||||
}
|
||||
|
||||
/// Cast the value to `usize`.
|
||||
///
|
||||
/// If the value is a Lua [`Integer`], try to convert it to `usize` or return `None` otherwise.
|
||||
#[inline]
|
||||
pub fn as_usize(&self) -> Option<usize> {
|
||||
self.as_integer().and_then(|i| usize::try_from(i).ok())
|
||||
}
|
||||
|
||||
/// Returns `true` if the value is a Lua [`Number`].
|
||||
#[inline]
|
||||
pub fn is_number(&self) -> bool {
|
||||
self.as_number().is_some()
|
||||
}
|
||||
|
||||
/// Cast the value to [`Number`].
|
||||
///
|
||||
/// If the value is a Lua [`Number`], returns it or `None` otherwise.
|
||||
#[inline]
|
||||
pub fn as_number(&self) -> Option<Number> {
|
||||
match *self {
|
||||
Value::Number(n) => Some(n),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Cast the value to `f32`.
|
||||
///
|
||||
/// If the value is a Lua [`Number`], try to convert it to `f32` or return `None` otherwise.
|
||||
#[inline]
|
||||
pub fn as_f32(&self) -> Option<f32> {
|
||||
self.as_number().and_then(f32::from_f64)
|
||||
}
|
||||
|
||||
/// Cast the value to `f64`.
|
||||
///
|
||||
/// If the value is a Lua [`Number`], try to convert it to `f64` or return `None` otherwise.
|
||||
#[inline]
|
||||
pub fn as_f64(&self) -> Option<f64> {
|
||||
self.as_number()
|
||||
}
|
||||
|
||||
/// Returns `true` if the value is a Lua [`String`].
|
||||
#[inline]
|
||||
pub fn is_string(&self) -> bool {
|
||||
self.as_string().is_some()
|
||||
}
|
||||
|
||||
/// Cast the value to Lua [`String`].
|
||||
///
|
||||
/// If the value is a Lua [`String`], returns it or `None` otherwise.
|
||||
#[inline]
|
||||
pub fn as_string(&self) -> Option<&String> {
|
||||
match self {
|
||||
Value::String(s) => Some(s),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Cast the value to [`str`].
|
||||
///
|
||||
/// If the value is a Lua [`String`], try to convert it to [`str`] or return `None` otherwise.
|
||||
#[inline]
|
||||
pub fn as_str(&self) -> Option<&str> {
|
||||
self.as_string().and_then(|s| s.to_str().ok())
|
||||
}
|
||||
|
||||
/// Cast the value to [`Cow<str>`].
|
||||
///
|
||||
/// If the value is a Lua [`String`], converts it to [`Cow<str>`] or returns `None` otherwise.
|
||||
#[inline]
|
||||
pub fn as_string_lossy(&self) -> Option<Cow<str>> {
|
||||
self.as_string().map(|s| s.to_string_lossy())
|
||||
}
|
||||
|
||||
/// Returns `true` if the value is a Lua [`Table`].
|
||||
#[inline]
|
||||
pub fn is_table(&self) -> bool {
|
||||
self.as_table().is_some()
|
||||
}
|
||||
|
||||
/// Cast the value to [`Table`].
|
||||
///
|
||||
/// If the value is a Lua [`Table`], returns it or `None` otherwise.
|
||||
#[inline]
|
||||
pub fn as_table(&self) -> Option<&Table> {
|
||||
match self {
|
||||
Value::Table(t) => Some(t),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `true` if the value is a Lua [`Thread`].
|
||||
#[inline]
|
||||
pub fn is_thread(&self) -> bool {
|
||||
self.as_thread().is_some()
|
||||
}
|
||||
|
||||
/// Cast the value to [`Thread`].
|
||||
///
|
||||
/// If the value is a Lua [`Thread`], returns it or `None` otherwise.
|
||||
#[inline]
|
||||
pub fn as_thread(&self) -> Option<&Thread> {
|
||||
match self {
|
||||
Value::Thread(t) => Some(t),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `true` if the value is a Lua [`Function`].
|
||||
#[inline]
|
||||
pub fn is_function(&self) -> bool {
|
||||
self.as_function().is_some()
|
||||
}
|
||||
|
||||
/// Cast the value to [`Function`].
|
||||
///
|
||||
/// If the value is a Lua [`Function`], returns it or `None` otherwise.
|
||||
#[inline]
|
||||
pub fn as_function(&self) -> Option<&Function> {
|
||||
match self {
|
||||
Value::Function(f) => Some(f),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `true` if the value is an [`AnyUserData`].
|
||||
#[inline]
|
||||
pub fn is_userdata(&self) -> bool {
|
||||
self.as_userdata().is_some()
|
||||
}
|
||||
|
||||
/// Cast the value to [`AnyUserData`].
|
||||
///
|
||||
/// If the value is an [`AnyUserData`], returns it or `None` otherwise.
|
||||
#[inline]
|
||||
pub fn as_userdata(&self) -> Option<&AnyUserData> {
|
||||
match self {
|
||||
Value::UserData(ud) => Some(ud),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `true` if the value is a Buffer wrapped in [`AnyUserData`].
|
||||
#[cfg(any(feature = "luau", doc))]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
|
||||
#[doc(hidden)]
|
||||
#[inline]
|
||||
pub fn is_buffer(&self) -> bool {
|
||||
self.as_userdata()
|
||||
.map(|ud| ud.1 == SubtypeId::Buffer)
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
/// Returns `true` if the value is a CData wrapped in [`AnyUserData`].
|
||||
#[cfg(any(feature = "luajit", doc))]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "luajit")))]
|
||||
#[doc(hidden)]
|
||||
#[inline]
|
||||
pub fn is_cdata(&self) -> bool {
|
||||
self.as_userdata()
|
||||
.map(|ud| ud.1 == SubtypeId::CData)
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
/// Wrap reference to this Value into [`SerializableValue`].
|
||||
///
|
||||
/// This allows customizing serialization behavior using serde.
|
||||
#[cfg(feature = "serialize")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "serialize")))]
|
||||
#[doc(hidden)]
|
||||
pub fn to_serializable(&self) -> SerializableValue<'_, 'lua> {
|
||||
SerializableValue::new(self, Default::default(), None)
|
||||
}
|
||||
|
||||
// Compares two values.
|
||||
// Used to sort values for Debug printing.
|
||||
pub(crate) fn cmp(&self, other: &Self) -> Ordering {
|
||||
@@ -289,37 +575,132 @@ impl<'lua> AsRef<Value<'lua>> for Value<'lua> {
|
||||
}
|
||||
}
|
||||
|
||||
/// A wrapped [`Value`] with customized serialization behavior.
|
||||
#[cfg(feature = "serialize")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "serialize")))]
|
||||
pub struct SerializableValue<'a, 'lua> {
|
||||
value: &'a Value<'lua>,
|
||||
options: crate::serde::de::Options,
|
||||
// In many cases we don't need `visited` map, so don't allocate memory by default
|
||||
visited: Option<Rc<RefCell<FxHashSet<*const c_void>>>>,
|
||||
}
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
impl<'lua> Serialize for Value<'lua> {
|
||||
#[inline]
|
||||
fn serialize<S: Serializer>(&self, serializer: S) -> StdResult<S::Ok, S::Error> {
|
||||
SerializableValue::new(self, Default::default(), None).serialize(serializer)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
impl<'a, 'lua> SerializableValue<'a, 'lua> {
|
||||
#[inline]
|
||||
pub(crate) fn new(
|
||||
value: &'a Value<'lua>,
|
||||
options: crate::serde::de::Options,
|
||||
visited: Option<&Rc<RefCell<FxHashSet<*const c_void>>>>,
|
||||
) -> Self {
|
||||
if let Value::Table(_) = value {
|
||||
return Self {
|
||||
value,
|
||||
options,
|
||||
// We need to always initialize the `visited` map for Tables
|
||||
visited: visited.cloned().or_else(|| Some(Default::default())),
|
||||
};
|
||||
}
|
||||
Self {
|
||||
value,
|
||||
options,
|
||||
visited: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// If true, an attempt to serialize types such as [`Function`], [`Thread`], [`LightUserData`]
|
||||
/// and [`Error`] will cause an error.
|
||||
/// Otherwise these types skipped when iterating or serialized as unit type.
|
||||
///
|
||||
/// Default: **true**
|
||||
#[must_use]
|
||||
pub const fn deny_unsupported_types(mut self, enabled: bool) -> Self {
|
||||
self.options.deny_unsupported_types = enabled;
|
||||
self
|
||||
}
|
||||
|
||||
/// If true, an attempt to serialize a recursive table (table that refers to itself)
|
||||
/// will cause an error.
|
||||
/// Otherwise subsequent attempts to serialize the same table will be ignored.
|
||||
///
|
||||
/// Default: **true**
|
||||
#[must_use]
|
||||
pub const fn deny_recursive_tables(mut self, enabled: bool) -> Self {
|
||||
self.options.deny_recursive_tables = enabled;
|
||||
self
|
||||
}
|
||||
|
||||
/// If true, keys in tables will be iterated (and serialized) in sorted order.
|
||||
///
|
||||
/// Default: **false**
|
||||
#[must_use]
|
||||
pub const fn sort_keys(mut self, enabled: bool) -> Self {
|
||||
self.options.sort_keys = enabled;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
impl<'a, 'lua> Serialize for SerializableValue<'a, 'lua> {
|
||||
fn serialize<S>(&self, serializer: S) -> StdResult<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
match self {
|
||||
match self.value {
|
||||
Value::Nil => serializer.serialize_unit(),
|
||||
Value::Boolean(b) => serializer.serialize_bool(*b),
|
||||
#[allow(clippy::useless_conversion)]
|
||||
Value::Integer(i) => serializer
|
||||
.serialize_i64((*i).try_into().expect("cannot convert Lua Integer to i64")),
|
||||
Value::Integer(i) => serializer.serialize_i64((*i).into()),
|
||||
Value::Number(n) => serializer.serialize_f64(*n),
|
||||
#[cfg(feature = "luau")]
|
||||
Value::Vector(v) => v.serialize(serializer),
|
||||
Value::String(s) => s.serialize(serializer),
|
||||
Value::Table(t) => t.serialize(serializer),
|
||||
Value::UserData(ud) => ud.serialize(serializer),
|
||||
Value::Table(t) => {
|
||||
let visited = self.visited.as_ref().unwrap().clone();
|
||||
SerializableTable::new(t, self.options, visited).serialize(serializer)
|
||||
}
|
||||
Value::LightUserData(ud) if ud.0.is_null() => serializer.serialize_none(),
|
||||
Value::Error(_) | Value::LightUserData(_) | Value::Function(_) | Value::Thread(_) => {
|
||||
let msg = format!("cannot serialize <{}>", self.type_name());
|
||||
Err(ser::Error::custom(msg))
|
||||
Value::UserData(ud) if ud.is_serializable() || self.options.deny_unsupported_types => {
|
||||
ud.serialize(serializer)
|
||||
}
|
||||
Value::Function(_)
|
||||
| Value::Thread(_)
|
||||
| Value::UserData(_)
|
||||
| Value::LightUserData(_)
|
||||
| Value::Error(_) => {
|
||||
if self.options.deny_unsupported_types {
|
||||
let msg = format!("cannot serialize <{}>", self.value.type_name());
|
||||
Err(ser::Error::custom(msg))
|
||||
} else {
|
||||
serializer.serialize_unit()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Trait for types convertible to `Value`.
|
||||
pub trait IntoLua<'lua> {
|
||||
pub trait IntoLua<'lua>: Sized {
|
||||
/// Performs the conversion.
|
||||
fn into_lua(self, lua: &'lua Lua) -> Result<Value<'lua>>;
|
||||
|
||||
/// Pushes the value into the Lua stack.
|
||||
///
|
||||
/// # Safety
|
||||
/// This method does not check Lua stack space.
|
||||
#[doc(hidden)]
|
||||
#[inline]
|
||||
unsafe fn push_into_stack(self, lua: &'lua Lua) -> Result<()> {
|
||||
lua.push_value(self.into_lua(lua)?)
|
||||
}
|
||||
}
|
||||
|
||||
/// Trait for types convertible from `Value`.
|
||||
@@ -332,13 +713,33 @@ pub trait FromLua<'lua>: Sized {
|
||||
/// `i` is the argument index (position),
|
||||
/// `to` is a function name that received the argument.
|
||||
#[doc(hidden)]
|
||||
fn from_lua_arg(
|
||||
value: Value<'lua>,
|
||||
#[inline]
|
||||
fn from_lua_arg(arg: Value<'lua>, i: usize, to: Option<&str>, lua: &'lua Lua) -> Result<Self> {
|
||||
Self::from_lua(arg, lua).map_err(|err| Error::BadArgument {
|
||||
to: to.map(|s| s.to_string()),
|
||||
pos: i,
|
||||
name: None,
|
||||
cause: Arc::new(err),
|
||||
})
|
||||
}
|
||||
|
||||
/// Performs the conversion for a value in the Lua stack at index `idx`.
|
||||
#[doc(hidden)]
|
||||
#[inline]
|
||||
unsafe fn from_stack(idx: c_int, lua: &'lua Lua) -> Result<Self> {
|
||||
Self::from_lua(lua.stack_value(idx), lua)
|
||||
}
|
||||
|
||||
/// Same as `from_lua_arg` but for a value in the Lua stack at index `idx`.
|
||||
#[doc(hidden)]
|
||||
#[inline]
|
||||
unsafe fn from_stack_arg(
|
||||
idx: c_int,
|
||||
i: usize,
|
||||
to: Option<&str>,
|
||||
lua: &'lua Lua,
|
||||
) -> Result<Self> {
|
||||
Self::from_lua(value, lua).map_err(|err| Error::BadArgument {
|
||||
Self::from_stack(idx, lua).map_err(|err| Error::BadArgument {
|
||||
to: to.map(|s| s.to_string()),
|
||||
pos: i,
|
||||
name: None,
|
||||
@@ -349,24 +750,43 @@ pub trait FromLua<'lua>: Sized {
|
||||
|
||||
/// Multiple Lua values used for both argument passing and also for multiple return values.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct MultiValue<'lua>(Vec<Value<'lua>>);
|
||||
pub struct MultiValue<'lua> {
|
||||
vec: Vec<Value<'lua>>,
|
||||
lua: Option<&'lua Lua>,
|
||||
}
|
||||
|
||||
impl Drop for MultiValue<'_> {
|
||||
fn drop(&mut self) {
|
||||
if let Some(lua) = self.lua {
|
||||
let vec = mem::take(&mut self.vec);
|
||||
lua.push_multivalue_to_pool(vec);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> MultiValue<'lua> {
|
||||
/// Creates an empty `MultiValue` containing no values.
|
||||
pub const fn new() -> MultiValue<'lua> {
|
||||
MultiValue(Vec::new())
|
||||
MultiValue {
|
||||
vec: Vec::new(),
|
||||
lua: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Similar to `new` but can return previously used container with allocated capacity.
|
||||
/// Similar to `new` but can reuse previously used container with allocated capacity.
|
||||
#[inline]
|
||||
pub(crate) fn new_or_pooled(lua: &'lua Lua) -> MultiValue<'lua> {
|
||||
lua.new_multivalue_from_pool()
|
||||
}
|
||||
|
||||
/// Clears and returns previously allocated multivalue container to the pool.
|
||||
#[inline]
|
||||
pub(crate) fn return_to_pool(multivalue: Self, lua: &Lua) {
|
||||
lua.return_multivalue_to_pool(multivalue);
|
||||
pub(crate) fn with_lua_and_capacity(lua: &'lua Lua, capacity: usize) -> MultiValue<'lua> {
|
||||
let vec = lua
|
||||
.pop_multivalue_from_pool()
|
||||
.map(|mut vec| {
|
||||
vec.reserve(capacity);
|
||||
vec
|
||||
})
|
||||
.unwrap_or_else(|| Vec::with_capacity(capacity));
|
||||
MultiValue {
|
||||
vec,
|
||||
lua: Some(lua),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -389,8 +809,10 @@ impl<'lua> IntoIterator for MultiValue<'lua> {
|
||||
type IntoIter = iter::Rev<vec::IntoIter<Value<'lua>>>;
|
||||
|
||||
#[inline]
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.0.into_iter().rev()
|
||||
fn into_iter(mut self) -> Self::IntoIter {
|
||||
let vec = mem::take(&mut self.vec);
|
||||
mem::forget(self);
|
||||
vec.into_iter().rev()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -400,7 +822,7 @@ impl<'a, 'lua> IntoIterator for &'a MultiValue<'lua> {
|
||||
|
||||
#[inline]
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.0.iter().rev()
|
||||
self.vec.iter().rev()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -423,64 +845,60 @@ impl<'lua> Index<usize> for MultiValue<'lua> {
|
||||
|
||||
impl<'lua> MultiValue<'lua> {
|
||||
#[inline]
|
||||
pub fn from_vec(mut v: Vec<Value<'lua>>) -> MultiValue<'lua> {
|
||||
v.reverse();
|
||||
MultiValue(v)
|
||||
pub fn from_vec(mut vec: Vec<Value<'lua>>) -> MultiValue<'lua> {
|
||||
vec.reverse();
|
||||
MultiValue { vec, lua: None }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn into_vec(self) -> Vec<Value<'lua>> {
|
||||
let mut v = self.0;
|
||||
v.reverse();
|
||||
v
|
||||
pub fn into_vec(mut self) -> Vec<Value<'lua>> {
|
||||
let mut vec = mem::take(&mut self.vec);
|
||||
mem::forget(self);
|
||||
vec.reverse();
|
||||
vec
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get(&self, index: usize) -> Option<&Value<'lua>> {
|
||||
if index < self.0.len() {
|
||||
return self.0.get(self.0.len() - index - 1);
|
||||
if index < self.vec.len() {
|
||||
return self.vec.get(self.vec.len() - index - 1);
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn reserve(&mut self, size: usize) {
|
||||
self.0.reserve(size);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn pop_front(&mut self) -> Option<Value<'lua>> {
|
||||
self.0.pop()
|
||||
self.vec.pop()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn push_front(&mut self, value: Value<'lua>) {
|
||||
self.0.push(value);
|
||||
self.vec.push(value);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn clear(&mut self) {
|
||||
self.0.clear();
|
||||
self.vec.clear();
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn len(&self) -> usize {
|
||||
self.0.len()
|
||||
self.vec.len()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.0.is_empty()
|
||||
self.vec.is_empty()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn iter(&self) -> iter::Rev<slice::Iter<Value<'lua>>> {
|
||||
self.0.iter().rev()
|
||||
self.vec.iter().rev()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn drain_all(&mut self) -> iter::Rev<vec::Drain<Value<'lua>>> {
|
||||
self.0.drain(..).rev()
|
||||
self.vec.drain(..).rev()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@@ -488,11 +906,11 @@ impl<'lua> MultiValue<'lua> {
|
||||
&mut self,
|
||||
iter: impl IntoIterator<Item = Result<Value<'lua>>>,
|
||||
) -> Result<()> {
|
||||
self.0.clear();
|
||||
self.vec.clear();
|
||||
for value in iter {
|
||||
self.0.push(value?);
|
||||
self.vec.push(value?);
|
||||
}
|
||||
self.0.reverse();
|
||||
self.vec.reverse();
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -501,9 +919,26 @@ impl<'lua> MultiValue<'lua> {
|
||||
///
|
||||
/// 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<'lua> {
|
||||
pub trait IntoLuaMulti<'lua>: Sized {
|
||||
/// Performs the conversion.
|
||||
fn into_lua_multi(self, lua: &'lua Lua) -> Result<MultiValue<'lua>>;
|
||||
|
||||
/// Pushes the values into the Lua stack.
|
||||
///
|
||||
/// Returns number of pushed values.
|
||||
#[doc(hidden)]
|
||||
#[inline]
|
||||
unsafe fn push_into_stack_multi(self, lua: &'lua Lua) -> Result<c_int> {
|
||||
let mut values = self.into_lua_multi(lua)?;
|
||||
let len: c_int = values.len().try_into().unwrap();
|
||||
unsafe {
|
||||
check_stack(lua.state(), len + 1)?;
|
||||
for v in values.drain_all() {
|
||||
lua.push_value(v)?;
|
||||
}
|
||||
}
|
||||
Ok(len)
|
||||
}
|
||||
}
|
||||
|
||||
/// Trait for types that can be created from an arbitrary number of Lua values.
|
||||
@@ -525,15 +960,43 @@ pub trait FromLuaMulti<'lua>: Sized {
|
||||
/// `to` is a function name that received the arguments.
|
||||
#[doc(hidden)]
|
||||
#[inline]
|
||||
fn from_lua_multi_args(
|
||||
values: MultiValue<'lua>,
|
||||
fn from_lua_args(
|
||||
args: MultiValue<'lua>,
|
||||
i: usize,
|
||||
to: Option<&str>,
|
||||
lua: &'lua Lua,
|
||||
) -> Result<Self> {
|
||||
let _ = (i, to);
|
||||
Self::from_lua_multi(args, lua)
|
||||
}
|
||||
|
||||
/// Performs the conversion for a number of values in the Lua stack.
|
||||
#[doc(hidden)]
|
||||
#[inline]
|
||||
unsafe fn from_stack_multi(nvals: c_int, lua: &'lua Lua) -> Result<Self> {
|
||||
let mut values = MultiValue::with_lua_and_capacity(lua, nvals as usize);
|
||||
for idx in 1..=nvals {
|
||||
values.push_front(lua.stack_value(-idx));
|
||||
}
|
||||
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)
|
||||
}
|
||||
|
||||
/// Same as `from_lua_args` but for a number of values in the Lua stack.
|
||||
#[doc(hidden)]
|
||||
#[inline]
|
||||
unsafe fn from_stack_args(
|
||||
nargs: c_int,
|
||||
i: usize,
|
||||
to: Option<&str>,
|
||||
lua: &'lua Lua,
|
||||
) -> Result<Self> {
|
||||
let _ = (i, to);
|
||||
Self::from_stack_multi(nargs, lua)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
+29
-3
@@ -6,14 +6,21 @@ use std::time::Duration;
|
||||
use futures_util::stream::TryStreamExt;
|
||||
|
||||
use mlua::{
|
||||
AnyUserDataExt, Error, Function, Lua, LuaOptions, Result, StdLib, Table, TableExt, UserData,
|
||||
UserDataMethods, Value,
|
||||
AnyUserDataExt, Error, Function, Lua, LuaOptions, MultiValue, Result, StdLib, Table, TableExt,
|
||||
UserData, UserDataMethods, Value,
|
||||
};
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
async fn sleep_ms(ms: u64) {
|
||||
tokio::time::sleep(Duration::from_millis(ms)).await;
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
async fn sleep_ms(_ms: u64) {
|
||||
// I was unable to make sleep() work in wasm32-emscripten target
|
||||
tokio::task::yield_now().await;
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_async_function() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
@@ -82,6 +89,25 @@ async fn test_async_call() -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_async_call_many_returns() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let hello = lua.create_async_function(|_lua, ()| async move {
|
||||
sleep_ms(10).await;
|
||||
Ok(("a", "b", "c", 1))
|
||||
})?;
|
||||
|
||||
let vals = hello.call_async::<_, MultiValue>(()).await?;
|
||||
assert_eq!(vals.len(), 4);
|
||||
assert_eq!(vals[0].to_string()?, "a");
|
||||
assert_eq!(vals[1].to_string()?, "b");
|
||||
assert_eq!(vals[2].to_string()?, "c");
|
||||
assert_eq!(vals[3], Value::Integer(1));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_async_bind_call() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
@@ -417,7 +443,7 @@ async fn test_async_userdata() -> Result<()> {
|
||||
let globals = lua.globals();
|
||||
|
||||
let userdata = lua.create_userdata(MyUserData(11))?;
|
||||
globals.set("userdata", userdata.clone())?;
|
||||
globals.set("userdata", &userdata)?;
|
||||
|
||||
lua.load(
|
||||
r#"
|
||||
|
||||
+9
-3
@@ -1,12 +1,18 @@
|
||||
use std::fs;
|
||||
use std::io;
|
||||
|
||||
use mlua::{AnyUserData, Function, Lua, Result};
|
||||
use mlua::{Lua, Result};
|
||||
|
||||
#[test]
|
||||
fn test_chunk_path() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
if cfg!(target_arch = "wasm32") {
|
||||
// TODO: figure out why emscripten fails on file operations
|
||||
// Also see https://github.com/rust-lang/rust/issues/119250
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let temp_dir = tempfile::tempdir().unwrap();
|
||||
fs::write(
|
||||
temp_dir.path().join("module.lua"),
|
||||
@@ -36,8 +42,8 @@ fn test_chunk_macro() -> Result<()> {
|
||||
let data = lua.create_table()?;
|
||||
data.raw_set("num", 1)?;
|
||||
|
||||
let ud = AnyUserData::wrap("hello");
|
||||
let f = Function::wrap(|_lua, ()| Ok(()));
|
||||
let ud = mlua::AnyUserData::wrap("hello");
|
||||
let f = mlua::Function::wrap(|_lua, ()| Ok(()));
|
||||
|
||||
lua.globals().set("g", 123)?;
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ error: lifetime may not live long enough
|
||||
9 | reg.add_async_method("t", |_, this: &String, ()| async {
|
||||
| ___________________________________----------------------_^
|
||||
| | | |
|
||||
| | | return type of closure `[async block@$DIR/tests/compile/async_any_userdata_method.rs:9:58: 12:10]` contains a lifetime `'2`
|
||||
| | | return type of closure `{async block@$DIR/tests/compile/async_any_userdata_method.rs:9:58: 12:10}` contains a lifetime `'2`
|
||||
| | lifetime `'1` represents this closure's body
|
||||
10 | | s = this;
|
||||
11 | | Ok(())
|
||||
@@ -27,6 +27,8 @@ error[E0596]: cannot borrow `s` as mutable, as it is a captured variable in a `F
|
||||
error[E0597]: `s` does not live long enough
|
||||
--> tests/compile/async_any_userdata_method.rs:8:21
|
||||
|
|
||||
7 | let s = String::new();
|
||||
| - binding `s` declared here
|
||||
8 | let mut s = &s;
|
||||
| ^^ borrowed value does not live long enough
|
||||
9 | / reg.add_async_method("t", |_, this: &String, ()| async {
|
||||
|
||||
@@ -7,14 +7,46 @@ error[E0277]: the type `UnsafeCell<mlua::lua::ExtraData>` may contain interior m
|
||||
| required by a bound introduced by this call
|
||||
|
|
||||
= help: within `Lua`, the trait `RefUnwindSafe` is not implemented for `UnsafeCell<mlua::lua::ExtraData>`
|
||||
= note: required because it appears within the type `ArcInner<UnsafeCell<ExtraData>>`
|
||||
= note: required because it appears within the type `PhantomData<ArcInner<UnsafeCell<ExtraData>>>`
|
||||
= note: required because it appears within the type `Arc<UnsafeCell<ExtraData>>`
|
||||
= note: required because it appears within the type `LuaInner`
|
||||
= note: required because it appears within the type `ArcInner<LuaInner>`
|
||||
= note: required because it appears within the type `PhantomData<ArcInner<LuaInner>>`
|
||||
= note: required because it appears within the type `Arc<LuaInner>`
|
||||
= note: required because it appears within the type `Lua`
|
||||
note: required because it appears within the type `ArcInner<UnsafeCell<ExtraData>>`
|
||||
--> $RUST/alloc/src/sync.rs
|
||||
|
|
||||
| struct ArcInner<T: ?Sized> {
|
||||
| ^^^^^^^^
|
||||
note: required because it appears within the type `PhantomData<ArcInner<UnsafeCell<ExtraData>>>`
|
||||
--> $RUST/core/src/marker.rs
|
||||
|
|
||||
| pub struct PhantomData<T: ?Sized>;
|
||||
| ^^^^^^^^^^^
|
||||
note: required because it appears within the type `Arc<UnsafeCell<ExtraData>>`
|
||||
--> $RUST/alloc/src/sync.rs
|
||||
|
|
||||
| pub struct Arc<
|
||||
| ^^^
|
||||
note: required because it appears within the type `LuaInner`
|
||||
--> src/lua.rs
|
||||
|
|
||||
| pub struct LuaInner {
|
||||
| ^^^^^^^^
|
||||
note: required because it appears within the type `ArcInner<LuaInner>`
|
||||
--> $RUST/alloc/src/sync.rs
|
||||
|
|
||||
| struct ArcInner<T: ?Sized> {
|
||||
| ^^^^^^^^
|
||||
note: required because it appears within the type `PhantomData<ArcInner<LuaInner>>`
|
||||
--> $RUST/core/src/marker.rs
|
||||
|
|
||||
| pub struct PhantomData<T: ?Sized>;
|
||||
| ^^^^^^^^^^^
|
||||
note: required because it appears within the type `Arc<LuaInner>`
|
||||
--> $RUST/alloc/src/sync.rs
|
||||
|
|
||||
| pub struct Arc<
|
||||
| ^^^
|
||||
note: required because it appears within the type `Lua`
|
||||
--> src/lua.rs
|
||||
|
|
||||
| pub struct Lua(Arc<LuaInner>);
|
||||
| ^^^
|
||||
= note: required for `&Lua` to implement `UnwindSafe`
|
||||
note: required because it's used within this closure
|
||||
--> tests/compile/lua_norefunwindsafe.rs:7:18
|
||||
|
||||
@@ -4,22 +4,25 @@ error[E0277]: `Rc<Cell<i32>>` cannot be sent between threads safely
|
||||
11 | lua.create_function(move |_, ()| {
|
||||
| --------------- ^-----------
|
||||
| | |
|
||||
| _________|_______________within this `[closure@$DIR/tests/compile/non_send.rs:11:25: 11:37]`
|
||||
| _________|_______________within this `{closure@$DIR/tests/compile/non_send.rs:11:25: 11:37}`
|
||||
| | |
|
||||
| | required by a bound introduced by this call
|
||||
12 | | Ok(data.get())
|
||||
13 | | })?
|
||||
| |_____^ `Rc<Cell<i32>>` cannot be sent between threads safely
|
||||
|
|
||||
= help: within `[closure@$DIR/tests/compile/non_send.rs:11:25: 11:37]`, the trait `Send` is not implemented for `Rc<Cell<i32>>`
|
||||
= help: within `{closure@$DIR/tests/compile/non_send.rs:11:25: 11:37}`, the trait `Send` is not implemented for `Rc<Cell<i32>>`
|
||||
note: required because it's used within this closure
|
||||
--> tests/compile/non_send.rs:11:25
|
||||
|
|
||||
11 | lua.create_function(move |_, ()| {
|
||||
| ^^^^^^^^^^^^
|
||||
= note: required for `[closure@$DIR/tests/compile/non_send.rs:11:25: 11:37]` to implement `mlua::types::MaybeSend`
|
||||
= note: required for `{closure@$DIR/tests/compile/non_send.rs:11:25: 11:37}` to implement `mlua::types::MaybeSend`
|
||||
note: required by a bound in `Lua::create_function`
|
||||
--> src/lua.rs
|
||||
|
|
||||
| pub fn create_function<'lua, A, R, F>(&'lua self, func: F) -> Result<Function<'lua>>
|
||||
| --------------- required by a bound in this associated function
|
||||
...
|
||||
| F: Fn(&'lua Lua, A) -> Result<R> + MaybeSend + 'static,
|
||||
| ^^^^^^^^^ required by this bound in `Lua::create_function`
|
||||
|
||||
@@ -7,17 +7,57 @@ error[E0277]: the type `UnsafeCell<mlua::lua::ExtraData>` may contain interior m
|
||||
| required by a bound introduced by this call
|
||||
|
|
||||
= help: within `Lua`, the trait `RefUnwindSafe` is not implemented for `UnsafeCell<mlua::lua::ExtraData>`
|
||||
= note: required because it appears within the type `ArcInner<UnsafeCell<ExtraData>>`
|
||||
= note: required because it appears within the type `PhantomData<ArcInner<UnsafeCell<ExtraData>>>`
|
||||
= note: required because it appears within the type `Arc<UnsafeCell<ExtraData>>`
|
||||
= note: required because it appears within the type `LuaInner`
|
||||
= note: required because it appears within the type `ArcInner<LuaInner>`
|
||||
= note: required because it appears within the type `PhantomData<ArcInner<LuaInner>>`
|
||||
= note: required because it appears within the type `Arc<LuaInner>`
|
||||
= note: required because it appears within the type `Lua`
|
||||
note: required because it appears within the type `ArcInner<UnsafeCell<ExtraData>>`
|
||||
--> $RUST/alloc/src/sync.rs
|
||||
|
|
||||
| struct ArcInner<T: ?Sized> {
|
||||
| ^^^^^^^^
|
||||
note: required because it appears within the type `PhantomData<ArcInner<UnsafeCell<ExtraData>>>`
|
||||
--> $RUST/core/src/marker.rs
|
||||
|
|
||||
| pub struct PhantomData<T: ?Sized>;
|
||||
| ^^^^^^^^^^^
|
||||
note: required because it appears within the type `Arc<UnsafeCell<ExtraData>>`
|
||||
--> $RUST/alloc/src/sync.rs
|
||||
|
|
||||
| pub struct Arc<
|
||||
| ^^^
|
||||
note: required because it appears within the type `LuaInner`
|
||||
--> src/lua.rs
|
||||
|
|
||||
| pub struct LuaInner {
|
||||
| ^^^^^^^^
|
||||
note: required because it appears within the type `ArcInner<LuaInner>`
|
||||
--> $RUST/alloc/src/sync.rs
|
||||
|
|
||||
| struct ArcInner<T: ?Sized> {
|
||||
| ^^^^^^^^
|
||||
note: required because it appears within the type `PhantomData<ArcInner<LuaInner>>`
|
||||
--> $RUST/core/src/marker.rs
|
||||
|
|
||||
| pub struct PhantomData<T: ?Sized>;
|
||||
| ^^^^^^^^^^^
|
||||
note: required because it appears within the type `Arc<LuaInner>`
|
||||
--> $RUST/alloc/src/sync.rs
|
||||
|
|
||||
| pub struct Arc<
|
||||
| ^^^
|
||||
note: required because it appears within the type `Lua`
|
||||
--> src/lua.rs
|
||||
|
|
||||
| pub struct Lua(Arc<LuaInner>);
|
||||
| ^^^
|
||||
= note: required for `&Lua` to implement `UnwindSafe`
|
||||
= note: required because it appears within the type `LuaRef<'_>`
|
||||
= note: required because it appears within the type `Table<'_>`
|
||||
note: required because it appears within the type `LuaRef<'_>`
|
||||
--> src/types.rs
|
||||
|
|
||||
| pub(crate) struct LuaRef<'lua> {
|
||||
| ^^^^^^
|
||||
note: required because it appears within the type `Table<'_>`
|
||||
--> src/table.rs
|
||||
|
|
||||
| pub struct Table<'lua>(pub(crate) LuaRef<'lua>);
|
||||
| ^^^^^
|
||||
note: required because it's used within this closure
|
||||
--> tests/compile/ref_nounwindsafe.rs:8:18
|
||||
|
|
||||
|
||||
@@ -4,6 +4,8 @@ error[E0597]: `ibad` does not live long enough
|
||||
11 | lua.scope(|scope| {
|
||||
| ----- has type `&mlua::Scope<'_, '1>`
|
||||
...
|
||||
14 | let ibad = 42;
|
||||
| ---- binding `ibad` declared here
|
||||
15 | scope.create_nonstatic_userdata(MyUserData(&ibad)).unwrap();
|
||||
| -------------------------------------------^^^^^--
|
||||
| | |
|
||||
|
||||
@@ -1,35 +1,31 @@
|
||||
error[E0597]: `lua` does not live long enough
|
||||
--> tests/compile/static_callback_args.rs:12:5
|
||||
|
|
||||
10 | let lua = Lua::new();
|
||||
| --- binding `lua` declared here
|
||||
10 | let lua = Lua::new();
|
||||
| --- binding `lua` declared here
|
||||
11 |
|
||||
12 | / lua.create_function(|_, table: Table| {
|
||||
13 | |/ BAD_TIME.with(|bt| {
|
||||
14 | || *bt.borrow_mut() = Some(table);
|
||||
15 | || });
|
||||
| ||__________- argument requires that `lua` is borrowed for `'static`
|
||||
16 | | Ok(())
|
||||
17 | | })?
|
||||
| |_______^ borrowed value does not live long enough
|
||||
12 | lua.create_function(|_, table: Table| {
|
||||
| ^^^ borrowed value does not live long enough
|
||||
13 | / BAD_TIME.with(|bt| {
|
||||
14 | | *bt.borrow_mut() = Some(table);
|
||||
15 | | });
|
||||
| |__________- argument requires that `lua` is borrowed for `'static`
|
||||
...
|
||||
32 | }
|
||||
| - `lua` dropped here while still borrowed
|
||||
32 | }
|
||||
| - `lua` dropped here while still borrowed
|
||||
|
||||
error[E0505]: cannot move out of `lua` because it is borrowed
|
||||
--> tests/compile/static_callback_args.rs:22:10
|
||||
|
|
||||
10 | let lua = Lua::new();
|
||||
| --- binding `lua` declared here
|
||||
10 | let lua = Lua::new();
|
||||
| --- binding `lua` declared here
|
||||
11 |
|
||||
12 | / lua.create_function(|_, table: Table| {
|
||||
13 | |/ BAD_TIME.with(|bt| {
|
||||
14 | || *bt.borrow_mut() = Some(table);
|
||||
15 | || });
|
||||
| ||__________- argument requires that `lua` is borrowed for `'static`
|
||||
16 | | Ok(())
|
||||
17 | | })?
|
||||
| |_______- borrow of `lua` occurs here
|
||||
12 | lua.create_function(|_, table: Table| {
|
||||
| --- borrow of `lua` occurs here
|
||||
13 | / BAD_TIME.with(|bt| {
|
||||
14 | | *bt.borrow_mut() = Some(table);
|
||||
15 | | });
|
||||
| |__________- argument requires that `lua` is borrowed for `'static`
|
||||
...
|
||||
22 | drop(lua);
|
||||
| ^^^ move out of `lua` occurs here
|
||||
22 | drop(lua);
|
||||
| ^^^ move out of `lua` occurs here
|
||||
|
||||
+334
-1
@@ -2,8 +2,280 @@ use std::borrow::Cow;
|
||||
use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet};
|
||||
use std::ffi::{CStr, CString};
|
||||
|
||||
use bstr::BString;
|
||||
use maplit::{btreemap, btreeset, hashmap, hashset};
|
||||
use mlua::{Error, Lua, Result};
|
||||
use mlua::{
|
||||
AnyUserData, Error, Function, IntoLua, Lua, RegistryKey, Result, Table, Thread, UserDataRef,
|
||||
Value,
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn test_value_into_lua() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
// Direct conversion
|
||||
let v = Value::Boolean(true);
|
||||
let v2 = (&v).into_lua(&lua)?;
|
||||
assert_eq!(v, v2);
|
||||
|
||||
// Push into stack
|
||||
let table = lua.create_table()?;
|
||||
table.set("v", &v)?;
|
||||
assert_eq!(v, table.get::<_, Value>("v")?);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_string_into_lua() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
// Direct conversion
|
||||
let s = lua.create_string("hello, world!")?;
|
||||
let s2 = (&s).into_lua(&lua)?;
|
||||
assert_eq!(s, s2.as_string().unwrap());
|
||||
|
||||
// Push into stack
|
||||
let table = lua.create_table()?;
|
||||
table.set("s", &s)?;
|
||||
assert_eq!(s, table.get::<_, String>("s")?);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "unstable", not(feature = "send")))]
|
||||
#[test]
|
||||
fn test_owned_string_into_lua() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
// Direct conversion
|
||||
let s = lua.create_string("hello, world")?.into_owned();
|
||||
let s2 = (&s).into_lua(&lua)?;
|
||||
assert_eq!(s.to_ref(), *s2.as_string().unwrap());
|
||||
|
||||
// Push into stack
|
||||
let table = lua.create_table()?;
|
||||
table.set("s", &s)?;
|
||||
assert_eq!(s.to_ref(), table.get::<_, String>("s")?);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "unstable", not(feature = "send")))]
|
||||
#[test]
|
||||
fn test_owned_string_from_lua() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let s = lua.unpack::<mlua::OwnedString>(lua.pack("hello, world")?)?;
|
||||
assert_eq!(s.to_ref(), "hello, world");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_table_into_lua() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
// Direct conversion
|
||||
let t = lua.create_table()?;
|
||||
let t2 = (&t).into_lua(&lua)?;
|
||||
assert_eq!(&t, t2.as_table().unwrap());
|
||||
|
||||
// Push into stack
|
||||
let f = lua.create_function(|_, (t, s): (Table, String)| t.set("s", s))?;
|
||||
f.call((&t, "hello"))?;
|
||||
assert_eq!("hello", t.get::<_, String>("s")?);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "unstable", not(feature = "send")))]
|
||||
#[test]
|
||||
fn test_owned_table_into_lua() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
// Direct conversion
|
||||
let t = lua.create_table()?.into_owned();
|
||||
let t2 = (&t).into_lua(&lua)?;
|
||||
assert_eq!(t.to_ref(), *t2.as_table().unwrap());
|
||||
|
||||
// Push into stack
|
||||
let f = lua.create_function(|_, (t, s): (Table, String)| t.set("s", s))?;
|
||||
f.call((&t, "hello"))?;
|
||||
assert_eq!("hello", t.to_ref().get::<_, String>("s")?);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_function_into_lua() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
// Direct conversion
|
||||
let f = lua.create_function(|_, ()| Ok::<_, Error>(()))?;
|
||||
let f2 = (&f).into_lua(&lua)?;
|
||||
assert_eq!(&f, f2.as_function().unwrap());
|
||||
|
||||
// Push into stack
|
||||
let table = lua.create_table()?;
|
||||
table.set("f", &f)?;
|
||||
assert_eq!(f, table.get::<_, Function>("f")?);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "unstable", not(feature = "send")))]
|
||||
#[test]
|
||||
fn test_owned_function_into_lua() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
// Direct conversion
|
||||
let f = lua
|
||||
.create_function(|_, ()| Ok::<_, Error>(()))?
|
||||
.into_owned();
|
||||
let f2 = (&f).into_lua(&lua)?;
|
||||
assert_eq!(f.to_ref(), *f2.as_function().unwrap());
|
||||
|
||||
// Push into stack
|
||||
let table = lua.create_table()?;
|
||||
table.set("f", &f)?;
|
||||
assert_eq!(f.to_ref(), table.get::<_, Function>("f")?);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_thread_into_lua() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
// Direct conversion
|
||||
let f = lua.create_function(|_, ()| Ok::<_, Error>(()))?;
|
||||
let th = lua.create_thread(f)?;
|
||||
let th2 = (&th).into_lua(&lua)?;
|
||||
assert_eq!(&th, th2.as_thread().unwrap());
|
||||
|
||||
// Push into stack
|
||||
let table = lua.create_table()?;
|
||||
table.set("th", &th)?;
|
||||
assert_eq!(th, table.get::<_, Thread>("th")?);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "unstable", not(feature = "send")))]
|
||||
#[test]
|
||||
fn test_owned_thread_into_lua() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
// Direct conversion
|
||||
let f = lua.create_function(|_, ()| Ok::<_, Error>(()))?;
|
||||
let th = lua.create_thread(f)?.into_owned();
|
||||
let th2 = (&th).into_lua(&lua)?;
|
||||
assert_eq!(&th.to_ref(), th2.as_thread().unwrap());
|
||||
|
||||
// Push into stack
|
||||
let table = lua.create_table()?;
|
||||
table.set("th", &th)?;
|
||||
assert_eq!(th.to_ref(), table.get::<_, Thread>("th")?);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "unstable", not(feature = "send")))]
|
||||
#[test]
|
||||
fn test_owned_thread_from_lua() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let th = lua.unpack::<mlua::OwnedThread>(Value::Thread(lua.current_thread()))?;
|
||||
assert_eq!(th.to_ref(), lua.current_thread());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_anyuserdata_into_lua() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
// Direct conversion
|
||||
let ud = lua.create_any_userdata(String::from("hello"))?;
|
||||
let ud2 = (&ud).into_lua(&lua)?;
|
||||
assert_eq!(&ud, ud2.as_userdata().unwrap());
|
||||
|
||||
// Push into stack
|
||||
let table = lua.create_table()?;
|
||||
table.set("ud", &ud)?;
|
||||
assert_eq!(ud, table.get::<_, AnyUserData>("ud")?);
|
||||
assert_eq!("hello", *table.get::<_, UserDataRef<String>>("ud")?);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "unstable", not(feature = "send")))]
|
||||
#[test]
|
||||
fn test_owned_anyuserdata_into_lua() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
// Direct conversion
|
||||
let ud = lua.create_any_userdata(String::from("hello"))?.into_owned();
|
||||
let ud2 = (&ud).into_lua(&lua)?;
|
||||
assert_eq!(ud.to_ref(), *ud2.as_userdata().unwrap());
|
||||
|
||||
// Push into stack
|
||||
let table = lua.create_table()?;
|
||||
table.set("ud", &ud)?;
|
||||
assert_eq!(ud.to_ref(), table.get::<_, AnyUserData>("ud")?);
|
||||
assert_eq!("hello", *table.get::<_, UserDataRef<String>>("ud")?);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_registry_value_into_lua() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
// Direct conversion
|
||||
let s = lua.create_string("hello, world")?;
|
||||
let r = lua.create_registry_value(&s)?;
|
||||
let value1 = lua.pack(&r)?;
|
||||
let value2 = lua.pack(r)?;
|
||||
assert_eq!(value1.as_str(), Some("hello, world"));
|
||||
assert_eq!(value2.to_pointer(), value2.to_pointer());
|
||||
|
||||
// Push into stack
|
||||
let t = lua.create_table()?;
|
||||
let r = lua.create_registry_value(&t)?;
|
||||
let f = lua.create_function(|_, (t, k, v): (Table, Value, Value)| t.set(k, v))?;
|
||||
f.call((&r, "hello", "world"))?;
|
||||
f.call((r, "welcome", "to the jungle"))?;
|
||||
assert_eq!(t.get::<_, String>("hello")?, "world");
|
||||
assert_eq!(t.get::<_, String>("welcome")?, "to the jungle");
|
||||
|
||||
// Try to set nil registry key
|
||||
let r_nil = lua.create_registry_value(Value::Nil)?;
|
||||
t.set("hello", &r_nil)?;
|
||||
assert_eq!(t.get::<_, Value>("hello")?, Value::Nil);
|
||||
|
||||
// Check non-owned registry key
|
||||
let lua2 = Lua::new();
|
||||
let r2 = lua2.create_registry_value("abc")?;
|
||||
assert!(matches!(
|
||||
f.call::<_, ()>(&r2),
|
||||
Err(Error::MismatchedRegistryKey)
|
||||
));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_registry_key_from_lua() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let fkey = lua.load("function() return 1 end").eval::<RegistryKey>()?;
|
||||
let f = lua.registry_value::<Function>(&fkey)?;
|
||||
assert_eq!(f.call::<_, i32>(())?, 1);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_conv_vec() -> Result<()> {
|
||||
@@ -138,3 +410,64 @@ fn test_conv_array() -> Result<()> {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_bstring_from_lua() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let s = lua.create_string("hello, world")?;
|
||||
let bstr = lua.unpack::<BString>(Value::String(s))?;
|
||||
assert_eq!(bstr, "hello, world");
|
||||
|
||||
let bstr = lua.unpack::<BString>(Value::Integer(123))?;
|
||||
assert_eq!(bstr, "123");
|
||||
|
||||
let bstr = lua.unpack::<BString>(Value::Number(-123.55))?;
|
||||
assert_eq!(bstr, "-123.55");
|
||||
|
||||
// Test from stack
|
||||
let f = lua.create_function(|_, bstr: BString| Ok(bstr))?;
|
||||
let bstr = f.call::<_, BString>("hello, world")?;
|
||||
assert_eq!(bstr, "hello, world");
|
||||
|
||||
let bstr = f.call::<_, BString>(-43.22)?;
|
||||
assert_eq!(bstr, "-43.22");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(feature = "luau")]
|
||||
#[test]
|
||||
fn test_bstring_from_lua_buffer() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let b = lua.create_buffer("hello, world")?;
|
||||
let bstr = lua.unpack::<BString>(Value::UserData(b))?;
|
||||
assert_eq!(bstr, "hello, world");
|
||||
|
||||
// Test from stack
|
||||
let f = lua.create_function(|_, bstr: BString| Ok(bstr))?;
|
||||
let buf = lua.create_buffer("hello, world")?;
|
||||
let bstr = f.call::<_, BString>(buf)?;
|
||||
assert_eq!(bstr, "hello, world");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_option_into_from_lua() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
// Direct conversion
|
||||
let v = Some(42);
|
||||
let v2 = v.into_lua(&lua)?;
|
||||
assert_eq!(v, v2.as_i32());
|
||||
|
||||
// Push into stack / get from stack
|
||||
let f = lua.create_function(|_, v: Option<i32>| Ok(v))?;
|
||||
assert_eq!(f.call::<_, Option<i32>>(Some(42))?, Some(42));
|
||||
assert_eq!(f.call::<_, Option<i32>>(Option::<i32>::None)?, None);
|
||||
assert_eq!(f.call::<_, Option<i32>>(())?, None);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
+3
-1
@@ -41,7 +41,9 @@ fn test_error_context() -> Result<()> {
|
||||
.context("some new context")
|
||||
})?;
|
||||
let res = func3.call::<_, ()>(()).err().unwrap();
|
||||
let Error::CallbackError { cause, .. } = &res else { unreachable!() };
|
||||
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());
|
||||
|
||||
@@ -231,6 +231,40 @@ fn test_function_info() -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_function_pointer() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let func1 = lua.load("return function() end").into_function()?;
|
||||
let func2 = func1.call::<_, Function>(())?;
|
||||
|
||||
assert_eq!(func1.to_pointer(), func1.clone().to_pointer());
|
||||
assert_ne!(func1.to_pointer(), func2.to_pointer());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(feature = "luau")]
|
||||
#[test]
|
||||
fn test_function_deep_clone() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
lua.globals().set("a", 1)?;
|
||||
let func1 = lua.load("a += 1; return a").into_function()?;
|
||||
let func2 = func1.deep_clone();
|
||||
|
||||
assert_ne!(func1.to_pointer(), func2.to_pointer());
|
||||
assert_eq!(func1.call::<_, i32>(())?, 2);
|
||||
assert_eq!(func2.call::<_, i32>(())?, 3);
|
||||
|
||||
// Check that for Rust functions deep_clone is just a clone
|
||||
let rust_func = lua.create_function(|_, ()| Ok(42))?;
|
||||
let rust_func2 = rust_func.deep_clone();
|
||||
assert_eq!(rust_func.to_pointer(), rust_func2.to_pointer());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_function_wrap() -> Result<()> {
|
||||
use mlua::Error;
|
||||
|
||||
+4
-2
@@ -1,6 +1,5 @@
|
||||
#![cfg(not(feature = "luau"))]
|
||||
|
||||
use std::cell::RefCell;
|
||||
use std::ops::Deref;
|
||||
use std::sync::atomic::{AtomicI64, Ordering};
|
||||
use std::sync::{Arc, Mutex};
|
||||
@@ -170,10 +169,13 @@ fn test_hook_removal() -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Having the code compiled (even not run) on macos and luajit causes a memory reference issue
|
||||
// See https://github.com/LuaJIT/LuaJIT/issues/1099
|
||||
#[cfg(not(all(feature = "luajit", target_os = "macos")))]
|
||||
#[test]
|
||||
fn test_hook_swap_within_hook() -> Result<()> {
|
||||
thread_local! {
|
||||
static TL_LUA: RefCell<Option<Lua>> = RefCell::new(None);
|
||||
static TL_LUA: std::cell::RefCell<Option<Lua>> = Default::default();
|
||||
}
|
||||
|
||||
TL_LUA.with(|tl| {
|
||||
|
||||
+132
-7
@@ -1,6 +1,5 @@
|
||||
#![cfg(feature = "luau")]
|
||||
|
||||
use std::env;
|
||||
use std::fmt::Debug;
|
||||
use std::fs;
|
||||
use std::panic::{catch_unwind, AssertUnwindSafe};
|
||||
@@ -8,7 +7,8 @@ use std::sync::atomic::{AtomicU64, Ordering};
|
||||
use std::sync::Arc;
|
||||
|
||||
use mlua::{
|
||||
Compiler, CoverageInfo, Error, Lua, Result, Table, ThreadStatus, Value, Vector, VmState,
|
||||
Compiler, CoverageInfo, Error, Lua, LuaOptions, Result, StdLib, Table, ThreadStatus, Value,
|
||||
Vector, VmState,
|
||||
};
|
||||
|
||||
#[test]
|
||||
@@ -23,7 +23,18 @@ fn test_version() -> Result<()> {
|
||||
|
||||
#[test]
|
||||
fn test_require() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
// Ensure that require() is not available if package module is not loaded
|
||||
let mut lua = Lua::new_with(StdLib::NONE, LuaOptions::default())?;
|
||||
assert!(lua.globals().get::<_, Option<Value>>("require")?.is_none());
|
||||
assert!(lua.globals().get::<_, Option<Value>>("package")?.is_none());
|
||||
|
||||
if cfg!(target_arch = "wasm32") {
|
||||
// TODO: figure out why emscripten fails on file operations
|
||||
// Also see https://github.com/rust-lang/rust/issues/119250
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
lua = Lua::new();
|
||||
|
||||
let temp_dir = tempfile::tempdir().unwrap();
|
||||
fs::write(
|
||||
@@ -37,7 +48,10 @@ fn test_require() -> Result<()> {
|
||||
"#,
|
||||
)?;
|
||||
|
||||
env::set_var("LUAU_PATH", temp_dir.path().join("?.luau"));
|
||||
lua.globals()
|
||||
.get::<_, Table>("package")?
|
||||
.set("path", temp_dir.path().join("?.luau").to_string_lossy())?;
|
||||
|
||||
lua.load(
|
||||
r#"
|
||||
local module = require("module")
|
||||
@@ -49,7 +63,28 @@ fn test_require() -> Result<()> {
|
||||
assert(not ok and string.find(err, "module.luau") ~= nil)
|
||||
"#,
|
||||
)
|
||||
.exec()
|
||||
.exec()?;
|
||||
|
||||
// Require non-existent module
|
||||
match lua.load("require('non-existent')").exec() {
|
||||
Err(Error::RuntimeError(e)) if e.contains("module 'non-existent' not found") => {}
|
||||
r => panic!("expected RuntimeError(...) with a specific message, got {r:?}"),
|
||||
}
|
||||
|
||||
// Require binary module in safe mode
|
||||
lua.globals()
|
||||
.get::<_, Table>("package")?
|
||||
.set("cpath", temp_dir.path().join("?.so").to_string_lossy())?;
|
||||
fs::write(temp_dir.path().join("dylib.so"), "")?;
|
||||
match lua.load("require('dylib')").exec() {
|
||||
Err(Error::RuntimeError(e)) if cfg!(unix) && e.contains("module 'dylib' not found") => {
|
||||
assert!(e.contains("dynamic libraries are disabled in safe mode"))
|
||||
}
|
||||
Err(Error::RuntimeError(e)) if e.contains("module 'dylib' not found") => {}
|
||||
r => panic!("expected RuntimeError(...) with a specific message, got {r:?}"),
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "luau-vector4"))]
|
||||
@@ -84,7 +119,7 @@ fn test_vectors() -> Result<()> {
|
||||
assert(v.z == 3)
|
||||
"#,
|
||||
)
|
||||
.set_compiler(Compiler::new().set_vector_ctor(Some("vector".to_string())))
|
||||
.set_compiler(Compiler::new().set_vector_ctor("vector"))
|
||||
.exec()?;
|
||||
|
||||
Ok(())
|
||||
@@ -124,7 +159,49 @@ fn test_vectors() -> Result<()> {
|
||||
assert(v.w == 4)
|
||||
"#,
|
||||
)
|
||||
.set_compiler(Compiler::new().set_vector_ctor(Some("vector".to_string())))
|
||||
.set_compiler(Compiler::new().set_vector_ctor("vector"))
|
||||
.exec()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(all(not(feature = "luau-vector4"), feature = "unstable"))]
|
||||
#[test]
|
||||
fn test_vector_metatable() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let vector_mt = lua
|
||||
.load(
|
||||
r#"
|
||||
{
|
||||
__index = {
|
||||
new = vector,
|
||||
|
||||
product = function(a, b)
|
||||
return vector(a.x * b.x, a.y * b.y, a.z * b.z)
|
||||
end
|
||||
}
|
||||
}
|
||||
"#,
|
||||
)
|
||||
.eval::<Table>()?;
|
||||
vector_mt.set_metatable(Some(vector_mt.clone()));
|
||||
lua.set_vector_metatable(Some(vector_mt.clone()));
|
||||
lua.globals().set("Vector3", vector_mt)?;
|
||||
|
||||
let compiler = Compiler::new()
|
||||
.set_vector_lib("Vector3")
|
||||
.set_vector_ctor("new");
|
||||
|
||||
// Test vector methods (fastcall)
|
||||
lua.load(
|
||||
r#"
|
||||
local v = Vector3.new(1, 2, 3)
|
||||
local v2 = v:product(Vector3.new(2, 3, 4))
|
||||
assert(v2.x == 2 and v2.y == 6 and v2.z == 12)
|
||||
"#,
|
||||
)
|
||||
.set_compiler(compiler)
|
||||
.exec()?;
|
||||
|
||||
Ok(())
|
||||
@@ -198,6 +275,22 @@ fn test_sandbox() -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sandbox_nolibs() -> Result<()> {
|
||||
let lua = Lua::new_with(StdLib::NONE, LuaOptions::default()).unwrap();
|
||||
|
||||
lua.sandbox(true)?;
|
||||
lua.load("global = 123").exec()?;
|
||||
let n: i32 = lua.load("return global").eval()?;
|
||||
assert_eq!(n, 123);
|
||||
assert_eq!(lua.globals().get::<_, Option<i32>>("global")?, Some(123));
|
||||
|
||||
lua.sandbox(false)?;
|
||||
assert_eq!(lua.globals().get::<_, Option<i32>>("global")?, None);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sandbox_threads() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
@@ -365,3 +458,35 @@ fn test_coverage() -> Result<()> {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_buffer() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let buf1 = lua
|
||||
.load(
|
||||
r#"
|
||||
local buf = buffer.fromstring("hello")
|
||||
assert(buffer.len(buf) == 5)
|
||||
return buf
|
||||
"#,
|
||||
)
|
||||
.eval::<Value>()?;
|
||||
assert!(buf1.is_userdata() && buf1.is_buffer());
|
||||
assert_eq!(buf1.type_name(), "buffer");
|
||||
|
||||
let buf2 = lua.load("buffer.fromstring('hello')").eval::<Value>()?;
|
||||
assert_ne!(buf1, buf2);
|
||||
|
||||
// Check that we can pass buffer type to Lua
|
||||
let func = lua.create_function(|_, buf: Value| return buf.to_string())?;
|
||||
assert!(func.call::<_, String>(buf1)?.starts_with("buffer:"));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_fflags() {
|
||||
// We cannot really on any particular feature flag to be present
|
||||
assert!(Lua::set_fflag("UnknownFlag", true).is_err());
|
||||
}
|
||||
|
||||
+1
-1
@@ -84,7 +84,7 @@ fn test_gc_control() -> Result<()> {
|
||||
|
||||
assert_eq!(lua.gc_inc(200, 100, 13), GCMode::Incremental);
|
||||
|
||||
struct MyUserdata(Arc<()>);
|
||||
struct MyUserdata(#[allow(unused)] Arc<()>);
|
||||
impl UserData for MyUserdata {}
|
||||
|
||||
let rc = Arc::new(());
|
||||
|
||||
@@ -18,6 +18,7 @@ lua53 = ["mlua/lua53"]
|
||||
lua52 = ["mlua/lua52"]
|
||||
lua51 = ["mlua/lua51"]
|
||||
luajit = ["mlua/luajit"]
|
||||
luau = ["mlua/luau"]
|
||||
|
||||
[dependencies]
|
||||
mlua = { path = "../..", features = ["module"] }
|
||||
|
||||
@@ -10,6 +10,7 @@ lua53 = ["mlua/lua53"]
|
||||
lua52 = ["mlua/lua52"]
|
||||
lua51 = ["mlua/lua51"]
|
||||
luajit = ["mlua/luajit"]
|
||||
luau = ["mlua/luau"]
|
||||
vendored = ["mlua/vendored"]
|
||||
|
||||
[dependencies]
|
||||
|
||||
@@ -4,7 +4,7 @@ use std::path::PathBuf;
|
||||
use mlua::{Lua, Result};
|
||||
|
||||
#[test]
|
||||
fn test_module() -> Result<()> {
|
||||
fn test_module_simple() -> Result<()> {
|
||||
let lua = make_lua()?;
|
||||
lua.load(
|
||||
r#"
|
||||
@@ -59,7 +59,8 @@ fn test_module_from_thread() -> Result<()> {
|
||||
assert(mod.sum(a, b) == a + b)
|
||||
end)
|
||||
|
||||
coroutine.resume(co, 3, 5)
|
||||
local ok, err = coroutine.resume(co, 3, 5)
|
||||
assert(ok, err)
|
||||
collectgarbage()
|
||||
|
||||
assert(mod.used_memory() > 0)
|
||||
@@ -68,6 +69,29 @@ fn test_module_from_thread() -> Result<()> {
|
||||
.exec()
|
||||
}
|
||||
|
||||
#[cfg(any(
|
||||
feature = "lua54",
|
||||
feature = "lua53",
|
||||
feature = "lua52",
|
||||
feature = "lua51"
|
||||
))]
|
||||
#[test]
|
||||
fn test_module_multi_from_thread() -> Result<()> {
|
||||
let lua = make_lua()?;
|
||||
lua.load(
|
||||
r#"
|
||||
local mod = require("test_module")
|
||||
local co = coroutine.create(function()
|
||||
local mod2 = require("test_module.second")
|
||||
assert(mod2.userdata ~= nil)
|
||||
end)
|
||||
local ok, err = coroutine.resume(co)
|
||||
assert(ok, err)
|
||||
"#,
|
||||
)
|
||||
.exec()
|
||||
}
|
||||
|
||||
fn make_lua() -> Result<Lua> {
|
||||
let (dylib_path, dylib_ext, separator);
|
||||
if cfg!(target_os = "macos") {
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
use mlua::{Error, ExternalError, IntoLuaMulti, Lua, Result, String, Value};
|
||||
|
||||
#[test]
|
||||
fn test_result_conversions() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
let globals = lua.globals();
|
||||
|
||||
let ok = lua.create_function(|_, ()| Ok(Ok::<(), Error>(())))?;
|
||||
let err = lua.create_function(|_, ()| Ok(Err::<(), _>("failure1".into_lua_err())))?;
|
||||
let ok2 = lua.create_function(|_, ()| Ok(Ok::<_, Error>("!".to_owned())))?;
|
||||
let err2 = lua.create_function(|_, ()| Ok(Err::<String, _>("failure2".into_lua_err())))?;
|
||||
|
||||
globals.set("ok", ok)?;
|
||||
globals.set("ok2", ok2)?;
|
||||
globals.set("err", err)?;
|
||||
globals.set("err2", err2)?;
|
||||
|
||||
lua.load(
|
||||
r#"
|
||||
local r, e = ok()
|
||||
assert(r == nil and e == nil)
|
||||
|
||||
local r, e = err()
|
||||
assert(r == nil)
|
||||
assert(tostring(e):find("failure1") ~= nil)
|
||||
|
||||
local r, e = ok2()
|
||||
assert(r == "!")
|
||||
assert(e == nil)
|
||||
|
||||
local r, e = err2()
|
||||
assert(r == nil)
|
||||
assert(tostring(e):find("failure2") ~= nil)
|
||||
"#,
|
||||
)
|
||||
.exec()?;
|
||||
|
||||
// Try to convert Result into MultiValue
|
||||
let ok1 = Ok::<(), Error>(());
|
||||
let multi_ok1 = ok1.into_lua_multi(&lua)?;
|
||||
assert_eq!(multi_ok1.len(), 0);
|
||||
let err1 = Err::<(), _>("failure1");
|
||||
let multi_err1 = err1.into_lua_multi(&lua)?;
|
||||
assert_eq!(multi_err1.len(), 2);
|
||||
assert_eq!(multi_err1[0], Value::Nil);
|
||||
assert_eq!(multi_err1[1].as_str().unwrap(), "failure1");
|
||||
|
||||
let ok2 = Ok::<_, Error>("!");
|
||||
let multi_ok2 = ok2.into_lua_multi(&lua)?;
|
||||
assert_eq!(multi_ok2.len(), 1);
|
||||
assert_eq!(multi_ok2[0].as_str().unwrap(), "!");
|
||||
let err2 = Err::<String, _>("failure2".into_lua_err());
|
||||
let multi_err2 = err2.into_lua_multi(&lua)?;
|
||||
assert_eq!(multi_err2.len(), 2);
|
||||
assert_eq!(multi_err2[0], Value::Nil);
|
||||
assert!(matches!(multi_err2[1], Value::Error(_)));
|
||||
assert_eq!(multi_err2[1].to_string()?, "failure2");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
+52
-13
@@ -1,5 +1,6 @@
|
||||
use std::cell::Cell;
|
||||
use std::rc::Rc;
|
||||
use std::string::String as StdString;
|
||||
use std::sync::Arc;
|
||||
|
||||
use mlua::{
|
||||
@@ -76,6 +77,7 @@ fn test_scope_userdata_fields() -> Result<()> {
|
||||
|
||||
impl<'a> UserData for MyUserData<'a> {
|
||||
fn add_fields<'lua, F: UserDataFields<'lua, Self>>(fields: &mut F) {
|
||||
fields.add_field("field", "hello");
|
||||
fields.add_field_method_get("val", |_, data| Ok(data.0.get()));
|
||||
fields.add_field_method_set("val", |_, data, val| {
|
||||
data.0.set(val);
|
||||
@@ -91,6 +93,7 @@ fn test_scope_userdata_fields() -> Result<()> {
|
||||
.load(
|
||||
r#"
|
||||
function(u)
|
||||
assert(u.field == "hello")
|
||||
assert(u.val == 42)
|
||||
u.val = 44
|
||||
end
|
||||
@@ -152,14 +155,14 @@ fn test_scope_userdata_functions() -> Result<()> {
|
||||
|
||||
impl<'a> UserData for MyUserData<'a> {
|
||||
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
|
||||
methods.add_meta_function(MetaMethod::Add, |lua, ()| {
|
||||
methods.add_meta_method(MetaMethod::Add, |lua, this, ()| {
|
||||
let globals = lua.globals();
|
||||
globals.set("i", globals.get::<_, i64>("i")? + 1)?;
|
||||
globals.set("i", globals.get::<_, i64>("i")? + this.0)?;
|
||||
Ok(())
|
||||
});
|
||||
methods.add_meta_function(MetaMethod::Sub, |lua, ()| {
|
||||
methods.add_meta_method(MetaMethod::Sub, |lua, this, ()| {
|
||||
let globals = lua.globals();
|
||||
globals.set("i", globals.get::<_, i64>("i")? + 1)?;
|
||||
globals.set("i", globals.get::<_, i64>("i")? + this.0)?;
|
||||
Ok(())
|
||||
});
|
||||
}
|
||||
@@ -167,7 +170,7 @@ fn test_scope_userdata_functions() -> Result<()> {
|
||||
|
||||
let lua = Lua::new();
|
||||
|
||||
let dummy = 0;
|
||||
let dummy = 1;
|
||||
let f = lua
|
||||
.load(
|
||||
r#"
|
||||
@@ -175,7 +178,7 @@ fn test_scope_userdata_functions() -> Result<()> {
|
||||
return function(u)
|
||||
_ = u + u
|
||||
_ = u - 1
|
||||
_ = 1 + u
|
||||
_ = u + 1
|
||||
end
|
||||
"#,
|
||||
)
|
||||
@@ -227,9 +230,19 @@ fn test_scope_userdata_mismatch() -> Result<()> {
|
||||
let bu = scope.create_nonstatic_userdata(MyUserData(&b))?;
|
||||
assert!(okay.call::<_, ()>((au.clone(), bu.clone())).is_ok());
|
||||
match bad.call::<_, ()>((au, bu)) {
|
||||
Err(Error::CallbackError { ref cause, .. }) => match *cause.as_ref() {
|
||||
Error::UserDataTypeMismatch => {}
|
||||
ref other => panic!("wrong error type {:?}", other),
|
||||
Err(Error::CallbackError { ref cause, .. }) => match cause.as_ref() {
|
||||
Error::BadArgument {
|
||||
to,
|
||||
pos,
|
||||
name,
|
||||
cause,
|
||||
} => {
|
||||
assert_eq!(to.as_deref(), Some("MyUserData.inc"));
|
||||
assert_eq!(*pos, 1);
|
||||
assert_eq!(name.as_deref(), Some("self"));
|
||||
assert!(matches!(*cause.as_ref(), Error::UserDataTypeMismatch));
|
||||
}
|
||||
other => panic!("wrong error type {:?}", other),
|
||||
},
|
||||
Err(other) => panic!("wrong error type {:?}", other),
|
||||
Ok(_) => panic!("incorrectly returned Ok"),
|
||||
@@ -244,7 +257,7 @@ fn test_scope_userdata_mismatch() -> Result<()> {
|
||||
fn test_scope_userdata_drop() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
struct MyUserData(Rc<()>);
|
||||
struct MyUserData(#[allow(unused)] Rc<()>);
|
||||
|
||||
impl UserData for MyUserData {
|
||||
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
|
||||
@@ -252,7 +265,7 @@ fn test_scope_userdata_drop() -> Result<()> {
|
||||
}
|
||||
}
|
||||
|
||||
struct MyUserDataArc(Arc<()>);
|
||||
struct MyUserDataArc(#[allow(unused)] Arc<()>);
|
||||
|
||||
impl UserData for MyUserDataArc {}
|
||||
|
||||
@@ -302,7 +315,7 @@ fn test_scope_userdata_drop() -> Result<()> {
|
||||
fn test_scope_nonstatic_userdata_drop() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
struct MyUserData<'a>(&'a Cell<i64>, Arc<()>);
|
||||
struct MyUserData<'a>(&'a Cell<i64>, #[allow(unused)] Arc<()>);
|
||||
|
||||
impl<'a> UserData for MyUserData<'a> {
|
||||
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
|
||||
@@ -313,7 +326,7 @@ fn test_scope_nonstatic_userdata_drop() -> Result<()> {
|
||||
}
|
||||
}
|
||||
|
||||
struct MyUserDataArc(Arc<()>);
|
||||
struct MyUserDataArc(#[allow(unused)] Arc<()>);
|
||||
|
||||
impl UserData for MyUserDataArc {}
|
||||
|
||||
@@ -417,6 +430,32 @@ fn test_scope_userdata_ref_mut() -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
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()));
|
||||
})?;
|
||||
|
||||
lua.scope(|scope| {
|
||||
let ud = scope.create_any_userdata(StdString::from("foo"))?;
|
||||
lua.globals().set("ud", ud)?;
|
||||
lua.load("assert(tostring(ud) == 'foo')").exec()
|
||||
})?;
|
||||
|
||||
// Check that userdata is destructed
|
||||
match lua.load("tostring(ud)").exec() {
|
||||
Err(Error::CallbackError { ref cause, .. }) => match cause.as_ref() {
|
||||
Error::CallbackDestructed => {}
|
||||
err => panic!("expected CallbackDestructed, got {:?}", err),
|
||||
},
|
||||
r => panic!("improper return for destructed userdata: {:?}", r),
|
||||
};
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_scope_any_userdata_ref() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
+184
-4
@@ -4,8 +4,8 @@ use std::collections::HashMap;
|
||||
use std::error::Error as StdError;
|
||||
|
||||
use mlua::{
|
||||
DeserializeOptions, Error, Lua, LuaSerdeExt, Result as LuaResult, SerializeOptions, UserData,
|
||||
Value,
|
||||
DeserializeOptions, Error, ExternalResult, Lua, LuaSerdeExt, Result as LuaResult,
|
||||
SerializeOptions, UserData, Value,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
@@ -99,7 +99,7 @@ fn test_serialize_in_scope() -> LuaResult<()> {
|
||||
Err(e) => panic!("expected destructed error, got {}", e),
|
||||
}
|
||||
|
||||
struct MyUserDataRef<'a>(&'a ());
|
||||
struct MyUserDataRef<'a>(#[allow(unused)] &'a ());
|
||||
|
||||
impl<'a> UserData for MyUserDataRef<'a> {}
|
||||
|
||||
@@ -115,6 +115,21 @@ fn test_serialize_in_scope() -> LuaResult<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_serialize_any_userdata() -> Result<(), Box<dyn StdError>> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let json_val = serde_json::json!({
|
||||
"a": 1,
|
||||
"b": "test",
|
||||
});
|
||||
let json_ud = lua.create_ser_any_userdata(json_val)?;
|
||||
let json_str = serde_json::to_string_pretty(&json_ud)?;
|
||||
assert_eq!(json_str, "{\n \"a\": 1,\n \"b\": \"test\"\n}");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_serialize_failure() -> Result<(), Box<dyn StdError>> {
|
||||
#[derive(Serialize)]
|
||||
@@ -191,6 +206,90 @@ fn test_serialize_vector() -> Result<(), Box<dyn StdError>> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_serialize_sorted() -> LuaResult<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let globals = lua.globals();
|
||||
globals.set("null", lua.null())?;
|
||||
|
||||
let empty_array = lua.create_table()?;
|
||||
empty_array.set_metatable(Some(lua.array_metatable()));
|
||||
globals.set("empty_array", empty_array)?;
|
||||
|
||||
let value = lua
|
||||
.load(
|
||||
r#"
|
||||
{
|
||||
_bool = true,
|
||||
_integer = 123,
|
||||
_number = 321.99,
|
||||
_string = "test string serialization",
|
||||
_table_arr = {nil, "value 1", nil, "value 2", {}},
|
||||
_table_map = {["table"] = "map", ["null"] = null},
|
||||
_bytes = "\240\040\140\040",
|
||||
_null = null,
|
||||
_empty_map = {},
|
||||
_empty_array = empty_array,
|
||||
}
|
||||
"#,
|
||||
)
|
||||
.eval::<Value>()?;
|
||||
|
||||
let json = serde_json::to_string(&value.to_serializable().sort_keys(true)).unwrap();
|
||||
assert_eq!(
|
||||
json,
|
||||
r#"{"_bool":true,"_bytes":[240,40,140,40],"_empty_array":[],"_empty_map":{},"_integer":123,"_null":null,"_number":321.99,"_string":"test string serialization","_table_arr":[null,"value 1",null,"value 2",{}],"_table_map":{"null":null,"table":"map"}}"#
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_serialize_globals() -> LuaResult<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let globals = Value::Table(lua.globals());
|
||||
|
||||
// By default it should not work
|
||||
if let Ok(v) = serde_json::to_value(&globals) {
|
||||
panic!("expected serialization error, got {v:?}");
|
||||
}
|
||||
|
||||
// It should work with `deny_recursive_tables` and `deny_unsupported_types` disabled
|
||||
if let Err(err) = serde_json::to_value(
|
||||
globals
|
||||
.to_serializable()
|
||||
.deny_recursive_tables(false)
|
||||
.deny_unsupported_types(false),
|
||||
) {
|
||||
panic!("expected no errors, got {err:?}");
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_serialize_same_table_twice() -> LuaResult<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let value = lua
|
||||
.load(
|
||||
r#"
|
||||
local foo = {}
|
||||
return {
|
||||
a = foo,
|
||||
b = foo,
|
||||
}
|
||||
"#,
|
||||
)
|
||||
.eval::<Value>()?;
|
||||
let json = serde_json::to_string(&value.to_serializable().sort_keys(true)).unwrap();
|
||||
assert_eq!(json, r#"{"a":{},"b":{}}"#);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_to_value_struct() -> LuaResult<()> {
|
||||
let lua = Lua::new();
|
||||
@@ -535,7 +634,7 @@ fn test_from_value_with_options() -> Result<(), Box<dyn StdError>> {
|
||||
|
||||
// Check recursion when using `Serialize` impl
|
||||
let t = lua.create_table()?;
|
||||
t.set("t", t.clone())?;
|
||||
t.set("t", &t)?;
|
||||
assert!(serde_json::to_string(&t).is_err());
|
||||
|
||||
// Serialize Lua globals table
|
||||
@@ -611,3 +710,84 @@ fn test_from_value_userdata() -> Result<(), Box<dyn StdError>> {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_from_value_sorted() -> Result<(), Box<dyn StdError>> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let to_json = lua.create_function(|lua, value| {
|
||||
let json_value: serde_json::Value =
|
||||
lua.from_value_with(value, DeserializeOptions::new().sort_keys(true))?;
|
||||
serde_json::to_string(&json_value).into_lua_err()
|
||||
})?;
|
||||
lua.globals().set("to_json", to_json)?;
|
||||
|
||||
lua.load(
|
||||
r#"
|
||||
local json = to_json({c = 3, b = 2, hello = "world", x = {1}, ["0a"] = {z = "z", d = "d"}})
|
||||
assert(json == '{"0a":{"d":"d","z":"z"},"b":2,"c":3,"hello":"world","x":[1]}', "invalid json")
|
||||
"#,
|
||||
)
|
||||
.exec()
|
||||
.unwrap();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_arbitrary_precision() {
|
||||
let lua = Lua::new();
|
||||
|
||||
let opts = SerializeOptions::new().detect_serde_json_arbitrary_precision(true);
|
||||
|
||||
// Number
|
||||
let num = serde_json::Value::Number(serde_json::Number::from_f64(1.244e2).unwrap());
|
||||
let num = lua.to_value_with(&num, opts).unwrap();
|
||||
assert_eq!(num, Value::Number(1.244e2));
|
||||
|
||||
// Integer
|
||||
let num = serde_json::Value::Number(serde_json::Number::from_f64(123.0).unwrap());
|
||||
let num = lua.to_value_with(&num, opts).unwrap();
|
||||
assert_eq!(num, Value::Integer(123));
|
||||
|
||||
// Max u64
|
||||
let num = serde_json::Value::Number(serde_json::Number::from(i64::MAX));
|
||||
let num = lua.to_value_with(&num, opts).unwrap();
|
||||
assert_eq!(num, Value::Number(i64::MAX as f64));
|
||||
|
||||
// Check that the option is disabled by default
|
||||
let num = serde_json::Value::Number(serde_json::Number::from_f64(1.244e2).unwrap());
|
||||
let num = lua.to_value(&num).unwrap();
|
||||
assert_eq!(num.type_name(), "table");
|
||||
assert_eq!(
|
||||
format!("{:#?}", num),
|
||||
"{\n [\"$serde_json::private::Number\"] = \"124.4\",\n}"
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(feature = "luau")]
|
||||
#[test]
|
||||
fn test_buffer_serialize() {
|
||||
let lua = Lua::new();
|
||||
|
||||
let buf = lua.create_buffer(&[1, 2, 3, 4]).unwrap();
|
||||
let val = serde_value::to_value(&buf).unwrap();
|
||||
assert_eq!(val, serde_value::Value::Bytes(vec![1, 2, 3, 4]));
|
||||
|
||||
// Try empty buffer
|
||||
let buf = lua.create_buffer(&[]).unwrap();
|
||||
let val = serde_value::to_value(&buf).unwrap();
|
||||
assert_eq!(val, serde_value::Value::Bytes(vec![]));
|
||||
}
|
||||
|
||||
#[cfg(feature = "luau")]
|
||||
#[test]
|
||||
fn test_buffer_from_value() {
|
||||
let lua = Lua::new();
|
||||
|
||||
let buf = lua.create_buffer(&[1, 2, 3, 4]).unwrap();
|
||||
let val = lua
|
||||
.from_value::<serde_value::Value>(Value::UserData(buf))
|
||||
.unwrap();
|
||||
assert_eq!(val, serde_value::Value::Bytes(vec![1, 2, 3, 4]));
|
||||
}
|
||||
|
||||
+11
-2
@@ -73,13 +73,22 @@ fn test_static_lua_coroutine() -> Result<()> {
|
||||
async fn test_static_async() -> Result<()> {
|
||||
let lua = Lua::new().into_static();
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
async fn sleep_ms(ms: u64) {
|
||||
tokio::time::sleep(std::time::Duration::from_millis(ms)).await;
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
async fn sleep_ms(_ms: u64) {
|
||||
tokio::task::yield_now().await;
|
||||
}
|
||||
|
||||
let timer =
|
||||
lua.create_async_function(|_, (i, n, f): (u64, u64, mlua::Function)| async move {
|
||||
tokio::task::spawn_local(async move {
|
||||
let dur = std::time::Duration::from_millis(i);
|
||||
for _ in 0..n {
|
||||
tokio::task::spawn_local(f.call_async::<(), ()>(()));
|
||||
tokio::time::sleep(dur).await;
|
||||
sleep_ms(i).await;
|
||||
}
|
||||
});
|
||||
Ok(())
|
||||
|
||||
@@ -99,6 +99,19 @@ fn test_string_debug() -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_string_pointer() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let str1 = lua.create_string("hello")?;
|
||||
let str2 = lua.create_string("hello")?;
|
||||
|
||||
// Lua uses string interning, so these should be the same
|
||||
assert_eq!(str1.to_pointer(), str2.to_pointer());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "unstable", not(feature = "send")))]
|
||||
#[test]
|
||||
fn test_owned_string() -> Result<()> {
|
||||
|
||||
@@ -217,6 +217,74 @@ fn test_table_sequence_from() -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_table_pairs() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let table = lua
|
||||
.load(
|
||||
r#"
|
||||
{
|
||||
foo = "bar",
|
||||
baz = "baf",
|
||||
[123] = 456,
|
||||
[789] = 101112,
|
||||
5,
|
||||
}
|
||||
"#,
|
||||
)
|
||||
.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")?,
|
||||
// Try to delete the 2nd key
|
||||
1 => {
|
||||
table2.set(k, Value::Nil)?;
|
||||
lua.gc_collect()?;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_table_for_each() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let table = lua
|
||||
.load(
|
||||
r#"
|
||||
{
|
||||
foo = "bar",
|
||||
baz = "baf",
|
||||
[123] = 456,
|
||||
[789] = 101112,
|
||||
5,
|
||||
}
|
||||
"#,
|
||||
)
|
||||
.eval::<Table>()?;
|
||||
|
||||
let mut i = 0;
|
||||
table.for_each::<String, Value>(|k, _| {
|
||||
if i == 0 {
|
||||
// Delete first key
|
||||
table.set(k, Value::Nil)?;
|
||||
lua.gc_collect()?;
|
||||
}
|
||||
Ok(i += 1)
|
||||
})?;
|
||||
assert_eq!(i, 5);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_table_scope() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
@@ -301,6 +369,19 @@ fn test_table_eq() -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_table_pointer() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let table1 = lua.create_table()?;
|
||||
let table2 = lua.create_table()?;
|
||||
|
||||
assert_eq!(table1.to_pointer(), table1.clone().to_pointer());
|
||||
assert_ne!(table1.to_pointer(), table2.to_pointer());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_table_error() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
+37
-52
@@ -1,4 +1,5 @@
|
||||
use std::collections::HashMap;
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
use std::iter::FromIterator;
|
||||
use std::panic::{catch_unwind, AssertUnwindSafe};
|
||||
use std::string::String as StdString;
|
||||
@@ -312,30 +313,29 @@ fn test_error() -> Result<()> {
|
||||
globals.set("rust_error_function", rust_error_function)?;
|
||||
|
||||
let no_error = globals.get::<_, Function>("no_error")?;
|
||||
let lua_error = globals.get::<_, Function>("lua_error")?;
|
||||
let rust_error = globals.get::<_, Function>("rust_error")?;
|
||||
let return_error = globals.get::<_, Function>("return_error")?;
|
||||
let return_string_error = globals.get::<_, Function>("return_string_error")?;
|
||||
let test_pcall = globals.get::<_, Function>("test_pcall")?;
|
||||
let understand_recursion = globals.get::<_, Function>("understand_recursion")?;
|
||||
|
||||
assert!(no_error.call::<_, ()>(()).is_ok());
|
||||
|
||||
let lua_error = globals.get::<_, Function>("lua_error")?;
|
||||
match lua_error.call::<_, ()>(()) {
|
||||
Err(Error::RuntimeError(_)) => {}
|
||||
Err(e) => panic!("error is not RuntimeError kind, got {:?}", e),
|
||||
_ => panic!("error not returned"),
|
||||
}
|
||||
|
||||
let rust_error = globals.get::<_, Function>("rust_error")?;
|
||||
match rust_error.call::<_, ()>(()) {
|
||||
Err(Error::CallbackError { .. }) => {}
|
||||
Err(e) => panic!("error is not CallbackError kind, got {:?}", e),
|
||||
_ => panic!("error not returned"),
|
||||
}
|
||||
|
||||
let return_error = globals.get::<_, Function>("return_error")?;
|
||||
match return_error.call::<_, Value>(()) {
|
||||
Ok(Value::Error(_)) => {}
|
||||
_ => panic!("Value::Error not returned"),
|
||||
}
|
||||
|
||||
let return_string_error = globals.get::<_, Function>("return_string_error")?;
|
||||
assert!(return_string_error.call::<_, Error>(()).is_ok());
|
||||
|
||||
match lua
|
||||
@@ -358,9 +358,14 @@ fn test_error() -> Result<()> {
|
||||
_ => panic!("error not returned"),
|
||||
}
|
||||
|
||||
let test_pcall = globals.get::<_, Function>("test_pcall")?;
|
||||
test_pcall.call::<_, ()>(())?;
|
||||
|
||||
assert!(understand_recursion.call::<_, ()>(()).is_err());
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
{
|
||||
let understand_recursion = globals.get::<_, Function>("understand_recursion")?;
|
||||
assert!(understand_recursion.call::<_, ()>(()).is_err());
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -499,37 +504,6 @@ fn test_panic() -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_result_conversions() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
let globals = lua.globals();
|
||||
|
||||
let err = lua.create_function(|_, ()| {
|
||||
Ok(Err::<String, _>(
|
||||
"only through failure can we succeed".into_lua_err(),
|
||||
))
|
||||
})?;
|
||||
let ok = lua.create_function(|_, ()| Ok(Ok::<_, Error>("!".to_owned())))?;
|
||||
|
||||
globals.set("err", err)?;
|
||||
globals.set("ok", ok)?;
|
||||
|
||||
lua.load(
|
||||
r#"
|
||||
local r, e = err()
|
||||
assert(r == nil)
|
||||
assert(tostring(e):find("only through failure can we succeed") ~= nil)
|
||||
|
||||
local r, e = ok()
|
||||
assert(r == "!")
|
||||
assert(e == nil)
|
||||
"#,
|
||||
)
|
||||
.exec()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_num_conversion() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
@@ -769,7 +743,7 @@ fn test_registry_value() -> Result<()> {
|
||||
|
||||
#[test]
|
||||
fn test_drop_registry_value() -> Result<()> {
|
||||
struct MyUserdata(Arc<()>);
|
||||
struct MyUserdata(#[allow(unused)] Arc<()>);
|
||||
|
||||
impl UserData for MyUserdata {}
|
||||
|
||||
@@ -801,12 +775,11 @@ fn test_replace_registry_value() -> Result<()> {
|
||||
lua.replace_registry_value(&key, 123)?;
|
||||
assert_eq!(lua.registry_value::<i32>(&key)?, 123);
|
||||
|
||||
// It should be impossible to replace (initial) nil value with non-nil
|
||||
let key2 = lua.create_registry_value(Value::Nil)?;
|
||||
match lua.replace_registry_value(&key2, "abc") {
|
||||
Err(Error::RuntimeError(_)) => {}
|
||||
r => panic!("expected RuntimeError, got {r:?}"),
|
||||
}
|
||||
lua.replace_registry_value(&key2, Value::Nil)?;
|
||||
assert_eq!(lua.registry_value::<Value>(&key2)?, Value::Nil);
|
||||
lua.replace_registry_value(&key2, "abc")?;
|
||||
assert_eq!(lua.registry_value::<String>(&key2)?, "abc");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -940,6 +913,7 @@ fn test_application_data() -> Result<()> {
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
fn test_recursion() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
@@ -952,21 +926,23 @@ fn test_recursion() -> Result<()> {
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
lua.globals().set("f", f.clone())?;
|
||||
lua.globals().set("f", &f)?;
|
||||
f.call::<_, ()>(1)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
fn test_too_many_returns() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
let f = lua.create_function(|_, ()| Ok(Variadic::from_iter(1..1000000)))?;
|
||||
assert!(f.call::<_, Vec<u32>>(()).is_err());
|
||||
assert!(f.call::<_, Variadic<u32>>(()).is_err());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
fn test_too_many_arguments() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
lua.load("function test(...) end").exec()?;
|
||||
@@ -981,19 +957,21 @@ fn test_too_many_arguments() -> Result<()> {
|
||||
|
||||
#[test]
|
||||
#[cfg(not(feature = "luajit"))]
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
fn test_too_many_recursions() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let f = lua
|
||||
.create_function(move |lua, ()| lua.globals().get::<_, Function>("f")?.call::<_, ()>(()))?;
|
||||
|
||||
lua.globals().set("f", f.clone())?;
|
||||
lua.globals().set("f", &f)?;
|
||||
assert!(f.call::<_, ()>(()).is_err());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
fn test_too_many_binds() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
let globals = lua.globals();
|
||||
@@ -1015,6 +993,7 @@ fn test_too_many_binds() -> Result<()> {
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
fn test_ref_stack_exhaustion() {
|
||||
match catch_unwind(AssertUnwindSafe(|| -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
@@ -1318,10 +1297,10 @@ fn test_warnings() -> Result<()> {
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "luajit")]
|
||||
#[should_panic]
|
||||
fn test_luajit_cdata() {
|
||||
fn test_luajit_cdata() -> Result<()> {
|
||||
let lua = unsafe { Lua::unsafe_new() };
|
||||
let _v: Result<Value> = lua
|
||||
|
||||
let cdata = lua
|
||||
.load(
|
||||
r#"
|
||||
local ffi = require("ffi")
|
||||
@@ -1334,11 +1313,17 @@ fn test_luajit_cdata() {
|
||||
return ptr
|
||||
"#,
|
||||
)
|
||||
.eval();
|
||||
.eval::<Value>()?;
|
||||
assert!(cdata.is_userdata() && cdata.is_cdata());
|
||||
assert_eq!(cdata.type_name(), "cdata");
|
||||
assert!(cdata.to_string()?.starts_with("cdata<void *>:"));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "send")]
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
fn test_send() {
|
||||
let lua = Lua::new();
|
||||
std::thread::spawn(move || {
|
||||
|
||||
+74
-2
@@ -90,6 +90,19 @@ fn test_thread() -> Result<()> {
|
||||
_ => panic!("resuming dead coroutine did not return error"),
|
||||
}
|
||||
|
||||
// Already running thread must be unresumable
|
||||
let thread = lua.create_thread(lua.create_function(|lua, ()| {
|
||||
assert_eq!(lua.current_thread().status(), ThreadStatus::Unresumable);
|
||||
let result = lua.current_thread().resume::<_, ()>(());
|
||||
assert!(
|
||||
matches!(result, Err(Error::CoroutineInactive)),
|
||||
"unexpected result: {result:?}",
|
||||
);
|
||||
Ok(())
|
||||
})?)?;
|
||||
let result = thread.resume::<_, ()>(());
|
||||
assert!(result.is_ok(), "unexpected result: {result:?}");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -101,7 +114,7 @@ fn test_thread_reset() -> Result<()> {
|
||||
|
||||
let lua = Lua::new();
|
||||
|
||||
struct MyUserData(Arc<()>);
|
||||
struct MyUserData(#[allow(unused)] Arc<()>);
|
||||
impl UserData for MyUserData {}
|
||||
|
||||
let arc = Arc::new(());
|
||||
@@ -146,6 +159,21 @@ fn test_thread_reset() -> Result<()> {
|
||||
assert_eq!(thread.status(), ThreadStatus::Resumable);
|
||||
}
|
||||
|
||||
// Try reset running thread
|
||||
let thread = lua.create_thread(lua.create_function(|lua, ()| {
|
||||
let this = lua.current_thread();
|
||||
this.reset(lua.create_function(|_, ()| Ok(()))?)?;
|
||||
Ok(())
|
||||
})?)?;
|
||||
let result = thread.resume::<_, ()>(());
|
||||
assert!(
|
||||
matches!(result, Err(Error::CallbackError{ ref cause, ..})
|
||||
if matches!(cause.as_ref(), Error::RuntimeError(ref err)
|
||||
if err == "cannot reset a running thread")
|
||||
),
|
||||
"unexpected result: {result:?}",
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -182,7 +210,7 @@ fn test_coroutine_panic() {
|
||||
let thrd_main = lua.create_function(|_, ()| -> Result<()> {
|
||||
panic!("test_panic");
|
||||
})?;
|
||||
lua.globals().set("main", thrd_main.clone())?;
|
||||
lua.globals().set("main", &thrd_main)?;
|
||||
let thrd: Thread = lua.create_thread(thrd_main)?;
|
||||
thrd.resume(())
|
||||
}) {
|
||||
@@ -190,3 +218,47 @@ fn test_coroutine_panic() {
|
||||
Err(p) => assert!(*p.downcast::<&str>().unwrap() == "test_panic"),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_thread_pointer() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let func = lua.load("return 123").into_function()?;
|
||||
let thread = lua.create_thread(func.clone())?;
|
||||
|
||||
assert_eq!(thread.to_pointer(), thread.clone().to_pointer());
|
||||
assert_ne!(thread.to_pointer(), lua.current_thread().to_pointer());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "unstable", not(feature = "send")))]
|
||||
#[test]
|
||||
fn test_owned_thread() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let accumulate = lua
|
||||
.create_thread(
|
||||
lua.load(
|
||||
r#"
|
||||
function (sum)
|
||||
while true do
|
||||
sum = sum + coroutine.yield(sum)
|
||||
end
|
||||
end
|
||||
"#,
|
||||
)
|
||||
.eval::<Function>()?,
|
||||
)?
|
||||
.into_owned();
|
||||
|
||||
for i in 0..4 {
|
||||
accumulate.resume::<_, ()>(i)?;
|
||||
}
|
||||
assert_eq!(accumulate.resume::<_, i64>(4)?, 10);
|
||||
assert_eq!(accumulate.status(), ThreadStatus::Resumable);
|
||||
assert!(accumulate.resume::<_, ()>("error").is_err());
|
||||
assert_eq!(accumulate.status(), ThreadStatus::Error);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
+77
-9
@@ -15,7 +15,7 @@ use std::sync::atomic::{AtomicI64, Ordering};
|
||||
|
||||
use mlua::{
|
||||
AnyUserData, AnyUserDataExt, Error, ExternalError, Function, Lua, MetaMethod, Nil, Result,
|
||||
String, UserData, UserDataFields, UserDataMethods, UserDataRef, Value,
|
||||
String, UserData, UserDataFields, UserDataMethods, UserDataRef, Value, Variadic,
|
||||
};
|
||||
|
||||
#[test]
|
||||
@@ -58,7 +58,7 @@ fn test_methods() -> Result<()> {
|
||||
|
||||
fn check_methods(lua: &Lua, userdata: AnyUserData) -> Result<()> {
|
||||
let globals = lua.globals();
|
||||
globals.set("userdata", userdata.clone())?;
|
||||
globals.set("userdata", &userdata)?;
|
||||
lua.load(
|
||||
r#"
|
||||
function get_it()
|
||||
@@ -92,6 +92,30 @@ fn test_methods() -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_method_variadic() -> Result<()> {
|
||||
struct MyUserData(i64);
|
||||
|
||||
impl UserData for MyUserData {
|
||||
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
|
||||
methods.add_method("get", |_, data, ()| Ok(data.0));
|
||||
methods.add_method_mut("add", |_, data, vals: Variadic<i64>| {
|
||||
data.0 += vals.into_iter().sum::<i64>();
|
||||
Ok(())
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
let lua = Lua::new();
|
||||
let globals = lua.globals();
|
||||
globals.set("userdata", MyUserData(0))?;
|
||||
lua.load("userdata:add(1, 5, -10)").exec()?;
|
||||
let ud: UserDataRef<MyUserData> = globals.get("userdata")?;
|
||||
assert_eq!(ud.0, -4);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_metamethods() -> Result<()> {
|
||||
#[derive(Copy, Clone)]
|
||||
@@ -318,7 +342,7 @@ fn test_userdata_take() -> Result<()> {
|
||||
}
|
||||
|
||||
fn check_userdata_take(lua: &Lua, userdata: AnyUserData, rc: Arc<i64>) -> Result<()> {
|
||||
lua.globals().set("userdata", userdata.clone())?;
|
||||
lua.globals().set("userdata", &userdata)?;
|
||||
assert_eq!(Arc::strong_count(&rc), 3);
|
||||
{
|
||||
let _value = userdata.borrow::<MyUserdata>()?;
|
||||
@@ -346,6 +370,8 @@ fn test_userdata_take() -> Result<()> {
|
||||
r => panic!("improper return for destructed userdata: {:?}", r),
|
||||
}
|
||||
|
||||
assert!(!userdata.is::<MyUserdata>());
|
||||
|
||||
drop(userdata);
|
||||
lua.globals().raw_remove("userdata")?;
|
||||
lua.gc_collect()?;
|
||||
@@ -376,7 +402,7 @@ fn test_userdata_take() -> Result<()> {
|
||||
|
||||
#[test]
|
||||
fn test_userdata_destroy() -> Result<()> {
|
||||
struct MyUserdata(Arc<()>);
|
||||
struct MyUserdata(#[allow(unused)] Arc<()>);
|
||||
|
||||
impl UserData for MyUserdata {}
|
||||
|
||||
@@ -450,7 +476,7 @@ fn test_functions() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
let globals = lua.globals();
|
||||
let userdata = lua.create_userdata(MyUserData(42))?;
|
||||
globals.set("userdata", userdata.clone())?;
|
||||
globals.set("userdata", &userdata)?;
|
||||
lua.load(
|
||||
r#"
|
||||
function get_it()
|
||||
@@ -482,6 +508,9 @@ fn test_functions() -> Result<()> {
|
||||
|
||||
#[test]
|
||||
fn test_fields() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
let globals = lua.globals();
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
struct MyUserData(i64);
|
||||
|
||||
@@ -509,8 +538,6 @@ fn test_fields() -> Result<()> {
|
||||
}
|
||||
}
|
||||
|
||||
let lua = Lua::new();
|
||||
let globals = lua.globals();
|
||||
globals.set("ud", MyUserData(7))?;
|
||||
lua.load(
|
||||
r#"
|
||||
@@ -531,6 +558,33 @@ fn test_fields() -> Result<()> {
|
||||
)
|
||||
.exec()?;
|
||||
|
||||
// Case: fields + __index metamethod (function)
|
||||
struct MyUserData2(i64);
|
||||
|
||||
impl UserData for MyUserData2 {
|
||||
fn add_fields<'lua, F: UserDataFields<'lua, Self>>(fields: &mut F) {
|
||||
fields.add_field("z", 0);
|
||||
fields.add_field_method_get("x", |_, data| Ok(data.0));
|
||||
}
|
||||
|
||||
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
|
||||
methods.add_meta_method(MetaMethod::Index, |_, _, name: StdString| match &*name {
|
||||
"y" => Ok(Some(-1)),
|
||||
_ => Ok(None),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
globals.set("ud", MyUserData2(1))?;
|
||||
lua.load(
|
||||
r#"
|
||||
assert(ud.x == 1)
|
||||
assert(ud.y == -1)
|
||||
assert(ud.z == 0)
|
||||
"#,
|
||||
)
|
||||
.exec()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -899,6 +953,20 @@ fn test_userdata_method_errors() -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_userdata_pointer() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let ud1 = lua.create_any_userdata("hello")?;
|
||||
let ud2 = lua.create_any_userdata("hello")?;
|
||||
|
||||
assert_eq!(ud1.to_pointer(), ud1.clone().to_pointer());
|
||||
// Different userdata objects with the same value should have different pointers
|
||||
assert_ne!(ud1.to_pointer(), ud2.to_pointer());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "unstable", not(feature = "send")))]
|
||||
#[test]
|
||||
fn test_owned_userdata() -> Result<()> {
|
||||
@@ -939,9 +1007,9 @@ fn test_userdata_derive() -> Result<()> {
|
||||
// More complex struct where generics and where clause
|
||||
|
||||
#[derive(Clone, Copy, mlua::FromLua)]
|
||||
struct MyUserData2<'a, T>(&'a T)
|
||||
struct MyUserData2<'a, T: ?Sized>(&'a T)
|
||||
where
|
||||
T: ?Sized;
|
||||
T: Copy;
|
||||
|
||||
lua.register_userdata_type::<MyUserData2<'static, i32>>(|reg| {
|
||||
reg.add_function("val", |_, this: MyUserData2<'static, i32>| Ok(*this.0));
|
||||
|
||||
@@ -158,3 +158,74 @@ fn test_debug_format() -> Result<()> {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_value_conversions() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
assert!(Value::Nil.is_nil());
|
||||
assert!(!Value::NULL.is_nil());
|
||||
assert!(Value::NULL.is_null());
|
||||
assert!(Value::NULL.is_light_userdata());
|
||||
assert!(Value::NULL.as_light_userdata() == Some(LightUserData(ptr::null_mut())));
|
||||
assert!(Value::Boolean(true).is_boolean());
|
||||
assert_eq!(Value::Boolean(false).as_boolean(), Some(false));
|
||||
assert!(Value::Integer(1).is_integer());
|
||||
assert_eq!(Value::Integer(1).as_integer(), Some(1));
|
||||
assert_eq!(Value::Integer(1).as_i32(), Some(1i32));
|
||||
assert_eq!(Value::Integer(1).as_u32(), Some(1u32));
|
||||
assert_eq!(Value::Integer(1).as_i64(), Some(1i64));
|
||||
assert_eq!(Value::Integer(1).as_u64(), Some(1u64));
|
||||
#[cfg(any(feature = "lua54", feature = "lua53"))]
|
||||
{
|
||||
assert_eq!(Value::Integer(mlua::Integer::MAX).as_i32(), None);
|
||||
assert_eq!(Value::Integer(mlua::Integer::MAX).as_u32(), None);
|
||||
}
|
||||
assert_eq!(Value::Integer(1).as_isize(), Some(1isize));
|
||||
assert_eq!(Value::Integer(1).as_usize(), Some(1usize));
|
||||
assert!(Value::Number(1.23).is_number());
|
||||
assert_eq!(Value::Number(1.23).as_number(), Some(1.23));
|
||||
assert_eq!(Value::Number(1.23).as_f32(), Some(1.23f32));
|
||||
assert_eq!(Value::Number(1.23).as_f64(), Some(1.23f64));
|
||||
assert!(Value::String(lua.create_string("hello")?).is_string());
|
||||
assert_eq!(
|
||||
Value::String(lua.create_string("hello")?)
|
||||
.as_string()
|
||||
.unwrap(),
|
||||
"hello"
|
||||
);
|
||||
assert_eq!(
|
||||
Value::String(lua.create_string("hello")?).as_str().unwrap(),
|
||||
"hello"
|
||||
);
|
||||
assert_eq!(
|
||||
Value::String(lua.create_string("hello")?)
|
||||
.as_string_lossy()
|
||||
.unwrap(),
|
||||
"hello"
|
||||
);
|
||||
assert!(Value::Table(lua.create_table()?).is_table());
|
||||
assert!(Value::Table(lua.create_table()?).as_table().is_some());
|
||||
assert!(Value::Function(lua.create_function(|_, ()| Ok(())).unwrap()).is_function());
|
||||
assert!(
|
||||
Value::Function(lua.create_function(|_, ()| Ok(())).unwrap())
|
||||
.as_function()
|
||||
.is_some()
|
||||
);
|
||||
assert!(Value::Thread(lua.create_thread(lua.load("function() end").eval()?)?).is_thread());
|
||||
assert!(
|
||||
Value::Thread(lua.create_thread(lua.load("function() end").eval()?)?)
|
||||
.as_thread()
|
||||
.is_some()
|
||||
);
|
||||
assert!(Value::UserData(lua.create_any_userdata("hello")?).is_userdata());
|
||||
assert_eq!(
|
||||
Value::UserData(lua.create_any_userdata("hello")?)
|
||||
.as_userdata()
|
||||
.and_then(|ud| ud.borrow::<&str>().ok())
|
||||
.as_deref(),
|
||||
Some(&"hello")
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user