mirror of
https://github.com/mlua-rs/mlua
synced 2026-06-08 16:05:43 +00:00
Compare commits
120 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 13ff0ca798 | |||
| 44f49e35d6 | |||
| e1ee4058a6 | |||
| f06d0020ea | |||
| d399559d30 | |||
| 30735d5ff1 | |||
| 75c23e5853 | |||
| 347856b806 | |||
| 774a63bece | |||
| c481c87eac | |||
| 85b280a9d6 | |||
| db7b782d3c | |||
| 5f38445558 | |||
| df0a44d405 | |||
| f0806a6d62 | |||
| 3516f4c6ca | |||
| ca73583714 | |||
| 36560435f7 | |||
| 763c2b2564 | |||
| bafdb6138c | |||
| c9d6a610e1 | |||
| bd63f63bc9 | |||
| c035c23a15 | |||
| cb153a52b2 | |||
| b1c69d3005 | |||
| 841bd332e4 | |||
| 815d1bd7c9 | |||
| 78331ceebe | |||
| f945a35cbd | |||
| 459edb6816 | |||
| 00328b0b64 | |||
| 928d94d255 | |||
| 583c35a172 | |||
| 1791c599f4 | |||
| 1e48817a64 | |||
| 8d219503dd | |||
| 95367855c1 | |||
| 49389c4aa4 | |||
| 13dc2b5352 | |||
| 7afbf74128 | |||
| 06c3bd9d69 | |||
| 1ddaea60ce | |||
| a653d08768 | |||
| 2b6b0144a1 | |||
| 4cfe0be945 | |||
| a9a4814c3c | |||
| b1f73ec29d | |||
| 61a2141151 | |||
| dea38f27a5 | |||
| 04aaa18dc8 | |||
| d8455c038a | |||
| ca22ea3be7 | |||
| cf05593d66 | |||
| d011a1f851 | |||
| 1ec4661bf9 | |||
| d3b2999d2f | |||
| 72f6536efb | |||
| 646827a6bb | |||
| 1882931cd9 | |||
| c90cac5189 | |||
| c0d839d8d2 | |||
| 80471c6dad | |||
| 4b9d1cf271 | |||
| 55c07f3b28 | |||
| ef4eabd327 | |||
| a3302afdc1 | |||
| dfb4e9a668 | |||
| 92db0f6d3a | |||
| a3697ab1db | |||
| 052740db15 | |||
| faf547c154 | |||
| 0de7cd1c7d | |||
| 58953e5635 | |||
| 6406de405d | |||
| 727096dd3b | |||
| 2445230759 | |||
| 1cd2bdc808 | |||
| f8ed33a2aa | |||
| 25955893e0 | |||
| 3f0c69b70b | |||
| 9da98d42c7 | |||
| f539f60987 | |||
| aa187e6663 | |||
| ec10bf2a39 | |||
| 3ea80b763d | |||
| 2e0e86dab2 | |||
| 0ac7aebb27 | |||
| 634e5d4550 | |||
| 05d6c20520 | |||
| 63e7cfd31b | |||
| 9c24c99cbe | |||
| 62f84828f2 | |||
| 7bc72be7d3 | |||
| 2fbbbe4238 | |||
| f00208373e | |||
| a2dc662a92 | |||
| caeac2e9a3 | |||
| 3d5261640d | |||
| b57a6239a6 | |||
| 031424f6ce | |||
| c4c9609ac6 | |||
| 6fcd18e434 | |||
| 39cac5699a | |||
| e6e1ef014f | |||
| 942a443592 | |||
| 65e292dac4 | |||
| 1a82f83644 | |||
| 38fbd08c72 | |||
| b3854d2f1d | |||
| 00a56b115b | |||
| 2fefaafaa6 | |||
| 76a8f8cc71 | |||
| 13395e9c3d | |||
| c61219dd93 | |||
| 2e7c654cfe | |||
| f36aaa5ce1 | |||
| df38878278 | |||
| c5c1fe3b85 | |||
| 0cc4b15f6b | |||
| f16aca687d |
+14
-14
@@ -27,8 +27,8 @@ jobs:
|
||||
- name: Build ${{ matrix.lua }} vendored
|
||||
run: |
|
||||
cargo build --features "${{ matrix.lua }},vendored"
|
||||
cargo build --features "${{ matrix.lua }},vendored,async,serialize,macros,anyhow,userdata-wrappers"
|
||||
cargo build --features "${{ matrix.lua }},vendored,async,serialize,macros,anyhow,userdata-wrappers,send"
|
||||
cargo build --features "${{ matrix.lua }},vendored,async,serde,macros,anyhow,userdata-wrappers"
|
||||
cargo build --features "${{ matrix.lua }},vendored,async,serde,macros,anyhow,userdata-wrappers,send"
|
||||
shell: bash
|
||||
- name: Build ${{ matrix.lua }} pkg-config
|
||||
if: ${{ matrix.os == 'ubuntu-latest' }}
|
||||
@@ -51,7 +51,7 @@ jobs:
|
||||
toolchain: stable
|
||||
target: aarch64-apple-darwin
|
||||
- name: Cross-compile
|
||||
run: cargo build --target aarch64-apple-darwin --features "${{ matrix.lua }},vendored,async,send,serialize,macros,anyhow,userdata-wrappers"
|
||||
run: cargo build --target aarch64-apple-darwin --features "${{ matrix.lua }},vendored,async,send,serde,macros,anyhow,userdata-wrappers"
|
||||
|
||||
build_aarch64_cross_ubuntu:
|
||||
name: Cross-compile to aarch64-unknown-linux-gnu
|
||||
@@ -72,7 +72,7 @@ jobs:
|
||||
sudo apt-get install -y --no-install-recommends gcc-aarch64-linux-gnu libc6-dev-arm64-cross
|
||||
shell: bash
|
||||
- name: Cross-compile
|
||||
run: cargo build --target aarch64-unknown-linux-gnu --features "${{ matrix.lua }},vendored,async,send,serialize,macros,anyhow,userdata-wrappers"
|
||||
run: cargo build --target aarch64-unknown-linux-gnu --features "${{ matrix.lua }},vendored,async,send,serde,macros,anyhow,userdata-wrappers"
|
||||
shell: bash
|
||||
|
||||
build_armv7_cross_ubuntu:
|
||||
@@ -94,7 +94,7 @@ jobs:
|
||||
sudo apt-get install -y --no-install-recommends gcc-arm-linux-gnueabihf libc-dev-armhf-cross
|
||||
shell: bash
|
||||
- name: Cross-compile
|
||||
run: cargo build --target armv7-unknown-linux-gnueabihf --features "${{ matrix.lua }},vendored,async,send,serialize,macros,anyhow,userdata-wrappers"
|
||||
run: cargo build --target armv7-unknown-linux-gnueabihf --features "${{ matrix.lua }},vendored,async,send,serde,macros,anyhow,userdata-wrappers"
|
||||
shell: bash
|
||||
|
||||
test:
|
||||
@@ -123,14 +123,14 @@ jobs:
|
||||
- name: Run ${{ matrix.lua }} tests
|
||||
run: |
|
||||
cargo test --features "${{ matrix.lua }},vendored"
|
||||
cargo test --features "${{ matrix.lua }},vendored,async,serialize,macros,anyhow,userdata-wrappers"
|
||||
cargo test --features "${{ matrix.lua }},vendored,async,serialize,macros,anyhow,userdata-wrappers,send"
|
||||
cargo test --features "${{ matrix.lua }},vendored,async,serde,macros,anyhow,userdata-wrappers"
|
||||
cargo test --features "${{ matrix.lua }},vendored,async,serde,macros,anyhow,userdata-wrappers,send"
|
||||
shell: bash
|
||||
- name: Run compile tests (macos lua54)
|
||||
if: ${{ matrix.os == 'macos-latest' && matrix.lua == 'lua54' }}
|
||||
run: |
|
||||
TRYBUILD=overwrite cargo test --features "${{ matrix.lua }},vendored" -- --ignored
|
||||
TRYBUILD=overwrite cargo test --features "${{ matrix.lua }},vendored,async,send,serialize,macros" -- --ignored
|
||||
TRYBUILD=overwrite cargo test --features "${{ matrix.lua }},vendored" --tests -- --ignored
|
||||
TRYBUILD=overwrite cargo test --features "${{ matrix.lua }},vendored,async,send,serde,macros" --tests -- --ignored
|
||||
shell: bash
|
||||
|
||||
test_with_sanitizer:
|
||||
@@ -154,8 +154,8 @@ jobs:
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
- name: Run ${{ matrix.lua }} tests with address sanitizer
|
||||
run: |
|
||||
cargo test --tests --features "${{ matrix.lua }},vendored,async,serialize,macros,anyhow" --target x86_64-unknown-linux-gnu -- --skip test_too_many_recursions
|
||||
cargo test --tests --features "${{ matrix.lua }},vendored,async,serialize,macros,anyhow,userdata-wrappers,send" --target x86_64-unknown-linux-gnu -- --skip test_too_many_recursions
|
||||
cargo test --tests --features "${{ matrix.lua }},vendored,async,serde,macros,anyhow" --target x86_64-unknown-linux-gnu -- --skip test_too_many_recursions
|
||||
cargo test --tests --features "${{ matrix.lua }},vendored,async,serde,macros,anyhow,userdata-wrappers,send" --target x86_64-unknown-linux-gnu -- --skip test_too_many_recursions
|
||||
shell: bash
|
||||
env:
|
||||
RUSTFLAGS: -Z sanitizer=address
|
||||
@@ -181,7 +181,7 @@ jobs:
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
- name: Run ${{ matrix.lua }} tests with forced memory limit
|
||||
run: |
|
||||
cargo test --tests --features "${{ matrix.lua }},vendored,async,send,serialize,macros,anyhow,userdata-wrappers"
|
||||
cargo test --tests --features "${{ matrix.lua }},vendored,async,send,serde,macros,anyhow,userdata-wrappers"
|
||||
shell: bash
|
||||
env:
|
||||
RUSTFLAGS: --cfg=force_memory_limit
|
||||
@@ -254,7 +254,7 @@ jobs:
|
||||
- name: Run ${{ matrix.lua }} tests
|
||||
run: |
|
||||
cargo test --tests --features "${{ matrix.lua }},vendored"
|
||||
cargo test --tests --features "${{ matrix.lua }},vendored,async,serialize,macros,anyhow,userdata-wrappers"
|
||||
cargo test --tests --features "${{ matrix.lua }},vendored,async,serde,macros,anyhow,userdata-wrappers"
|
||||
|
||||
rustfmt:
|
||||
name: Rustfmt
|
||||
@@ -281,4 +281,4 @@ jobs:
|
||||
- uses: giraffate/clippy-action@v1
|
||||
with:
|
||||
reporter: 'github-pr-review'
|
||||
clippy_flags: --features "${{ matrix.lua }},vendored,async,send,serialize,macros,anyhow,userdata-wrappers"
|
||||
clippy_flags: --features "${{ matrix.lua }},vendored,async,send,serde,macros,anyhow,userdata-wrappers"
|
||||
|
||||
@@ -1,3 +1,57 @@
|
||||
## v0.11.3 (Aug 30, 2025)
|
||||
|
||||
- Add `Lua::yield_with` to use as `coroutine.yield` functional replacement in async functions for any Lua
|
||||
- Do not try to yield at non-yielable points in Luau interrupt (#632)
|
||||
- Add `Buffer::cursor` method (Luau)
|
||||
- Add `Lua::create_buffer_with_capacity` method (Luau)
|
||||
- Make Lua reference values cheap to clone (only increments ref count)
|
||||
- Fix panic on large (>67M entries) table creation
|
||||
|
||||
## v0.11.2 (Aug 10, 2025)
|
||||
|
||||
- Faster stack push for `Variadic<T>`
|
||||
- Fix handling Windows paths with drive letter in Luau require (#623)
|
||||
- Make Luau registered aliases ascii case-insensitive (#620)
|
||||
- Fix deserializing negative zeros `-0.0` (#618)
|
||||
|
||||
## v0.11.1 (Jul 15, 2025)
|
||||
|
||||
- Fixed bug exhausting Lua auxiliary stack and leaving it without reserve (#615)
|
||||
- `Lua::push_c_function` now correctly handles OOM for Lua 5.1 and Luau
|
||||
|
||||
## v0.11.0 (Jul 14, 2025)
|
||||
|
||||
Changes since v0.11.0-beta.3
|
||||
|
||||
- Allow linking external Lua libraries in a build script (e.g. pluto) using `external` mlua-sys feature flag
|
||||
- `Lua::inspect_stack` takes a callback with `&Debug` argument, instead of returning `Debug` directly
|
||||
- Added `Debug::function` method to get function running at a given level
|
||||
- `Debug::curr_line` is deprecated in favour of `Debug::current_line` that returns `Option<usize>`
|
||||
- Added `Lua::set_globals` method to replace global environment
|
||||
- `Table::set_metatable` now returns `Result<()>` (this operation can fail in sandboxed Luau mode)
|
||||
- `impl ToString` replaced with `Into<StdString>` in `UserData` registration
|
||||
- `Value::as_str` and `Value::as_string_lossy` methods are deprecated (as they are non-idiomatic)
|
||||
- Bugfixes and improvements
|
||||
|
||||
## v0.11.0-beta.3 (Jun 23, 2025)
|
||||
|
||||
- Luau in sandboxed mode has reduced options in `collectgarbage` function (to follow the official doc)
|
||||
- `Function::deep_clone` now returns `Result<Function>` as this operation can trigger memory errors
|
||||
- Luau "Require" resolves included Lua files relative to the current directory (#605)
|
||||
- Fixed bug when finalizing `AsyncThread` on drop (`call_async` methods family)
|
||||
|
||||
## v0.11.0-beta.2 (Jun 12, 2025)
|
||||
|
||||
- Lua 5.4 updated to 5.4.8
|
||||
- Terminate Rust `Future` when `AsyncThread` is dropped (without relying on Lua GC)
|
||||
- Added `loadstring` function to Luau
|
||||
- Make `AsChunk` trait dyn-friendly
|
||||
- Luau `Require` trait synced with Luau 0.674
|
||||
- Luau `Require` trait methods now can return `Error` variant (in `NavigateError` enum)
|
||||
- Added `__type` to `Error`'s userdata metatable (for `typeof` function)
|
||||
- `parking_log/send_guard` is moved to `userdata-wrappers` feature flag
|
||||
- New `serde` feature flag to replace `serialize` (the old one is still available)
|
||||
|
||||
## v0.11.0-beta.1 (May 7th, 2025)
|
||||
|
||||
- New "require-by-string" for Luau (with `Require` trait and async support)
|
||||
|
||||
+17
-14
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "mlua"
|
||||
version = "0.11.0-beta.1" # remember to update mlua_derive
|
||||
version = "0.11.3" # remember to update mlua_derive
|
||||
authors = ["Aleksandr Orlenko <zxteam@pm.me>", "kyren <catherine@kyju.org>"]
|
||||
rust-version = "1.79.0"
|
||||
edition = "2021"
|
||||
@@ -16,7 +16,7 @@ with async/await features and support of writing native Lua modules in Rust.
|
||||
"""
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
features = ["lua54", "vendored", "async", "send", "serialize", "macros"]
|
||||
features = ["lua54", "vendored", "async", "send", "serde", "macros"]
|
||||
rustdoc-args = ["--cfg", "docsrs"]
|
||||
|
||||
[workspace]
|
||||
@@ -36,17 +36,20 @@ luau = ["ffi/luau"]
|
||||
luau-jit = ["luau", "ffi/luau-codegen"]
|
||||
luau-vector4 = ["luau", "ffi/luau-vector4"]
|
||||
vendored = ["ffi/vendored"]
|
||||
module = ["dep:mlua_derive", "ffi/module"]
|
||||
module = ["mlua_derive", "ffi/module"]
|
||||
async = ["dep:futures-util"]
|
||||
send = ["parking_lot/send_guard", "error-send"]
|
||||
send = ["error-send"]
|
||||
error-send = []
|
||||
serialize = ["dep:serde", "dep:erased-serde", "dep:serde-value", "bstr/serde"]
|
||||
serde = ["dep:serde", "dep:erased-serde", "dep:serde-value", "bstr/serde"]
|
||||
macros = ["mlua_derive/macros"]
|
||||
anyhow = ["dep:anyhow", "error-send"]
|
||||
userdata-wrappers = []
|
||||
userdata-wrappers = ["parking_lot/send_guard"]
|
||||
|
||||
# deprecated features
|
||||
serialize = ["serde"]
|
||||
|
||||
[dependencies]
|
||||
mlua_derive = { version = "=0.11.0-beta.1", optional = true, path = "mlua_derive" }
|
||||
mlua_derive = { version = "=0.11.0", optional = true, path = "mlua_derive" }
|
||||
bstr = { version = "1.0", features = ["std"], default-features = false }
|
||||
either = "1.0"
|
||||
num-traits = { version = "0.2.14" }
|
||||
@@ -59,7 +62,7 @@ parking_lot = { version = "0.12", features = ["arc_lock"] }
|
||||
anyhow = { version = "1.0", optional = true }
|
||||
rustversion = "1.0"
|
||||
|
||||
ffi = { package = "mlua-sys", version = "0.7.0", path = "mlua-sys" }
|
||||
ffi = { package = "mlua-sys", version = "0.8.3", path = "mlua-sys" }
|
||||
|
||||
[dev-dependencies]
|
||||
trybuild = "1.0"
|
||||
@@ -75,8 +78,8 @@ tempfile = "3"
|
||||
static_assertions = "1.0"
|
||||
|
||||
[target.'cfg(not(target_arch = "wasm32"))'.dev-dependencies]
|
||||
criterion = { version = "0.5", features = ["async_tokio"] }
|
||||
rustyline = "15.0"
|
||||
criterion = { version = "0.7", features = ["async_tokio"] }
|
||||
rustyline = "17.0"
|
||||
tokio = { version = "1.0", features = ["full"] }
|
||||
|
||||
[lints.rust]
|
||||
@@ -90,7 +93,7 @@ required-features = ["async"]
|
||||
[[bench]]
|
||||
name = "serde"
|
||||
harness = false
|
||||
required-features = ["serialize"]
|
||||
required-features = ["serde"]
|
||||
|
||||
[[example]]
|
||||
name = "async_http_client"
|
||||
@@ -98,7 +101,7 @@ required-features = ["async", "macros"]
|
||||
|
||||
[[example]]
|
||||
name = "async_http_reqwest"
|
||||
required-features = ["async", "serialize", "macros"]
|
||||
required-features = ["async", "serde", "macros"]
|
||||
|
||||
[[example]]
|
||||
name = "async_http_server"
|
||||
@@ -113,8 +116,8 @@ name = "guided_tour"
|
||||
required-features = ["macros"]
|
||||
|
||||
[[example]]
|
||||
name = "serialize"
|
||||
required-features = ["serialize"]
|
||||
name = "serde"
|
||||
required-features = ["serde"]
|
||||
|
||||
[[example]]
|
||||
name = "userdata"
|
||||
|
||||
@@ -17,18 +17,12 @@
|
||||
[Benchmarks]: https://github.com/khvzak/script-bench-rs
|
||||
[FAQ]: FAQ.md
|
||||
|
||||
# The main branch is the development version of `mlua`. Please see the [v0.10](https://github.com/mlua-rs/mlua/tree/v0.10) branch for the stable versions of `mlua`.
|
||||
`mlua` is a set of bindings to the [Lua](https://www.lua.org) programming language for Rust with a goal to provide a
|
||||
_safe_ (as much as possible), high level, easy to use, practical and flexible API.
|
||||
|
||||
> **Note**
|
||||
>
|
||||
> See v0.10 [release notes](https://github.com/mlua-rs/mlua/blob/main/docs/release_notes/v0.10.md).
|
||||
Started as an `rlua` fork, `mlua` supports Lua 5.4, 5.3, 5.2, 5.1 (including LuaJIT) and [Luau] and allows writing native Lua modules in Rust as well as using Lua in a standalone mode.
|
||||
|
||||
`mlua` is bindings to [Lua](https://www.lua.org) programming language for Rust with a goal to provide
|
||||
_safe_ (as far as it's possible), high level, easy to use, practical and flexible API.
|
||||
|
||||
Started as `rlua` fork, `mlua` supports Lua 5.4, 5.3, 5.2, 5.1 (including LuaJIT) and [Luau] and allows to write native Lua modules in Rust as well as use Lua in a standalone mode.
|
||||
|
||||
`mlua` tested on Windows/macOS/Linux including module mode in [GitHub Actions] on `x86_64` platform and cross-compilation to `aarch64` (other targets are also supported).
|
||||
`mlua` is tested on Windows/macOS/Linux including module mode in [GitHub Actions] on `x86_64` platforms and cross-compilation to `aarch64` (other targets are also supported).
|
||||
|
||||
WebAssembly (WASM) is supported through `wasm32-unknown-emscripten` target for all Lua/Luau versions excluding JIT.
|
||||
|
||||
@@ -39,7 +33,7 @@ WebAssembly (WASM) is supported through `wasm32-unknown-emscripten` target for a
|
||||
|
||||
### Feature flags
|
||||
|
||||
`mlua` uses feature flags to reduce the amount of dependencies, compiled code and allow to choose only required set of features.
|
||||
`mlua` uses feature flags to reduce the amount of dependencies and compiled code, and allow to choose only required set of features.
|
||||
Below is a list of the available feature flags. By default `mlua` does not enable any features.
|
||||
|
||||
* `lua54`: enable Lua [5.4] support
|
||||
@@ -51,12 +45,12 @@ Below is a list of the available feature flags. By default `mlua` does not enabl
|
||||
* `luau`: enable [Luau] support (auto vendored mode)
|
||||
* `luau-jit`: enable [Luau] support with JIT backend.
|
||||
* `luau-vector4`: enable [Luau] support with 4-dimensional vector.
|
||||
* `vendored`: build static Lua(JIT) library from sources during `mlua` compilation using [lua-src] or [luajit-src] crates
|
||||
* `vendored`: build static Lua(JIT) libraries from sources during `mlua` compilation using [lua-src] or [luajit-src]
|
||||
* `module`: enable module mode (building loadable `cdylib` library for Lua)
|
||||
* `async`: enable async/await support (any executor can be used, eg. [tokio] or [async-std])
|
||||
* `send`: make `mlua::Lua: Send + Sync` (adds [`Send`] requirement to `mlua::Function` and `mlua::UserData`)
|
||||
* `error-send`: make `mlua:Error: Send + Sync`
|
||||
* `serialize`: add serialization and deserialization support to `mlua` types using [serde] framework
|
||||
* `serde`: add serialization and deserialization support to `mlua` types using [serde]
|
||||
* `macros`: enable procedural macros (such as `chunk!`)
|
||||
* `anyhow`: enable `anyhow::Error` conversion into Lua
|
||||
* `userdata-wrappers`: opt into `impl UserData` for `Rc<T>`/`Arc<T>`/`Rc<RefCell<T>>`/`Arc<Mutex<T>>` where `T: UserData`
|
||||
@@ -78,7 +72,7 @@ Below is a list of the available feature flags. By default `mlua` does not enabl
|
||||
|
||||
`mlua` supports async/await for all Lua versions including Luau.
|
||||
|
||||
This works using Lua [coroutines](https://www.lua.org/manual/5.3/manual.html#2.6) and require running [Thread](https://docs.rs/mlua/latest/mlua/struct.Thread.html) along with enabling `feature = "async"` in `Cargo.toml`.
|
||||
This works using Lua [coroutines](https://www.lua.org/manual/5.3/manual.html#2.6) and requires running [Thread](https://docs.rs/mlua/latest/mlua/struct.Thread.html) along with enabling `feature = "async"` in `Cargo.toml`.
|
||||
|
||||
**Examples**:
|
||||
- [HTTP Client](examples/async_http_client.rs)
|
||||
@@ -93,18 +87,18 @@ This works using Lua [coroutines](https://www.lua.org/manual/5.3/manual.html#2.6
|
||||
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
|
||||
cargo run --example async_http_reqwest --features=lua54,async,macros,serde
|
||||
|
||||
# async http server
|
||||
cargo run --example async_http_server --features=lua54,async,macros,send
|
||||
curl -v http://localhost:3000
|
||||
```
|
||||
|
||||
### Serialization (serde) support
|
||||
### 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).
|
||||
With the `serde` 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 the [`serde::Serialize`] trait implementation for `mlua::Value` (including `UserData` support).
|
||||
|
||||
[Example](examples/serialize.rs)
|
||||
[Example](examples/serde.rs)
|
||||
|
||||
[`serde::Serialize`]: https://docs.serde.rs/serde/ser/trait.Serialize.html
|
||||
[`serde::Deserialize`]: https://docs.serde.rs/serde/de/trait.Deserialize.html
|
||||
@@ -114,28 +108,28 @@ With `serialize` feature flag enabled, `mlua` allows you to serialize/deserializ
|
||||
|
||||
You have to enable one of the features: `lua54`, `lua53`, `lua52`, `lua51`, `luajit(52)` or `luau`, according to the chosen Lua version.
|
||||
|
||||
By default `mlua` uses `pkg-config` tool to find lua includes and libraries for the chosen Lua version.
|
||||
In most cases it works as desired, although sometimes could be more preferable to use a custom lua library.
|
||||
To achieve this, mlua supports `LUA_LIB`, `LUA_LIB_NAME` and `LUA_LINK` environment variables.
|
||||
By default `mlua` uses `pkg-config` to find Lua includes and libraries for the chosen Lua version.
|
||||
In most cases it works as desired, although sometimes it may be preferable to use a custom Lua library.
|
||||
To achieve this, mlua supports the `LUA_LIB`, `LUA_LIB_NAME` and `LUA_LINK` environment variables.
|
||||
`LUA_LINK` is optional and may be `dylib` (a dynamic library) or `static` (a static library, `.a` archive).
|
||||
|
||||
An example how to use them:
|
||||
An example of how to use them:
|
||||
``` sh
|
||||
my_project $ LUA_LIB=$HOME/tmp/lua-5.2.4/src LUA_LIB_NAME=lua LUA_LINK=static cargo build
|
||||
```
|
||||
|
||||
`mlua` also supports vendored lua/luajit using the auxiliary crates [lua-src](https://crates.io/crates/lua-src) and
|
||||
`mlua` also supports vendored Lua/LuaJIT using the auxiliary crates [lua-src](https://crates.io/crates/lua-src) and
|
||||
[luajit-src](https://crates.io/crates/luajit-src).
|
||||
Just enable the `vendored` feature and cargo will automatically build and link specified lua/luajit version. This is the easiest way to get started with `mlua`.
|
||||
Just enable the `vendored` feature and cargo will automatically build and link the specified Lua/LuaJIT version. This is the easiest way to get started with `mlua`.
|
||||
|
||||
### Standalone mode
|
||||
In a standalone mode `mlua` allows to add to your application scripting support with a gently configured Lua runtime to ensure safety and soundness.
|
||||
In standalone mode, `mlua` allows adding scripting support to your application with a gently configured Lua runtime to ensure safety and soundness.
|
||||
|
||||
Add to `Cargo.toml` :
|
||||
Add to `Cargo.toml`:
|
||||
|
||||
``` toml
|
||||
[dependencies]
|
||||
mlua = { version = "0.10", features = ["lua54", "vendored"] }
|
||||
mlua = { version = "0.11", features = ["lua54", "vendored"] }
|
||||
```
|
||||
|
||||
`main.rs`
|
||||
@@ -159,21 +153,21 @@ fn main() -> LuaResult<()> {
|
||||
```
|
||||
|
||||
### Module mode
|
||||
In a module mode `mlua` allows to create a compiled Lua module that can be loaded from Lua code using [`require`](https://www.lua.org/manual/5.4/manual.html#pdf-require). In this case `mlua` uses an external Lua runtime which could lead to potential unsafety due to unpredictability of the Lua environment and usage of libraries such as [`debug`](https://www.lua.org/manual/5.4/manual.html#6.10).
|
||||
In module mode, `mlua` allows creating a compiled Lua module that can be loaded from Lua code using [`require`](https://www.lua.org/manual/5.4/manual.html#pdf-require). In this case `mlua` uses an external Lua runtime which could lead to potential unsafety due to the unpredictability of the Lua environment and usage of libraries such as [`debug`](https://www.lua.org/manual/5.4/manual.html#6.10).
|
||||
|
||||
[Example](examples/module)
|
||||
|
||||
Add to `Cargo.toml` :
|
||||
Add to `Cargo.toml`:
|
||||
|
||||
``` toml
|
||||
[lib]
|
||||
crate-type = ["cdylib"]
|
||||
|
||||
[dependencies]
|
||||
mlua = { version = "0.10", features = ["lua54", "module"] }
|
||||
mlua = { version = "0.11", features = ["lua54", "module"] }
|
||||
```
|
||||
|
||||
`lib.rs` :
|
||||
`lib.rs`:
|
||||
|
||||
``` rust
|
||||
use mlua::prelude::*;
|
||||
@@ -216,14 +210,14 @@ rustflags = [
|
||||
```
|
||||
On Linux you can build modules normally with `cargo build --release`.
|
||||
|
||||
On Windows the target module will be linked with `lua5x.dll` library (depending on your feature flags).
|
||||
On Windows the target module will be linked with the `lua5x.dll` library (depending on your feature flags).
|
||||
Your main application should provide this library.
|
||||
|
||||
Module builds don't require Lua lib or headers to be installed on the system.
|
||||
Module builds don't require Lua binaries or headers to be installed on the system.
|
||||
|
||||
### Publishing to luarocks.org
|
||||
|
||||
There is a LuaRocks build backend for mlua modules [`luarocks-build-rust-mlua`].
|
||||
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)
|
||||
@@ -236,10 +230,10 @@ Modules written in Rust and published to luarocks:
|
||||
|
||||
## Safety
|
||||
|
||||
One of the `mlua` goals is to provide *safe* API between Rust and Lua.
|
||||
Every place where the Lua C API may trigger an error longjmp in any way is protected by `lua_pcall`,
|
||||
and the user of the library is protected from directly interacting with unsafe things like the Lua stack,
|
||||
and there is overhead associated with this safety.
|
||||
One of `mlua`'s goals is to provide a *safe* API between Rust and Lua.
|
||||
Every place where the Lua C API may trigger an error longjmp is protected by `lua_pcall`,
|
||||
and the user of the library is protected from directly interacting with unsafe things like the Lua stack.
|
||||
There is overhead associated with this safety.
|
||||
|
||||
Unfortunately, `mlua` does not provide absolute safety even without using `unsafe` .
|
||||
This library contains a huge amount of unsafe code. There are almost certainly bugs still lurking in this library!
|
||||
@@ -247,8 +241,8 @@ It is surprisingly, fiendishly difficult to use the Lua C API without the potent
|
||||
|
||||
## Panic handling
|
||||
|
||||
`mlua` wraps panics that are generated inside Rust callbacks in a regular Lua error. Panics could be
|
||||
resumed then by returning or propagating the Lua error to Rust code.
|
||||
`mlua` wraps panics that are generated inside Rust callbacks in a regular Lua error. Panics can then be
|
||||
resumed by returning or propagating the Lua error to Rust code.
|
||||
|
||||
For example:
|
||||
``` rust
|
||||
@@ -267,12 +261,12 @@ let _ = lua.load(r#"
|
||||
unreachable!()
|
||||
```
|
||||
|
||||
Optionally `mlua` can disable Rust panics catching in Lua via `pcall`/`xpcall` and automatically resume
|
||||
Optionally, `mlua` can disable Rust panic catching in Lua via `pcall`/`xpcall` and automatically resume
|
||||
them across the Lua API boundary. This is controlled via `LuaOptions` and done by wrapping the Lua `pcall`/`xpcall`
|
||||
functions on a way to prevent catching errors that are wrapped Rust panics.
|
||||
functions to prevent catching errors that are wrapped Rust panics.
|
||||
|
||||
`mlua` should also be panic safe in another way as well, which is that any `Lua` instances or handles
|
||||
remains usable after a user generated panic, and such panics should not break internal invariants or
|
||||
remain usable after a user generated panic, and such panics should not break internal invariants or
|
||||
leak Lua stack space. This is mostly important to safely use `mlua` types in Drop impls, as you should not be
|
||||
using panics for general error handling.
|
||||
|
||||
@@ -289,12 +283,12 @@ If you encounter them, a bug report would be very welcome:
|
||||
|
||||
## Sandboxing
|
||||
|
||||
Please check the [Luau Sandboxing] page if you are interested in running untrusted Lua scripts in controlled environment.
|
||||
Please check the [Luau Sandboxing] page if you are interested in running untrusted Lua scripts in a controlled environment.
|
||||
|
||||
`mlua` provides `Lua::sandbox` method for enabling sandbox mode (Luau only).
|
||||
`mlua` provides the `Lua::sandbox` method for enabling sandbox mode (Luau only).
|
||||
|
||||
[Luau Sandboxing]: https://luau.org/sandbox
|
||||
|
||||
## License
|
||||
|
||||
This project is licensed under the [MIT license](LICENSE)
|
||||
This project is licensed under the [MIT license](LICENSE).
|
||||
|
||||
@@ -128,6 +128,22 @@ fn table_traversal_sequence(c: &mut Criterion) {
|
||||
});
|
||||
}
|
||||
|
||||
fn table_ref_clone(c: &mut Criterion) {
|
||||
let lua = Lua::new();
|
||||
|
||||
let t = lua.create_table().unwrap();
|
||||
|
||||
c.bench_function("table [ref clone]", |b| {
|
||||
b.iter_batched(
|
||||
|| collect_gc_twice(&lua),
|
||||
|_| {
|
||||
let _t2 = t.clone();
|
||||
},
|
||||
BatchSize::SmallInput,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
fn function_create(c: &mut Criterion) {
|
||||
let lua = Lua::new();
|
||||
|
||||
@@ -350,6 +366,39 @@ fn userdata_call_method(c: &mut Criterion) {
|
||||
});
|
||||
}
|
||||
|
||||
// A userdata method call that goes through an implicit `__index` function
|
||||
fn userdata_call_method_complex(c: &mut Criterion) {
|
||||
struct UserData(u64);
|
||||
impl LuaUserData for UserData {
|
||||
fn register(registry: &mut LuaUserDataRegistry<Self>) {
|
||||
registry.add_field_method_get("val", |_, this| Ok(this.0));
|
||||
registry.add_method_mut("inc_by", |_, this, by: u64| {
|
||||
this.0 += by;
|
||||
Ok(this.0)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
let lua = Lua::new();
|
||||
let ud = lua.create_userdata(UserData(0)).unwrap();
|
||||
let inc_by = lua
|
||||
.load("function(ud, s) return ud:inc_by(s) end")
|
||||
.eval::<LuaFunction>()
|
||||
.unwrap();
|
||||
|
||||
c.bench_function("userdata [call method complex]", |b| {
|
||||
b.iter_batched(
|
||||
|| {
|
||||
collect_gc_twice(&lua);
|
||||
},
|
||||
|_| {
|
||||
inc_by.call::<()>((&ud, 1)).unwrap();
|
||||
},
|
||||
BatchSize::SmallInput,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
fn userdata_async_call_method(c: &mut Criterion) {
|
||||
struct UserData(i64);
|
||||
impl LuaUserData for UserData {
|
||||
@@ -399,6 +448,7 @@ criterion_group! {
|
||||
table_traversal_pairs,
|
||||
table_traversal_for_each,
|
||||
table_traversal_sequence,
|
||||
table_ref_clone,
|
||||
|
||||
function_create,
|
||||
function_call_sum,
|
||||
@@ -413,6 +463,7 @@ criterion_group! {
|
||||
userdata_create,
|
||||
userdata_call_index,
|
||||
userdata_call_method,
|
||||
userdata_call_method_complex,
|
||||
userdata_async_call_method,
|
||||
}
|
||||
|
||||
|
||||
+10
-8
@@ -20,14 +20,16 @@ fn main() {
|
||||
match lua.load(&line).eval::<MultiValue>() {
|
||||
Ok(values) => {
|
||||
editor.add_history_entry(line).unwrap();
|
||||
println!(
|
||||
"{}",
|
||||
values
|
||||
.iter()
|
||||
.map(|value| format!("{:#?}", value))
|
||||
.collect::<Vec<_>>()
|
||||
.join("\t")
|
||||
);
|
||||
if values.len() > 0 {
|
||||
println!(
|
||||
"{}",
|
||||
values
|
||||
.iter()
|
||||
.map(|value| format!("{:#?}", value))
|
||||
.collect::<Vec<_>>()
|
||||
.join("\t")
|
||||
);
|
||||
}
|
||||
break;
|
||||
}
|
||||
Err(Error::SyntaxError {
|
||||
|
||||
+4
-3
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "mlua-sys"
|
||||
version = "0.7.0"
|
||||
version = "0.8.3"
|
||||
authors = ["Aleksandr Orlenko <zxteam@pm.me>"]
|
||||
rust-version = "1.71"
|
||||
edition = "2021"
|
||||
@@ -30,6 +30,7 @@ luau = ["luau0-src"]
|
||||
luau-codegen = ["luau"]
|
||||
luau-vector4 = ["luau"]
|
||||
vendored = ["lua-src", "luajit-src"]
|
||||
external = []
|
||||
module = []
|
||||
|
||||
[dependencies]
|
||||
@@ -38,9 +39,9 @@ module = []
|
||||
cc = "1.0"
|
||||
cfg-if = "1.0"
|
||||
pkg-config = "0.3.17"
|
||||
lua-src = { version = ">= 547.1.0, < 547.2.0", optional = true }
|
||||
lua-src = { version = ">= 548.1.0, < 548.2.0", optional = true }
|
||||
luajit-src = { version = ">= 210.6.0, < 210.7.0", optional = true }
|
||||
luau0-src = { version = "0.14.2", optional = true }
|
||||
luau0-src = { version = "0.15.6", optional = true }
|
||||
|
||||
[lints.rust]
|
||||
unexpected_cfgs = { level = "allow", check-cfg = ['cfg(raw_dylib)'] }
|
||||
|
||||
@@ -14,22 +14,29 @@ fn main() {
|
||||
#[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");
|
||||
#[cfg(any(
|
||||
all(feature = "vendored", any(feature = "external", feature = "module")),
|
||||
all(feature = "external", any(feature = "vendored", feature = "module")),
|
||||
all(feature = "module", any(feature = "vendored", feature = "external"))
|
||||
))]
|
||||
compile_error!("`vendored`, `external` and `module` features are mutually exclusive");
|
||||
|
||||
println!("cargo:rerun-if-changed=build");
|
||||
|
||||
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();
|
||||
return;
|
||||
// Check if compilation and linking is handled by external crate
|
||||
if cfg!(not(feature = "external")) {
|
||||
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();
|
||||
return;
|
||||
}
|
||||
|
||||
println!("cargo:rustc-cfg=raw_dylib");
|
||||
}
|
||||
|
||||
println!("cargo:rustc-cfg=raw_dylib");
|
||||
#[cfg(not(feature = "module"))]
|
||||
find::probe_lua();
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "module"))]
|
||||
find::probe_lua();
|
||||
}
|
||||
|
||||
@@ -186,7 +186,8 @@ pub unsafe fn lua_isinteger(L: *mut lua_State, idx: c_int) -> c_int {
|
||||
if lua_type(L, idx) == LUA_TNUMBER {
|
||||
let n = lua_tonumber(L, idx);
|
||||
let i = lua_tointeger(L, idx);
|
||||
if (n - i as lua_Number).abs() < lua_Number::EPSILON {
|
||||
// Lua 5.3+ returns "false" for `-0.0`
|
||||
if n.to_bits() == (i as lua_Number).to_bits() {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,7 +51,8 @@ pub unsafe fn lua_isinteger(L: *mut lua_State, idx: c_int) -> c_int {
|
||||
if lua_type(L, idx) == LUA_TNUMBER {
|
||||
let n = lua_tonumber(L, idx);
|
||||
let i = lua_tointeger(L, idx);
|
||||
if (n - i as lua_Number).abs() < lua_Number::EPSILON {
|
||||
// Lua 5.3+ returns "false" for `-0.0`
|
||||
if n.to_bits() == (i as lua_Number).to_bits() {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -120,7 +120,8 @@ pub unsafe fn lua_isinteger(L: *mut lua_State, idx: c_int) -> c_int {
|
||||
if lua_type(L, idx) == LUA_TNUMBER {
|
||||
let n = lua_tonumber(L, idx);
|
||||
let i = lua_tointeger(L, idx);
|
||||
if (n - i as lua_Number).abs() < lua_Number::EPSILON {
|
||||
// Lua 5.3+ returns "false" for `-0.0`
|
||||
if n.to_bits() == (i as lua_Number).to_bits() {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
@@ -388,7 +389,7 @@ pub unsafe fn luaL_loadbufferenv(
|
||||
}
|
||||
}
|
||||
|
||||
if chunk_is_text {
|
||||
let status = if chunk_is_text {
|
||||
if env < 0 {
|
||||
env -= 1;
|
||||
}
|
||||
@@ -397,14 +398,21 @@ pub unsafe fn luaL_loadbufferenv(
|
||||
ptr::write(data_ud, data);
|
||||
// By deferring the `free(data)` to the userdata destructor, we ensure that
|
||||
// even if `luau_load` throws an error, the `data` is still released.
|
||||
let ok = luau_load(L, name, data, size, env) == 0;
|
||||
let status = luau_load(L, name, data, size, env);
|
||||
lua_replace(L, -2); // replace data with the result
|
||||
if !ok {
|
||||
return LUA_ERRSYNTAX;
|
||||
status
|
||||
} else {
|
||||
luau_load(L, name, data, size, env)
|
||||
};
|
||||
|
||||
if status != 0 {
|
||||
if lua_isstring(L, -1) != 0 && CStr::from_ptr(lua_tostring(L, -1)) == c"not enough memory" {
|
||||
// A case for Luau >= 0.679
|
||||
return LUA_ERRMEM;
|
||||
}
|
||||
} else if luau_load(L, name, data, size, env) != 0 {
|
||||
return LUA_ERRSYNTAX;
|
||||
}
|
||||
|
||||
LUA_OK
|
||||
}
|
||||
|
||||
|
||||
@@ -235,6 +235,7 @@ unsafe extern "C-unwind" {
|
||||
) -> c_int;
|
||||
pub fn lua_call(L: *mut lua_State, nargs: c_int, nresults: c_int);
|
||||
pub fn lua_pcall(L: *mut lua_State, nargs: c_int, nresults: c_int, errfunc: c_int) -> c_int;
|
||||
pub fn lua_cpcall(L: *mut lua_State, f: lua_CFunction, ud: *mut c_void) -> c_int;
|
||||
|
||||
//
|
||||
// Coroutine functions
|
||||
|
||||
@@ -26,11 +26,14 @@ pub enum luarequire_WriteResult {
|
||||
#[repr(C)]
|
||||
pub struct luarequire_Configuration {
|
||||
// Returns whether requires are permitted from the given chunkname.
|
||||
pub is_require_allowed:
|
||||
unsafe extern "C" fn(L: *mut lua_State, ctx: *mut c_void, requirer_chunkname: *const c_char) -> bool,
|
||||
pub is_require_allowed: unsafe extern "C-unwind" fn(
|
||||
L: *mut lua_State,
|
||||
ctx: *mut c_void,
|
||||
requirer_chunkname: *const c_char,
|
||||
) -> bool,
|
||||
|
||||
// Resets the internal state to point at the requirer module.
|
||||
pub reset: unsafe extern "C" fn(
|
||||
pub reset: unsafe extern "C-unwind" fn(
|
||||
L: *mut lua_State,
|
||||
ctx: *mut c_void,
|
||||
requirer_chunkname: *const c_char,
|
||||
@@ -39,26 +42,27 @@ pub struct luarequire_Configuration {
|
||||
// Resets the internal state to point at an aliased module, given its exact path from a configuration
|
||||
// file. This function is only called when an alias's path cannot be resolved relative to its
|
||||
// configuration file.
|
||||
pub jump_to_alias: unsafe extern "C" fn(
|
||||
pub jump_to_alias: unsafe extern "C-unwind" fn(
|
||||
L: *mut lua_State,
|
||||
ctx: *mut c_void,
|
||||
path: *const c_char,
|
||||
) -> luarequire_NavigateResult,
|
||||
|
||||
// Navigates through the context by making mutations to the internal state.
|
||||
pub to_parent: unsafe extern "C" fn(L: *mut lua_State, ctx: *mut c_void) -> luarequire_NavigateResult,
|
||||
pub to_child: unsafe extern "C" fn(
|
||||
pub to_parent:
|
||||
unsafe extern "C-unwind" fn(L: *mut lua_State, ctx: *mut c_void) -> luarequire_NavigateResult,
|
||||
pub to_child: unsafe extern "C-unwind" fn(
|
||||
L: *mut lua_State,
|
||||
ctx: *mut c_void,
|
||||
name: *const c_char,
|
||||
) -> luarequire_NavigateResult,
|
||||
|
||||
// Returns whether the context is currently pointing at a module.
|
||||
pub is_module_present: unsafe extern "C" fn(L: *mut lua_State, ctx: *mut c_void) -> bool,
|
||||
pub is_module_present: unsafe extern "C-unwind" fn(L: *mut lua_State, ctx: *mut c_void) -> bool,
|
||||
|
||||
// Provides the contents of the current module. This function is only called if is_module_present returns
|
||||
// true.
|
||||
pub get_contents: unsafe extern "C" fn(
|
||||
// Provides a chunkname for the current module. This will be accessible through the debug library. This
|
||||
// function is only called if is_module_present returns true.
|
||||
pub get_chunkname: unsafe extern "C-unwind" fn(
|
||||
L: *mut lua_State,
|
||||
ctx: *mut c_void,
|
||||
buffer: *mut c_char,
|
||||
@@ -66,9 +70,9 @@ pub struct luarequire_Configuration {
|
||||
size_out: *mut usize,
|
||||
) -> luarequire_WriteResult,
|
||||
|
||||
// Provides a chunkname for the current module. This will be accessible through the debug library. This
|
||||
// function is only called if is_module_present returns true.
|
||||
pub get_chunkname: unsafe extern "C" fn(
|
||||
// Provides a loadname that identifies the current module and is passed to load. This function
|
||||
// is only called if is_module_present returns true.
|
||||
pub get_loadname: unsafe extern "C-unwind" fn(
|
||||
L: *mut lua_State,
|
||||
ctx: *mut c_void,
|
||||
buffer: *mut c_char,
|
||||
@@ -78,7 +82,7 @@ pub struct luarequire_Configuration {
|
||||
|
||||
// Provides a cache key representing the current module. This function is only called if
|
||||
// is_module_present returns true.
|
||||
pub get_cache_key: unsafe extern "C" fn(
|
||||
pub get_cache_key: unsafe extern "C-unwind" fn(
|
||||
L: *mut lua_State,
|
||||
ctx: *mut c_void,
|
||||
buffer: *mut c_char,
|
||||
@@ -89,17 +93,36 @@ pub struct luarequire_Configuration {
|
||||
// Returns whether a configuration file is present in the current context.
|
||||
// If not, require-by-string will call to_parent until either a configuration file is present or
|
||||
// NAVIGATE_FAILURE is returned (at root).
|
||||
pub is_config_present: unsafe extern "C" fn(L: *mut lua_State, ctx: *mut c_void) -> bool,
|
||||
pub is_config_present: unsafe extern "C-unwind" fn(L: *mut lua_State, ctx: *mut c_void) -> bool,
|
||||
|
||||
// Provides the contents of the configuration file in the current context.
|
||||
// This function is only called if is_config_present returns true.
|
||||
pub get_config: unsafe extern "C" fn(
|
||||
L: *mut lua_State,
|
||||
ctx: *mut c_void,
|
||||
buffer: *mut c_char,
|
||||
buffer_size: usize,
|
||||
size_out: *mut usize,
|
||||
) -> luarequire_WriteResult,
|
||||
// Parses the configuration file in the current context for the given alias and returns its
|
||||
// value or WRITE_FAILURE if not found. This function is only called if is_config_present
|
||||
// returns true. If this function pointer is set, get_config must not be set. Opting in to this
|
||||
// function pointer disables parsing configuration files internally and can be used for finer
|
||||
// control over the configuration file parsing process.
|
||||
pub get_alias: Option<
|
||||
unsafe extern "C-unwind" fn(
|
||||
L: *mut lua_State,
|
||||
ctx: *mut c_void,
|
||||
alias: *const c_char,
|
||||
buffer: *mut c_char,
|
||||
buffer_size: usize,
|
||||
size_out: *mut usize,
|
||||
) -> luarequire_WriteResult,
|
||||
>,
|
||||
|
||||
// Provides the contents of the configuration file in the current context. This function is only called
|
||||
// if is_config_present returns true. If this function pointer is set, get_alias must not be set. Opting
|
||||
// in to this function pointer enables parsing configuration files internally.
|
||||
pub get_config: Option<
|
||||
unsafe extern "C-unwind" fn(
|
||||
L: *mut lua_State,
|
||||
ctx: *mut c_void,
|
||||
buffer: *mut c_char,
|
||||
buffer_size: usize,
|
||||
size_out: *mut usize,
|
||||
) -> luarequire_WriteResult,
|
||||
>,
|
||||
|
||||
// Executes the module and places the result on the stack. Returns the number of results placed on the
|
||||
// stack.
|
||||
@@ -110,12 +133,12 @@ pub struct luarequire_Configuration {
|
||||
ctx: *mut c_void,
|
||||
path: *const c_char,
|
||||
chunkname: *const c_char,
|
||||
contents: *const c_char,
|
||||
loadname: *const c_char,
|
||||
) -> c_int,
|
||||
}
|
||||
|
||||
// Populates function pointers in the given luarequire_Configuration.
|
||||
pub type luarequire_Configuration_init = unsafe extern "C" fn(config: *mut luarequire_Configuration);
|
||||
pub type luarequire_Configuration_init = unsafe extern "C-unwind" fn(config: *mut luarequire_Configuration);
|
||||
|
||||
unsafe extern "C-unwind" {
|
||||
// Initializes and pushes the require closure onto the stack without registration.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "mlua_derive"
|
||||
version = "0.11.0-beta.1"
|
||||
version = "0.11.0"
|
||||
authors = ["Aleksandr Orlenko <zxteam@pm.me>"]
|
||||
edition = "2021"
|
||||
description = "Procedural macros for the mlua crate."
|
||||
|
||||
@@ -120,7 +120,7 @@ pub fn chunk(input: TokenStream) -> TokenStream {
|
||||
Some(ChunkMode::Text)
|
||||
}
|
||||
|
||||
fn source<'a>(self) -> IoResult<Cow<'a, [u8]>> {
|
||||
fn source<'a>(&self) -> IoResult<Cow<'a, [u8]>> {
|
||||
Ok(Cow::Borrowed((#source).as_bytes()))
|
||||
}
|
||||
}
|
||||
@@ -129,13 +129,13 @@ pub fn chunk(input: TokenStream) -> TokenStream {
|
||||
let globals = lua.globals();
|
||||
let env = lua.create_table()?;
|
||||
let meta = lua.create_table()?;
|
||||
meta.raw_set("__index", globals.clone())?;
|
||||
meta.raw_set("__newindex", globals)?;
|
||||
meta.raw_set("__index", &globals)?;
|
||||
meta.raw_set("__newindex", &globals)?;
|
||||
|
||||
// Add captured variables
|
||||
#(#caps)*
|
||||
|
||||
env.set_metatable(Some(meta));
|
||||
env.set_metatable(Some(meta))?;
|
||||
Ok(env)
|
||||
};
|
||||
|
||||
|
||||
+98
-17
@@ -1,6 +1,9 @@
|
||||
#[cfg(feature = "serialize")]
|
||||
use std::io;
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
use serde::ser::{Serialize, Serializer};
|
||||
|
||||
use crate::state::RawLua;
|
||||
use crate::types::ValueRef;
|
||||
|
||||
/// A Luau buffer type.
|
||||
@@ -16,16 +19,17 @@ pub struct Buffer(pub(crate) ValueRef);
|
||||
impl Buffer {
|
||||
/// Copies the buffer data into a new `Vec<u8>`.
|
||||
pub fn to_vec(&self) -> Vec<u8> {
|
||||
unsafe { self.as_slice().to_vec() }
|
||||
let lua = self.0.lua.lock();
|
||||
self.as_slice(&lua).to_vec()
|
||||
}
|
||||
|
||||
/// Returns the length of the buffer.
|
||||
pub fn len(&self) -> usize {
|
||||
unsafe { self.as_slice().len() }
|
||||
let lua = self.0.lua.lock();
|
||||
self.as_slice(&lua).len()
|
||||
}
|
||||
|
||||
/// Returns `true` if the buffer is empty.
|
||||
#[doc(hidden)]
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.len() == 0
|
||||
}
|
||||
@@ -35,7 +39,8 @@ impl Buffer {
|
||||
/// Offset is 0-based.
|
||||
#[track_caller]
|
||||
pub fn read_bytes<const N: usize>(&self, offset: usize) -> [u8; N] {
|
||||
let data = unsafe { self.as_slice() };
|
||||
let lua = self.0.lua.lock();
|
||||
let data = self.as_slice(&lua);
|
||||
let mut bytes = [0u8; N];
|
||||
bytes.copy_from_slice(&data[offset..offset + N]);
|
||||
bytes
|
||||
@@ -46,21 +51,36 @@ impl Buffer {
|
||||
/// Offset is 0-based.
|
||||
#[track_caller]
|
||||
pub fn write_bytes(&self, offset: usize, bytes: &[u8]) {
|
||||
let data = unsafe {
|
||||
let (buf, size) = self.as_raw_parts();
|
||||
std::slice::from_raw_parts_mut(buf, size)
|
||||
};
|
||||
let lua = self.0.lua.lock();
|
||||
let data = self.as_slice_mut(&lua);
|
||||
data[offset..offset + bytes.len()].copy_from_slice(bytes);
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn as_slice(&self) -> &[u8] {
|
||||
let (buf, size) = self.as_raw_parts();
|
||||
std::slice::from_raw_parts(buf, size)
|
||||
/// Returns an adaptor implementing [`io::Read`], [`io::Write`] and [`io::Seek`] over the
|
||||
/// buffer.
|
||||
///
|
||||
/// Buffer operations are infallible, none of the read/write functions will return a Err.
|
||||
pub fn cursor(self) -> impl io::Read + io::Write + io::Seek {
|
||||
BufferCursor(self, 0)
|
||||
}
|
||||
|
||||
pub(crate) fn as_slice(&self, lua: &RawLua) -> &[u8] {
|
||||
unsafe {
|
||||
let (buf, size) = self.as_raw_parts(lua);
|
||||
std::slice::from_raw_parts(buf, size)
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::mut_from_ref)]
|
||||
fn as_slice_mut(&self, lua: &RawLua) -> &mut [u8] {
|
||||
unsafe {
|
||||
let (buf, size) = self.as_raw_parts(lua);
|
||||
std::slice::from_raw_parts_mut(buf, size)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "luau")]
|
||||
unsafe fn as_raw_parts(&self) -> (*mut u8, usize) {
|
||||
let lua = self.0.lua.lock();
|
||||
unsafe fn as_raw_parts(&self, lua: &RawLua) -> (*mut u8, usize) {
|
||||
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");
|
||||
@@ -68,15 +88,76 @@ impl Buffer {
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "luau"))]
|
||||
unsafe fn as_raw_parts(&self) -> (*mut u8, usize) {
|
||||
unsafe fn as_raw_parts(&self, lua: &RawLua) -> (*mut u8, usize) {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
struct BufferCursor(Buffer, usize);
|
||||
|
||||
impl io::Read for BufferCursor {
|
||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
let lua = self.0 .0.lua.lock();
|
||||
let data = self.0.as_slice(&lua);
|
||||
if self.1 == data.len() {
|
||||
return Ok(0);
|
||||
}
|
||||
let len = buf.len().min(data.len() - self.1);
|
||||
buf[..len].copy_from_slice(&data[self.1..self.1 + len]);
|
||||
self.1 += len;
|
||||
Ok(len)
|
||||
}
|
||||
}
|
||||
|
||||
impl io::Write for BufferCursor {
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
let lua = self.0 .0.lua.lock();
|
||||
let data = self.0.as_slice_mut(&lua);
|
||||
if self.1 == data.len() {
|
||||
return Ok(0);
|
||||
}
|
||||
let len = buf.len().min(data.len() - self.1);
|
||||
data[self.1..self.1 + len].copy_from_slice(&buf[..len]);
|
||||
self.1 += len;
|
||||
Ok(len)
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl io::Seek for BufferCursor {
|
||||
fn seek(&mut self, pos: io::SeekFrom) -> io::Result<u64> {
|
||||
let lua = self.0 .0.lua.lock();
|
||||
let data = self.0.as_slice(&lua);
|
||||
let new_offset = match pos {
|
||||
io::SeekFrom::Start(offset) => offset as i64,
|
||||
io::SeekFrom::End(offset) => data.len() as i64 + offset,
|
||||
io::SeekFrom::Current(offset) => self.1 as i64 + offset,
|
||||
};
|
||||
if new_offset < 0 {
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::InvalidInput,
|
||||
"invalid seek to a negative position",
|
||||
));
|
||||
}
|
||||
if new_offset as usize > data.len() {
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::InvalidInput,
|
||||
"invalid seek to a position beyond the end of the buffer",
|
||||
));
|
||||
}
|
||||
self.1 = new_offset as usize;
|
||||
Ok(self.1 as u64)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
impl Serialize for Buffer {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error> {
|
||||
serializer.serialize_bytes(unsafe { self.as_slice() })
|
||||
let lua = self.0.lua.lock();
|
||||
serializer.serialize_bytes(self.as_slice(&lua))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+134
-60
@@ -38,28 +38,28 @@ pub trait AsChunk {
|
||||
}
|
||||
|
||||
/// Returns chunk data (can be text or binary)
|
||||
fn source<'a>(self) -> IoResult<Cow<'a, [u8]>>
|
||||
fn source<'a>(&self) -> IoResult<Cow<'a, [u8]>>
|
||||
where
|
||||
Self: 'a;
|
||||
}
|
||||
|
||||
impl AsChunk for &str {
|
||||
fn source<'a>(self) -> IoResult<Cow<'a, [u8]>>
|
||||
fn source<'a>(&self) -> IoResult<Cow<'a, [u8]>>
|
||||
where
|
||||
Self: 'a,
|
||||
{
|
||||
Ok(Cow::Borrowed(self.as_ref()))
|
||||
Ok(Cow::Borrowed(self.as_bytes()))
|
||||
}
|
||||
}
|
||||
|
||||
impl AsChunk for StdString {
|
||||
fn source<'a>(self) -> IoResult<Cow<'a, [u8]>> {
|
||||
Ok(Cow::Owned(self.into_bytes()))
|
||||
fn source<'a>(&self) -> IoResult<Cow<'a, [u8]>> {
|
||||
Ok(Cow::Owned(self.clone().into_bytes()))
|
||||
}
|
||||
}
|
||||
|
||||
impl AsChunk for &StdString {
|
||||
fn source<'a>(self) -> IoResult<Cow<'a, [u8]>>
|
||||
fn source<'a>(&self) -> IoResult<Cow<'a, [u8]>>
|
||||
where
|
||||
Self: 'a,
|
||||
{
|
||||
@@ -68,7 +68,7 @@ impl AsChunk for &StdString {
|
||||
}
|
||||
|
||||
impl AsChunk for &[u8] {
|
||||
fn source<'a>(self) -> IoResult<Cow<'a, [u8]>>
|
||||
fn source<'a>(&self) -> IoResult<Cow<'a, [u8]>>
|
||||
where
|
||||
Self: 'a,
|
||||
{
|
||||
@@ -77,13 +77,13 @@ impl AsChunk for &[u8] {
|
||||
}
|
||||
|
||||
impl AsChunk for Vec<u8> {
|
||||
fn source<'a>(self) -> IoResult<Cow<'a, [u8]>> {
|
||||
Ok(Cow::Owned(self))
|
||||
fn source<'a>(&self) -> IoResult<Cow<'a, [u8]>> {
|
||||
Ok(Cow::Owned(self.clone()))
|
||||
}
|
||||
}
|
||||
|
||||
impl AsChunk for &Vec<u8> {
|
||||
fn source<'a>(self) -> IoResult<Cow<'a, [u8]>>
|
||||
fn source<'a>(&self) -> IoResult<Cow<'a, [u8]>>
|
||||
where
|
||||
Self: 'a,
|
||||
{
|
||||
@@ -96,7 +96,7 @@ impl AsChunk for &Path {
|
||||
Some(format!("@{}", self.display()))
|
||||
}
|
||||
|
||||
fn source<'a>(self) -> IoResult<Cow<'a, [u8]>> {
|
||||
fn source<'a>(&self) -> IoResult<Cow<'a, [u8]>> {
|
||||
std::fs::read(self).map(Cow::Owned)
|
||||
}
|
||||
}
|
||||
@@ -106,11 +106,32 @@ impl AsChunk for PathBuf {
|
||||
Some(format!("@{}", self.display()))
|
||||
}
|
||||
|
||||
fn source<'a>(self) -> IoResult<Cow<'a, [u8]>> {
|
||||
fn source<'a>(&self) -> IoResult<Cow<'a, [u8]>> {
|
||||
std::fs::read(self).map(Cow::Owned)
|
||||
}
|
||||
}
|
||||
|
||||
impl<C: AsChunk + ?Sized> AsChunk for Box<C> {
|
||||
fn name(&self) -> Option<StdString> {
|
||||
(**self).name()
|
||||
}
|
||||
|
||||
fn environment(&self, lua: &Lua) -> Result<Option<Table>> {
|
||||
(**self).environment(lua)
|
||||
}
|
||||
|
||||
fn mode(&self) -> Option<ChunkMode> {
|
||||
(**self).mode()
|
||||
}
|
||||
|
||||
fn source<'a>(&self) -> IoResult<Cow<'a, [u8]>>
|
||||
where
|
||||
Self: 'a,
|
||||
{
|
||||
(**self).source()
|
||||
}
|
||||
}
|
||||
|
||||
/// Returned from [`Lua::load`] and is used to finalize loading and executing Lua main chunks.
|
||||
#[must_use = "`Chunk`s do nothing unless one of `exec`, `eval`, `call`, or `into_function` are called on them"]
|
||||
pub struct Chunk<'a> {
|
||||
@@ -139,18 +160,39 @@ pub enum CompileConstant {
|
||||
Boolean(bool),
|
||||
Number(crate::Number),
|
||||
Vector(crate::Vector),
|
||||
String(String),
|
||||
String(StdString),
|
||||
}
|
||||
|
||||
#[cfg(feature = "luau")]
|
||||
impl From<&'static str> for CompileConstant {
|
||||
fn from(s: &'static str) -> Self {
|
||||
CompileConstant::String(s.to_string())
|
||||
#[cfg(any(feature = "luau", doc))]
|
||||
impl From<bool> for CompileConstant {
|
||||
fn from(b: bool) -> Self {
|
||||
CompileConstant::Boolean(b)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "luau", doc))]
|
||||
type LibraryMemberConstantMap = std::sync::Arc<HashMap<(String, String), CompileConstant>>;
|
||||
impl From<crate::Number> for CompileConstant {
|
||||
fn from(n: crate::Number) -> Self {
|
||||
CompileConstant::Number(n)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "luau", doc))]
|
||||
impl From<crate::Vector> for CompileConstant {
|
||||
fn from(v: crate::Vector) -> Self {
|
||||
CompileConstant::Vector(v)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "luau", doc))]
|
||||
impl From<&str> for CompileConstant {
|
||||
fn from(s: &str) -> Self {
|
||||
CompileConstant::String(s.to_owned())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "luau", doc))]
|
||||
type LibraryMemberConstantMap = HashMap<(StdString, StdString), CompileConstant>;
|
||||
|
||||
/// Luau compiler
|
||||
#[cfg(any(feature = "luau", doc))]
|
||||
@@ -161,14 +203,14 @@ pub struct Compiler {
|
||||
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>,
|
||||
libraries_with_known_members: Vec<String>,
|
||||
vector_lib: Option<StdString>,
|
||||
vector_ctor: Option<StdString>,
|
||||
vector_type: Option<StdString>,
|
||||
mutable_globals: Vec<StdString>,
|
||||
userdata_types: Vec<StdString>,
|
||||
libraries_with_known_members: Vec<StdString>,
|
||||
library_constants: Option<LibraryMemberConstantMap>,
|
||||
disabled_builtins: Vec<String>,
|
||||
disabled_builtins: Vec<StdString>,
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "luau", doc))]
|
||||
@@ -246,70 +288,102 @@ impl Compiler {
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets alternative global builtin to construct vectors, in addition to default builtin
|
||||
/// `vector.create`.
|
||||
///
|
||||
/// To set the library and method name, use the `lib.ctor` format.
|
||||
#[doc(hidden)]
|
||||
#[must_use]
|
||||
pub fn set_vector_lib(mut self, lib: impl Into<String>) -> Self {
|
||||
self.vector_lib = Some(lib.into());
|
||||
pub fn set_vector_ctor(mut self, ctor: impl Into<StdString>) -> Self {
|
||||
let ctor = ctor.into();
|
||||
let lib_ctor = ctor.split_once('.');
|
||||
self.vector_lib = lib_ctor.as_ref().map(|&(lib, _)| lib.to_owned());
|
||||
self.vector_ctor = (lib_ctor.as_ref())
|
||||
.map(|&(_, ctor)| ctor.to_owned())
|
||||
.or(Some(ctor));
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets alternative vector type name for type tables, in addition to default type `vector`.
|
||||
#[doc(hidden)]
|
||||
#[must_use]
|
||||
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 {
|
||||
pub fn set_vector_type(mut self, r#type: impl Into<StdString>) -> Self {
|
||||
self.vector_type = Some(r#type.into());
|
||||
self
|
||||
}
|
||||
|
||||
/// Adds a mutable global.
|
||||
///
|
||||
/// It disables the import optimization for fields accessed through it.
|
||||
#[must_use]
|
||||
pub fn add_mutable_global(mut self, global: impl Into<StdString>) -> Self {
|
||||
self.mutable_globals.push(global.into());
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets a list of globals that are mutable.
|
||||
///
|
||||
/// It disables the import optimization for fields accessed through these.
|
||||
#[must_use]
|
||||
pub fn set_mutable_globals<S: Into<String>>(mut self, globals: Vec<S>) -> Self {
|
||||
pub fn set_mutable_globals<S: Into<StdString>>(mut self, globals: impl IntoIterator<Item = S>) -> Self {
|
||||
self.mutable_globals = globals.into_iter().map(|s| s.into()).collect();
|
||||
self
|
||||
}
|
||||
|
||||
/// Adds a userdata type to the list that will be included in the type information.
|
||||
#[must_use]
|
||||
pub fn add_userdata_type(mut self, r#type: impl Into<StdString>) -> Self {
|
||||
self.userdata_types.push(r#type.into());
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets a list of userdata types that will be included in the type information.
|
||||
#[must_use]
|
||||
pub fn set_userdata_types<S: Into<String>>(mut self, types: Vec<S>) -> Self {
|
||||
pub fn set_userdata_types<S: Into<StdString>>(mut self, types: impl IntoIterator<Item = S>) -> Self {
|
||||
self.userdata_types = types.into_iter().map(|s| s.into()).collect();
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets constants for known library members.
|
||||
/// Adds a constant for a known library member.
|
||||
///
|
||||
/// The constants are used by the compiler to optimize the generated bytecode.
|
||||
/// Optimization level must be at least 2 for this to have any effect.
|
||||
///
|
||||
/// The first element of the tuple is the library name,the second is the member name, and the
|
||||
/// third is the constant value.
|
||||
/// The `name` is a string in the format `lib.member`, where `lib` is the library name
|
||||
/// and `member` is the member (constant) name.
|
||||
#[must_use]
|
||||
pub fn set_library_constants<L, M>(mut self, constants: Vec<(L, M, CompileConstant)>) -> Self
|
||||
where
|
||||
L: Into<String>,
|
||||
M: Into<String>,
|
||||
{
|
||||
let map = constants
|
||||
.into_iter()
|
||||
.map(|(lib, member, cons)| ((lib.into(), member.into()), cons))
|
||||
.collect::<HashMap<_, _>>();
|
||||
self.library_constants = Some(std::sync::Arc::new(map));
|
||||
self.libraries_with_known_members = (self.library_constants.clone())
|
||||
.map(|map| map.keys().map(|(lib, _)| lib.clone()).collect())
|
||||
.unwrap_or_default();
|
||||
pub fn add_library_constant(
|
||||
mut self,
|
||||
name: impl AsRef<str>,
|
||||
r#const: impl Into<CompileConstant>,
|
||||
) -> Self {
|
||||
let Some((lib, member)) = name.as_ref().split_once('.') else {
|
||||
return self;
|
||||
};
|
||||
let (lib, member) = (lib.to_owned(), member.to_owned());
|
||||
|
||||
if !self.libraries_with_known_members.contains(&lib) {
|
||||
self.libraries_with_known_members.push(lib.clone());
|
||||
}
|
||||
self.library_constants
|
||||
.get_or_insert_with(HashMap::new)
|
||||
.insert((lib, member), r#const.into());
|
||||
self
|
||||
}
|
||||
|
||||
/// Adds a builtin that should be disabled.
|
||||
#[must_use]
|
||||
pub fn add_disabled_builtin(mut self, builtin: impl Into<StdString>) -> Self {
|
||||
self.disabled_builtins.push(builtin.into());
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets a list of builtins that should be disabled.
|
||||
#[must_use]
|
||||
pub fn set_disabled_builtins<S: Into<String>>(mut self, builtins: Vec<S>) -> Self {
|
||||
pub fn set_disabled_builtins<S: Into<StdString>>(
|
||||
mut self,
|
||||
builtins: impl IntoIterator<Item = S>,
|
||||
) -> Self {
|
||||
self.disabled_builtins = builtins.into_iter().map(|s| s.into()).collect();
|
||||
self
|
||||
}
|
||||
@@ -416,7 +490,7 @@ impl Compiler {
|
||||
if bytecode.first() == Some(&0) {
|
||||
// The rest of the bytecode is the error message starting with `:`
|
||||
// See https://github.com/luau-lang/luau/blob/0.640/Compiler/src/Compiler.cpp#L4336
|
||||
let message = String::from_utf8_lossy(&bytecode[2..]).to_string();
|
||||
let message = StdString::from_utf8_lossy(&bytecode[2..]).into_owned();
|
||||
return Err(Error::SyntaxError {
|
||||
incomplete_input: message.ends_with("<eof>"),
|
||||
message,
|
||||
@@ -439,7 +513,7 @@ impl Chunk<'_> {
|
||||
/// - `@` - file path (when truncation is needed, the end of the file path is kept, as this is
|
||||
/// more useful for identifying the file)
|
||||
/// - `=` - custom chunk name (when truncation is needed, the beginning of the name is kept)
|
||||
pub fn set_name(mut self, name: impl Into<String>) -> Self {
|
||||
pub fn set_name(mut self, name: impl Into<StdString>) -> Self {
|
||||
self.name = name.into();
|
||||
self
|
||||
}
|
||||
@@ -616,7 +690,7 @@ impl Chunk<'_> {
|
||||
if let Ok(ref source) = self.source {
|
||||
if self.detect_mode() == ChunkMode::Text {
|
||||
let lua = self.lua.lock();
|
||||
if let Some(cache) = lua.app_data_ref_unguarded::<ChunksCache>() {
|
||||
if let Some(cache) = lua.priv_app_data_ref::<ChunksCache>() {
|
||||
if let Some(data) = cache.0.get(source.as_ref()) {
|
||||
self.source = Ok(Cow::Owned(data.clone()));
|
||||
self.mode = Some(ChunkMode::Binary);
|
||||
@@ -633,12 +707,12 @@ impl Chunk<'_> {
|
||||
if let Ok(ref binary_source) = self.source {
|
||||
if self.detect_mode() == ChunkMode::Binary {
|
||||
let lua = self.lua.lock();
|
||||
if let Some(mut cache) = lua.app_data_mut_unguarded::<ChunksCache>() {
|
||||
if let Some(mut cache) = lua.priv_app_data_mut::<ChunksCache>() {
|
||||
cache.0.insert(text_source, binary_source.to_vec());
|
||||
} else {
|
||||
let mut cache = ChunksCache(HashMap::new());
|
||||
cache.0.insert(text_source, binary_source.to_vec());
|
||||
let _ = lua.try_set_app_data(cache);
|
||||
lua.set_priv_app_data(cache);
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -687,7 +761,7 @@ impl Chunk<'_> {
|
||||
ChunkMode::Text
|
||||
}
|
||||
|
||||
fn convert_name(name: String) -> Result<CString> {
|
||||
fn convert_name(name: StdString) -> Result<CString> {
|
||||
CString::new(name).map_err(|err| Error::runtime(format!("invalid name: {err}")))
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -645,7 +645,7 @@ impl FromLua for BString {
|
||||
match value {
|
||||
Value::String(s) => Ok((*s.as_bytes()).into()),
|
||||
#[cfg(feature = "luau")]
|
||||
Value::Buffer(buf) => unsafe { Ok(buf.as_slice().into()) },
|
||||
Value::Buffer(buf) => Ok(buf.to_vec().into()),
|
||||
_ => Ok((*lua
|
||||
.coerce_string(value)?
|
||||
.ok_or_else(|| Error::FromLuaConversionError {
|
||||
|
||||
+85
-102
@@ -1,66 +1,33 @@
|
||||
use std::borrow::Cow;
|
||||
use std::cell::UnsafeCell;
|
||||
use std::ops::Deref;
|
||||
#[cfg(not(feature = "luau"))]
|
||||
use std::ops::{BitOr, BitOrAssign};
|
||||
use std::os::raw::c_int;
|
||||
|
||||
use ffi::lua_Debug;
|
||||
use ffi::{lua_Debug, lua_State};
|
||||
|
||||
use crate::function::Function;
|
||||
use crate::state::RawLua;
|
||||
use crate::types::ReentrantMutexGuard;
|
||||
use crate::util::{linenumber_to_usize, ptr_to_lossy_str, ptr_to_str};
|
||||
use crate::util::{assert_stack, linenumber_to_usize, ptr_to_lossy_str, ptr_to_str, StackGuard};
|
||||
|
||||
/// Contains information about currently executing Lua code.
|
||||
///
|
||||
/// The `Debug` structure is provided as a parameter to the hook function set with
|
||||
/// [`Lua::set_hook`]. You may call the methods on this structure to retrieve information about the
|
||||
/// Lua code executing at the time that the hook function was called. Further information can be
|
||||
/// found in the Lua [documentation].
|
||||
/// You may call the methods on this structure to retrieve information about the Lua code executing
|
||||
/// at the specific level. Further information can be found in the Lua [documentation].
|
||||
///
|
||||
/// [documentation]: https://www.lua.org/manual/5.4/manual.html#lua_Debug
|
||||
/// [`Lua::set_hook`]: crate::Lua::set_hook
|
||||
pub struct Debug<'a> {
|
||||
lua: EitherLua<'a>,
|
||||
ar: ActivationRecord,
|
||||
#[cfg(feature = "luau")]
|
||||
state: *mut lua_State,
|
||||
lua: &'a RawLua,
|
||||
#[cfg_attr(not(feature = "luau"), allow(unused))]
|
||||
level: c_int,
|
||||
}
|
||||
|
||||
enum EitherLua<'a> {
|
||||
Owned(ReentrantMutexGuard<'a, RawLua>),
|
||||
#[cfg(not(feature = "luau"))]
|
||||
Borrowed(&'a RawLua),
|
||||
}
|
||||
|
||||
impl Deref for EitherLua<'_> {
|
||||
type Target = RawLua;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
match self {
|
||||
EitherLua::Owned(guard) => guard,
|
||||
#[cfg(not(feature = "luau"))]
|
||||
EitherLua::Borrowed(lua) => lua,
|
||||
}
|
||||
}
|
||||
ar: *mut lua_Debug,
|
||||
}
|
||||
|
||||
impl<'a> Debug<'a> {
|
||||
// We assume the lock is held when this function is called.
|
||||
#[cfg(not(feature = "luau"))]
|
||||
pub(crate) fn new(lua: &'a RawLua, ar: *mut lua_Debug) -> Self {
|
||||
pub(crate) fn new(lua: &'a RawLua, level: c_int, ar: *mut lua_Debug) -> Self {
|
||||
Debug {
|
||||
lua: EitherLua::Borrowed(lua),
|
||||
ar: ActivationRecord::Borrowed(ar),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn new_owned(guard: ReentrantMutexGuard<'a, RawLua>, _level: c_int, ar: lua_Debug) -> Self {
|
||||
Debug {
|
||||
lua: EitherLua::Owned(guard),
|
||||
ar: ActivationRecord::Owned(UnsafeCell::new(ar)),
|
||||
#[cfg(feature = "luau")]
|
||||
level: _level,
|
||||
state: lua.state(),
|
||||
lua,
|
||||
ar,
|
||||
level,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -74,7 +41,7 @@ impl<'a> Debug<'a> {
|
||||
#[cfg_attr(docsrs, doc(cfg(not(feature = "luau"))))]
|
||||
pub fn event(&self) -> DebugEvent {
|
||||
unsafe {
|
||||
match (*self.ar.get()).event {
|
||||
match (*self.ar).event {
|
||||
ffi::LUA_HOOKCALL => DebugEvent::Call,
|
||||
ffi::LUA_HOOKRET => DebugEvent::Ret,
|
||||
ffi::LUA_HOOKTAILCALL => DebugEvent::TailCall,
|
||||
@@ -85,24 +52,48 @@ impl<'a> Debug<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Corresponds to the `n` what mask.
|
||||
pub fn names(&self) -> DebugNames {
|
||||
/// Returns the function that is running at the given level.
|
||||
///
|
||||
/// Corresponds to the `f` "what" mask.
|
||||
pub fn function(&self) -> Function {
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(self.state);
|
||||
assert_stack(self.state, 1);
|
||||
|
||||
#[cfg(not(feature = "luau"))]
|
||||
mlua_assert!(
|
||||
ffi::lua_getinfo(self.state, cstr!("f"), self.ar) != 0,
|
||||
"lua_getinfo failed with `f`"
|
||||
);
|
||||
#[cfg(feature = "luau")]
|
||||
mlua_assert!(
|
||||
ffi::lua_getinfo(self.state, self.level, cstr!("f"), self.ar) != 0,
|
||||
"lua_getinfo failed with `f`"
|
||||
);
|
||||
|
||||
ffi::lua_xmove(self.state, self.lua.ref_thread(), 1);
|
||||
Function(self.lua.pop_ref_thread())
|
||||
}
|
||||
}
|
||||
|
||||
/// Corresponds to the `n` "what" mask.
|
||||
pub fn names(&self) -> DebugNames<'_> {
|
||||
unsafe {
|
||||
#[cfg(not(feature = "luau"))]
|
||||
mlua_assert!(
|
||||
ffi::lua_getinfo(self.lua.state(), cstr!("n"), self.ar.get()) != 0,
|
||||
ffi::lua_getinfo(self.state, cstr!("n"), self.ar) != 0,
|
||||
"lua_getinfo failed with `n`"
|
||||
);
|
||||
#[cfg(feature = "luau")]
|
||||
mlua_assert!(
|
||||
ffi::lua_getinfo(self.lua.state(), self.level, cstr!("n"), self.ar.get()) != 0,
|
||||
ffi::lua_getinfo(self.state, self.level, cstr!("n"), self.ar) != 0,
|
||||
"lua_getinfo failed with `n`"
|
||||
);
|
||||
|
||||
DebugNames {
|
||||
name: ptr_to_lossy_str((*self.ar.get()).name),
|
||||
name: ptr_to_lossy_str((*self.ar).name),
|
||||
#[cfg(not(feature = "luau"))]
|
||||
name_what: match ptr_to_str((*self.ar.get()).namewhat) {
|
||||
name_what: match ptr_to_str((*self.ar).namewhat) {
|
||||
Some("") => None,
|
||||
val => val,
|
||||
},
|
||||
@@ -112,118 +103,110 @@ impl<'a> Debug<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Corresponds to the `S` what mask.
|
||||
pub fn source(&self) -> DebugSource {
|
||||
/// Corresponds to the `S` "what" mask.
|
||||
pub fn source(&self) -> DebugSource<'_> {
|
||||
unsafe {
|
||||
#[cfg(not(feature = "luau"))]
|
||||
mlua_assert!(
|
||||
ffi::lua_getinfo(self.lua.state(), cstr!("S"), self.ar.get()) != 0,
|
||||
ffi::lua_getinfo(self.state, cstr!("S"), self.ar) != 0,
|
||||
"lua_getinfo failed with `S`"
|
||||
);
|
||||
#[cfg(feature = "luau")]
|
||||
mlua_assert!(
|
||||
ffi::lua_getinfo(self.lua.state(), self.level, cstr!("s"), self.ar.get()) != 0,
|
||||
ffi::lua_getinfo(self.state, self.level, cstr!("s"), self.ar) != 0,
|
||||
"lua_getinfo failed with `s`"
|
||||
);
|
||||
|
||||
DebugSource {
|
||||
source: ptr_to_lossy_str((*self.ar.get()).source),
|
||||
source: ptr_to_lossy_str((*self.ar).source),
|
||||
#[cfg(not(feature = "luau"))]
|
||||
short_src: ptr_to_lossy_str((*self.ar.get()).short_src.as_ptr()),
|
||||
short_src: ptr_to_lossy_str((*self.ar).short_src.as_ptr()),
|
||||
#[cfg(feature = "luau")]
|
||||
short_src: ptr_to_lossy_str((*self.ar.get()).short_src),
|
||||
line_defined: linenumber_to_usize((*self.ar.get()).linedefined),
|
||||
short_src: ptr_to_lossy_str((*self.ar).short_src),
|
||||
line_defined: linenumber_to_usize((*self.ar).linedefined),
|
||||
#[cfg(not(feature = "luau"))]
|
||||
last_line_defined: linenumber_to_usize((*self.ar.get()).lastlinedefined),
|
||||
last_line_defined: linenumber_to_usize((*self.ar).lastlinedefined),
|
||||
#[cfg(feature = "luau")]
|
||||
last_line_defined: None,
|
||||
what: ptr_to_str((*self.ar.get()).what).unwrap_or("main"),
|
||||
what: ptr_to_str((*self.ar).what).unwrap_or("main"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Corresponds to the `l` what mask. Returns the current line.
|
||||
#[doc(hidden)]
|
||||
#[deprecated(note = "Use `current_line` instead")]
|
||||
pub fn curr_line(&self) -> i32 {
|
||||
self.current_line().map(|n| n as i32).unwrap_or(-1)
|
||||
}
|
||||
|
||||
/// Corresponds to the `l` "what" mask. Returns the current line.
|
||||
pub fn current_line(&self) -> Option<usize> {
|
||||
unsafe {
|
||||
#[cfg(not(feature = "luau"))]
|
||||
mlua_assert!(
|
||||
ffi::lua_getinfo(self.lua.state(), cstr!("l"), self.ar.get()) != 0,
|
||||
ffi::lua_getinfo(self.state, cstr!("l"), self.ar) != 0,
|
||||
"lua_getinfo failed with `l`"
|
||||
);
|
||||
#[cfg(feature = "luau")]
|
||||
mlua_assert!(
|
||||
ffi::lua_getinfo(self.lua.state(), self.level, cstr!("l"), self.ar.get()) != 0,
|
||||
ffi::lua_getinfo(self.state, self.level, cstr!("l"), self.ar) != 0,
|
||||
"lua_getinfo failed with `l`"
|
||||
);
|
||||
|
||||
(*self.ar.get()).currentline
|
||||
linenumber_to_usize((*self.ar).currentline)
|
||||
}
|
||||
}
|
||||
|
||||
/// Corresponds to the `t` what mask. Returns true if the hook is in a function tail call, false
|
||||
/// otherwise.
|
||||
#[cfg(not(feature = "luau"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(not(feature = "luau"))))]
|
||||
/// Corresponds to the `t` "what" mask. Returns true if the hook is in a function tail call,
|
||||
/// false otherwise.
|
||||
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
|
||||
#[cfg_attr(
|
||||
docsrs,
|
||||
doc(cfg(any(feature = "lua54", feature = "lua53", feature = "lua52")))
|
||||
)]
|
||||
pub fn is_tail_call(&self) -> bool {
|
||||
unsafe {
|
||||
mlua_assert!(
|
||||
ffi::lua_getinfo(self.lua.state(), cstr!("t"), self.ar.get()) != 0,
|
||||
ffi::lua_getinfo(self.state, cstr!("t"), self.ar) != 0,
|
||||
"lua_getinfo failed with `t`"
|
||||
);
|
||||
(*self.ar.get()).currentline != 0
|
||||
(*self.ar).istailcall != 0
|
||||
}
|
||||
}
|
||||
|
||||
/// Corresponds to the `u` what mask.
|
||||
/// Corresponds to the `u` "what" mask.
|
||||
pub fn stack(&self) -> DebugStack {
|
||||
unsafe {
|
||||
#[cfg(not(feature = "luau"))]
|
||||
mlua_assert!(
|
||||
ffi::lua_getinfo(self.lua.state(), cstr!("u"), self.ar.get()) != 0,
|
||||
ffi::lua_getinfo(self.state, cstr!("u"), self.ar) != 0,
|
||||
"lua_getinfo failed with `u`"
|
||||
);
|
||||
#[cfg(feature = "luau")]
|
||||
mlua_assert!(
|
||||
ffi::lua_getinfo(self.lua.state(), self.level, cstr!("au"), self.ar.get()) != 0,
|
||||
ffi::lua_getinfo(self.state, self.level, cstr!("au"), self.ar) != 0,
|
||||
"lua_getinfo failed with `au`"
|
||||
);
|
||||
|
||||
#[cfg(not(feature = "luau"))]
|
||||
let stack = DebugStack {
|
||||
num_ups: (*self.ar.get()).nups as _,
|
||||
num_ups: (*self.ar).nups as _,
|
||||
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
|
||||
num_params: (*self.ar.get()).nparams as _,
|
||||
num_params: (*self.ar).nparams as _,
|
||||
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
|
||||
is_vararg: (*self.ar.get()).isvararg != 0,
|
||||
is_vararg: (*self.ar).isvararg != 0,
|
||||
};
|
||||
#[cfg(feature = "luau")]
|
||||
let stack = DebugStack {
|
||||
num_ups: (*self.ar.get()).nupvals,
|
||||
num_params: (*self.ar.get()).nparams,
|
||||
is_vararg: (*self.ar.get()).isvararg != 0,
|
||||
num_ups: (*self.ar).nupvals,
|
||||
num_params: (*self.ar).nparams,
|
||||
is_vararg: (*self.ar).isvararg != 0,
|
||||
};
|
||||
stack
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum ActivationRecord {
|
||||
#[cfg(not(feature = "luau"))]
|
||||
Borrowed(*mut lua_Debug),
|
||||
Owned(UnsafeCell<lua_Debug>),
|
||||
}
|
||||
|
||||
impl ActivationRecord {
|
||||
#[inline]
|
||||
fn get(&self) -> *mut lua_Debug {
|
||||
match self {
|
||||
#[cfg(not(feature = "luau"))]
|
||||
ActivationRecord::Borrowed(x) => *x,
|
||||
ActivationRecord::Owned(x) => x.get(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents a specific event that triggered the hook.
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub enum DebugEvent {
|
||||
@@ -382,7 +365,7 @@ impl HookTriggers {
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "luau"))]
|
||||
impl BitOr for HookTriggers {
|
||||
impl std::ops::BitOr for HookTriggers {
|
||||
type Output = Self;
|
||||
|
||||
fn bitor(mut self, rhs: Self) -> Self::Output {
|
||||
@@ -397,7 +380,7 @@ impl BitOr for HookTriggers {
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "luau"))]
|
||||
impl BitOrAssign for HookTriggers {
|
||||
impl std::ops::BitOrAssign for HookTriggers {
|
||||
fn bitor_assign(&mut self, rhs: Self) {
|
||||
*self = *self | rhs;
|
||||
}
|
||||
+8
-8
@@ -183,12 +183,12 @@ pub enum Error {
|
||||
/// and returned again.
|
||||
PreviouslyResumedPanic,
|
||||
/// Serialization error.
|
||||
#[cfg(feature = "serialize")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "serialize")))]
|
||||
#[cfg(feature = "serde")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "serde")))]
|
||||
SerializeError(StdString),
|
||||
/// Deserialization error.
|
||||
#[cfg(feature = "serialize")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "serialize")))]
|
||||
#[cfg(feature = "serde")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "serde")))]
|
||||
DeserializeError(StdString),
|
||||
/// A custom error.
|
||||
///
|
||||
@@ -309,11 +309,11 @@ impl fmt::Display for Error {
|
||||
Error::PreviouslyResumedPanic => {
|
||||
write!(fmt, "previously resumed panic returned again")
|
||||
}
|
||||
#[cfg(feature = "serialize")]
|
||||
#[cfg(feature = "serde")]
|
||||
Error::SerializeError(err) => {
|
||||
write!(fmt, "serialize error: {err}")
|
||||
},
|
||||
#[cfg(feature = "serialize")]
|
||||
#[cfg(feature = "serde")]
|
||||
Error::DeserializeError(err) => {
|
||||
write!(fmt, "deserialize error: {err}")
|
||||
},
|
||||
@@ -494,14 +494,14 @@ impl From<Utf8Error> for Error {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
#[cfg(feature = "serde")]
|
||||
impl serde::ser::Error for Error {
|
||||
fn custom<T: fmt::Display>(msg: T) -> Self {
|
||||
Self::SerializeError(msg.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
#[cfg(feature = "serde")]
|
||||
impl serde::de::Error for Error {
|
||||
fn custom<T: fmt::Display>(msg: T) -> Self {
|
||||
Self::DeserializeError(msg.to_string())
|
||||
|
||||
+35
-16
@@ -253,7 +253,7 @@ impl Function {
|
||||
"#,
|
||||
)
|
||||
.try_cache()
|
||||
.set_name("__mlua_bind")
|
||||
.set_name("=__mlua_bind")
|
||||
.call((self, args_wrapper))
|
||||
}
|
||||
|
||||
@@ -434,7 +434,7 @@ impl Function {
|
||||
/// [`Compiler::set_coverage_level`]: crate::chunk::Compiler::set_coverage_level
|
||||
#[cfg(any(feature = "luau", doc))]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
|
||||
pub fn coverage<F>(&self, mut func: F)
|
||||
pub fn coverage<F>(&self, func: F)
|
||||
where
|
||||
F: FnMut(CoverageInfo),
|
||||
{
|
||||
@@ -454,13 +454,16 @@ impl Function {
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let rust_callback = &mut *(data as *mut F);
|
||||
rust_callback(CoverageInfo {
|
||||
function,
|
||||
line_defined,
|
||||
depth,
|
||||
hits: slice::from_raw_parts(hits, size).to_vec(),
|
||||
});
|
||||
let rust_callback = &*(data as *const RefCell<F>);
|
||||
if let Ok(mut rust_callback) = rust_callback.try_borrow_mut() {
|
||||
// Call the Rust callback with CoverageInfo
|
||||
rust_callback(CoverageInfo {
|
||||
function,
|
||||
line_defined,
|
||||
depth,
|
||||
hits: slice::from_raw_parts(hits, size).to_vec(),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
let lua = self.0.lua.lock();
|
||||
@@ -470,7 +473,8 @@ impl Function {
|
||||
assert_stack(state, 1);
|
||||
|
||||
lua.push_ref(&self.0);
|
||||
let func_ptr = &mut func as *mut F as *mut c_void;
|
||||
let func = RefCell::new(func);
|
||||
let func_ptr = &func as *const RefCell<F> as *mut c_void;
|
||||
ffi::lua_getcoverage(state, -1, func_ptr, callback::<F>);
|
||||
}
|
||||
}
|
||||
@@ -492,16 +496,24 @@ impl Function {
|
||||
/// This function returns shallow clone (same handle) for Rust/C functions.
|
||||
#[cfg(any(feature = "luau", doc))]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
|
||||
pub fn deep_clone(&self) -> Self {
|
||||
pub fn deep_clone(&self) -> Result<Self> {
|
||||
let lua = self.0.lua.lock();
|
||||
let ref_thread = lua.ref_thread();
|
||||
let state = lua.state();
|
||||
unsafe {
|
||||
if ffi::lua_iscfunction(ref_thread, self.0.index) != 0 {
|
||||
return self.clone();
|
||||
let _sg = StackGuard::new(state);
|
||||
check_stack(state, 2)?;
|
||||
|
||||
lua.push_ref(&self.0);
|
||||
if ffi::lua_iscfunction(state, -1) != 0 {
|
||||
return Ok(self.clone());
|
||||
}
|
||||
|
||||
ffi::lua_clonefunction(ref_thread, self.0.index);
|
||||
Function(lua.pop_ref_thread())
|
||||
if lua.unlikely_memory_error() {
|
||||
ffi::lua_clonefunction(state, -1);
|
||||
} else {
|
||||
protect_lua!(state, 1, 1, fn(state) ffi::lua_clonefunction(state, -1))?;
|
||||
}
|
||||
Ok(Function(lua.pop_ref()))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -645,6 +657,13 @@ impl LuaType for Function {
|
||||
#[must_use = "futures do nothing unless you `.await` or poll them"]
|
||||
pub struct AsyncCallFuture<R: FromLuaMulti>(Result<AsyncThread<R>>);
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
impl<R: FromLuaMulti> AsyncCallFuture<R> {
|
||||
pub(crate) fn error(err: Error) -> Self {
|
||||
AsyncCallFuture(Err(err))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
impl<R: FromLuaMulti> Future for AsyncCallFuture<R> {
|
||||
type Output = Result<R>;
|
||||
|
||||
+10
-10
@@ -35,7 +35,7 @@
|
||||
//! The [`Value`] enum and other types implement [`serde::Serialize`] trait to support serializing
|
||||
//! Lua values into Rust values.
|
||||
//!
|
||||
//! Requires `feature = "serialize"`.
|
||||
//! Requires `feature = "serde"`.
|
||||
//!
|
||||
//! # Async/await support
|
||||
//!
|
||||
@@ -75,9 +75,9 @@ mod macros;
|
||||
mod buffer;
|
||||
mod chunk;
|
||||
mod conversion;
|
||||
mod debug;
|
||||
mod error;
|
||||
mod function;
|
||||
mod hook;
|
||||
#[cfg(any(feature = "luau", doc))]
|
||||
mod luau;
|
||||
mod memory;
|
||||
@@ -101,9 +101,9 @@ pub use bstr::BString;
|
||||
pub use ffi::{self, lua_CFunction, lua_State};
|
||||
|
||||
pub use crate::chunk::{AsChunk, Chunk, ChunkMode};
|
||||
pub use crate::debug::{Debug, DebugEvent, DebugNames, DebugSource, DebugStack};
|
||||
pub use crate::error::{Error, ErrorContext, ExternalError, ExternalResult, Result};
|
||||
pub use crate::function::{Function, FunctionInfo};
|
||||
pub use crate::hook::{Debug, DebugEvent, DebugNames, DebugSource, DebugStack};
|
||||
pub use crate::multi::{MultiValue, Variadic};
|
||||
pub use crate::scope::Scope;
|
||||
pub use crate::state::{GCMode, Lua, LuaOptions, WeakLua};
|
||||
@@ -124,7 +124,7 @@ pub use crate::userdata::{
|
||||
pub use crate::value::{Nil, Value};
|
||||
|
||||
#[cfg(not(feature = "luau"))]
|
||||
pub use crate::hook::HookTriggers;
|
||||
pub use crate::debug::HookTriggers;
|
||||
|
||||
#[cfg(any(feature = "luau", doc))]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
|
||||
@@ -132,7 +132,7 @@ pub use crate::{
|
||||
buffer::Buffer,
|
||||
chunk::{CompileConstant, Compiler},
|
||||
function::CoverageInfo,
|
||||
luau::{NavigateError, Require},
|
||||
luau::{NavigateError, Require, TextRequirer},
|
||||
vector::Vector,
|
||||
};
|
||||
|
||||
@@ -140,12 +140,12 @@ pub use crate::{
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
|
||||
pub use crate::{thread::AsyncThread, traits::LuaNativeAsyncFn};
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
#[cfg(feature = "serde")]
|
||||
#[doc(inline)]
|
||||
pub use crate::serde::{de::Options as DeserializeOptions, ser::Options as SerializeOptions, LuaSerdeExt};
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "serialize")))]
|
||||
#[cfg(feature = "serde")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "serde")))]
|
||||
pub mod serde;
|
||||
|
||||
#[cfg(feature = "mlua_derive")]
|
||||
@@ -217,7 +217,7 @@ pub use mlua_derive::FromLua;
|
||||
///
|
||||
/// You can register multiple entrypoints as required.
|
||||
///
|
||||
/// ```
|
||||
/// ```ignore
|
||||
/// use mlua::{Lua, Result, Table};
|
||||
///
|
||||
/// #[mlua::lua_module]
|
||||
@@ -254,7 +254,7 @@ pub use mlua_derive::FromLua;
|
||||
/// ...
|
||||
/// }
|
||||
/// ```
|
||||
#[cfg(any(feature = "module", docsrs))]
|
||||
#[cfg(all(feature = "mlua_derive", any(feature = "module", doc)))]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "module")))]
|
||||
pub use mlua_derive::lua_module;
|
||||
|
||||
|
||||
+30
-8
@@ -1,11 +1,15 @@
|
||||
use std::ffi::CStr;
|
||||
use std::os::raw::c_int;
|
||||
use std::ptr;
|
||||
|
||||
use crate::chunk::ChunkMode;
|
||||
use crate::error::Result;
|
||||
use crate::function::Function;
|
||||
use crate::state::Lua;
|
||||
use crate::state::{callback_error_ext, ExtraData, Lua};
|
||||
use crate::traits::{FromLuaMulti, IntoLua};
|
||||
use crate::types::MaybeSend;
|
||||
|
||||
pub use require::{NavigateError, Require};
|
||||
pub use require::{NavigateError, Require, TextRequirer};
|
||||
|
||||
// Since Luau has some missing standard functions, we re-implement them here
|
||||
|
||||
@@ -14,7 +18,7 @@ impl Lua {
|
||||
/// and load modules.
|
||||
#[cfg(any(feature = "luau", doc))]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
|
||||
pub fn create_require_function<R: Require + 'static>(&self, require: R) -> Result<Function> {
|
||||
pub fn create_require_function<R: Require + MaybeSend + 'static>(&self, require: R) -> Result<Function> {
|
||||
require::create_require_function(self, require)
|
||||
}
|
||||
|
||||
@@ -22,6 +26,7 @@ impl Lua {
|
||||
let globals = self.globals();
|
||||
|
||||
globals.raw_set("collectgarbage", self.create_c_function(lua_collectgarbage)?)?;
|
||||
globals.raw_set("loadstring", self.create_c_function(lua_loadstring)?)?;
|
||||
|
||||
// Set `_VERSION` global to include version number
|
||||
// The environment variable `LUAU_VERSION` set by the build script
|
||||
@@ -41,16 +46,17 @@ unsafe extern "C-unwind" fn lua_collectgarbage(state: *mut ffi::lua_State) -> c_
|
||||
let option = ffi::luaL_optstring(state, 1, cstr!("collect"));
|
||||
let option = CStr::from_ptr(option);
|
||||
let arg = ffi::luaL_optinteger(state, 2, 0);
|
||||
let is_sandboxed = (*ExtraData::get(state)).sandboxed;
|
||||
match option.to_str() {
|
||||
Ok("collect") => {
|
||||
Ok("collect") if !is_sandboxed => {
|
||||
ffi::lua_gc(state, ffi::LUA_GCCOLLECT, 0);
|
||||
0
|
||||
}
|
||||
Ok("stop") => {
|
||||
Ok("stop") if !is_sandboxed => {
|
||||
ffi::lua_gc(state, ffi::LUA_GCSTOP, 0);
|
||||
0
|
||||
}
|
||||
Ok("restart") => {
|
||||
Ok("restart") if !is_sandboxed => {
|
||||
ffi::lua_gc(state, ffi::LUA_GCRESTART, 0);
|
||||
0
|
||||
}
|
||||
@@ -60,12 +66,12 @@ unsafe extern "C-unwind" fn lua_collectgarbage(state: *mut ffi::lua_State) -> c_
|
||||
ffi::lua_pushnumber(state, kbytes + kbytes_rem / 1024.0);
|
||||
1
|
||||
}
|
||||
Ok("step") => {
|
||||
Ok("step") if !is_sandboxed => {
|
||||
let res = ffi::lua_gc(state, ffi::LUA_GCSTEP, arg as _);
|
||||
ffi::lua_pushboolean(state, res);
|
||||
1
|
||||
}
|
||||
Ok("isrunning") => {
|
||||
Ok("isrunning") if !is_sandboxed => {
|
||||
let res = ffi::lua_gc(state, ffi::LUA_GCISRUNNING, 0);
|
||||
ffi::lua_pushboolean(state, res);
|
||||
1
|
||||
@@ -74,4 +80,20 @@ unsafe extern "C-unwind" fn lua_collectgarbage(state: *mut ffi::lua_State) -> c_
|
||||
}
|
||||
}
|
||||
|
||||
unsafe extern "C-unwind" fn lua_loadstring(state: *mut ffi::lua_State) -> c_int {
|
||||
callback_error_ext(state, ptr::null_mut(), false, move |extra, nargs| {
|
||||
let rawlua = (*extra).raw_lua();
|
||||
let (chunk, chunk_name) =
|
||||
<(String, Option<String>)>::from_stack_args(nargs, 1, Some("loadstring"), rawlua)?;
|
||||
let chunk_name = chunk_name.as_deref().unwrap_or("=(loadstring)");
|
||||
(rawlua.lua())
|
||||
.load(chunk)
|
||||
.set_name(chunk_name)
|
||||
.set_mode(ChunkMode::Text)
|
||||
.into_function()?
|
||||
.push_into_stack(rawlua)?;
|
||||
Ok(1)
|
||||
})
|
||||
}
|
||||
|
||||
mod require;
|
||||
|
||||
+298
-254
@@ -2,97 +2,99 @@ use std::cell::RefCell;
|
||||
use std::collections::VecDeque;
|
||||
use std::ffi::CStr;
|
||||
use std::io::Result as IoResult;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use std::os::raw::{c_char, c_int, c_void};
|
||||
use std::path::{Component, Path, PathBuf};
|
||||
use std::result::Result as StdResult;
|
||||
use std::{env, fmt, fs, mem, ptr};
|
||||
|
||||
use crate::error::Result;
|
||||
use crate::error::{Error, Result};
|
||||
use crate::function::Function;
|
||||
use crate::state::{callback_error_ext, Lua};
|
||||
use crate::table::Table;
|
||||
use crate::types::MaybeSend;
|
||||
|
||||
/// An error that can occur during navigation in the Luau `require` system.
|
||||
/// An error that can occur during navigation in the Luau `require-by-string` system.
|
||||
#[cfg(any(feature = "luau", doc))]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum NavigateError {
|
||||
Ambiguous,
|
||||
NotFound,
|
||||
Other(Error),
|
||||
}
|
||||
|
||||
#[cfg(feature = "luau")]
|
||||
trait IntoNavigateResult {
|
||||
fn into_nav_result(self) -> ffi::luarequire_NavigateResult;
|
||||
fn into_nav_result(self) -> Result<ffi::luarequire_NavigateResult>;
|
||||
}
|
||||
|
||||
#[cfg(feature = "luau")]
|
||||
impl IntoNavigateResult for StdResult<(), NavigateError> {
|
||||
fn into_nav_result(self) -> ffi::luarequire_NavigateResult {
|
||||
fn into_nav_result(self) -> Result<ffi::luarequire_NavigateResult> {
|
||||
match self {
|
||||
Ok(()) => ffi::luarequire_NavigateResult::Success,
|
||||
Err(NavigateError::Ambiguous) => ffi::luarequire_NavigateResult::Ambiguous,
|
||||
Err(NavigateError::NotFound) => ffi::luarequire_NavigateResult::NotFound,
|
||||
Ok(()) => Ok(ffi::luarequire_NavigateResult::Success),
|
||||
Err(NavigateError::Ambiguous) => Ok(ffi::luarequire_NavigateResult::Ambiguous),
|
||||
Err(NavigateError::NotFound) => Ok(ffi::luarequire_NavigateResult::NotFound),
|
||||
Err(NavigateError::Other(err)) => Err(err),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Error> for NavigateError {
|
||||
fn from(err: Error) -> Self {
|
||||
NavigateError::Other(err)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "luau")]
|
||||
type WriteResult = ffi::luarequire_WriteResult;
|
||||
|
||||
/// A trait for handling modules loading and navigation in the Luau `require` system.
|
||||
pub trait Require: MaybeSend {
|
||||
/// A trait for handling modules loading and navigation in the Luau `require-by-string` system.
|
||||
#[cfg(any(feature = "luau", doc))]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
|
||||
pub trait Require {
|
||||
/// Returns `true` if "require" is permitted for the given chunk name.
|
||||
fn is_require_allowed(&self, chunk_name: &str) -> bool;
|
||||
|
||||
/// Resets the internal state to point at the requirer module.
|
||||
fn reset(&self, chunk_name: &str) -> StdResult<(), NavigateError>;
|
||||
fn reset(&mut self, chunk_name: &str) -> StdResult<(), NavigateError>;
|
||||
|
||||
/// Resets the internal state to point at an aliased module.
|
||||
///
|
||||
/// This function received an exact path from a configuration file.
|
||||
/// It's only called when an alias's path cannot be resolved relative to its
|
||||
/// configuration file.
|
||||
fn jump_to_alias(&self, path: &str) -> StdResult<(), NavigateError>;
|
||||
fn jump_to_alias(&mut self, path: &str) -> StdResult<(), NavigateError>;
|
||||
|
||||
// Navigate to parent directory
|
||||
fn to_parent(&self) -> StdResult<(), NavigateError>;
|
||||
fn to_parent(&mut self) -> StdResult<(), NavigateError>;
|
||||
|
||||
/// Navigate to the given child directory.
|
||||
fn to_child(&self, name: &str) -> StdResult<(), NavigateError>;
|
||||
fn to_child(&mut self, name: &str) -> StdResult<(), NavigateError>;
|
||||
|
||||
/// Returns whether the context is currently pointing at a module
|
||||
fn is_module_present(&self) -> bool;
|
||||
|
||||
/// Returns the contents of the current module
|
||||
///
|
||||
/// This function is only called if `is_module_present` returns true.
|
||||
fn contents(&self) -> IoResult<Vec<u8>>;
|
||||
|
||||
/// Returns a chunk name for the current module.
|
||||
///
|
||||
/// This function is only called if `is_module_present` returns true.
|
||||
/// The chunk name is used to identify the module using the debug library.
|
||||
fn chunk_name(&self) -> String;
|
||||
fn has_module(&self) -> bool;
|
||||
|
||||
/// Provides a cache key representing the current module.
|
||||
///
|
||||
/// This function is only called if `is_module_present` returns true.
|
||||
fn cache_key(&self) -> Vec<u8>;
|
||||
/// This function is only called if `has_module` returns true.
|
||||
fn cache_key(&self) -> String;
|
||||
|
||||
/// Returns whether a configuration file is present in the current context.
|
||||
fn is_config_present(&self) -> bool;
|
||||
/// Returns whether a configuration is present in the current context.
|
||||
fn has_config(&self) -> bool;
|
||||
|
||||
/// Returns the contents of the configuration file in the current context.
|
||||
///
|
||||
/// This function is only called if `is_config_present` returns true.
|
||||
/// This function is only called if `has_config` returns true.
|
||||
fn config(&self) -> IoResult<Vec<u8>>;
|
||||
|
||||
/// Returns a loader that when called, loads the module and returns the result.
|
||||
/// Returns a loader function for the current module, that when called, loads the module
|
||||
/// and returns the result.
|
||||
///
|
||||
/// Loader can be sync or async.
|
||||
fn loader(&self, lua: &Lua, path: &str, chunk_name: &str, content: &[u8]) -> Result<Function> {
|
||||
let _ = path;
|
||||
lua.load(content).set_name(chunk_name).into_function()
|
||||
}
|
||||
/// This function is only called if `has_module` returns true.
|
||||
fn loader(&self, lua: &Lua) -> Result<Function>;
|
||||
}
|
||||
|
||||
impl fmt::Debug for dyn Require {
|
||||
@@ -101,21 +103,33 @@ impl fmt::Debug for dyn Require {
|
||||
}
|
||||
}
|
||||
|
||||
/// The standard implementation of Luau `require` navigation.
|
||||
#[derive(Default)]
|
||||
pub(super) struct TextRequirer {
|
||||
abs_path: RefCell<PathBuf>,
|
||||
rel_path: RefCell<PathBuf>,
|
||||
module_path: RefCell<PathBuf>,
|
||||
/// The standard implementation of Luau `require-by-string` navigation.
|
||||
#[derive(Default, Debug)]
|
||||
pub struct TextRequirer {
|
||||
/// An absolute path to the current Luau module (not mapped to a physical file)
|
||||
abs_path: PathBuf,
|
||||
/// A relative path to the current Luau module (not mapped to a physical file)
|
||||
rel_path: PathBuf,
|
||||
/// A physical path to the current Luau module, which is a file or a directory with an
|
||||
/// `init.lua(u)` file
|
||||
resolved_path: Option<PathBuf>,
|
||||
}
|
||||
|
||||
impl TextRequirer {
|
||||
pub(super) fn new() -> Self {
|
||||
/// The prefix used for chunk names in the require system.
|
||||
/// Only chunk names starting with this prefix are allowed to be used in `require`.
|
||||
const CHUNK_PREFIX: &str = "@";
|
||||
|
||||
/// The file extensions that are considered valid for Luau modules.
|
||||
const FILE_EXTENSIONS: &[&str] = &["luau", "lua"];
|
||||
|
||||
/// Creates a new `TextRequirer` instance.
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
fn normalize_chunk_name(chunk_name: &str) -> &str {
|
||||
if let Some((path, line)) = chunk_name.split_once(':') {
|
||||
if let Some((path, line)) = chunk_name.rsplit_once(':') {
|
||||
if line.parse::<u32>().is_ok() {
|
||||
return path;
|
||||
}
|
||||
@@ -152,162 +166,192 @@ impl TextRequirer {
|
||||
components.into_iter().collect()
|
||||
}
|
||||
|
||||
fn find_module_path(path: &Path) -> StdResult<PathBuf, NavigateError> {
|
||||
/// Resolve a Luau module path to a physical file or directory.
|
||||
///
|
||||
/// Empty directories without init files are considered valid as "intermediate" directories.
|
||||
fn resolve_module(path: &Path) -> StdResult<Option<PathBuf>, NavigateError> {
|
||||
let mut found_path = None;
|
||||
|
||||
let current_ext = (path.extension().and_then(|s| s.to_str()))
|
||||
.map(|s| format!("{s}."))
|
||||
.unwrap_or_default();
|
||||
for ext in ["luau", "lua"] {
|
||||
let candidate = path.with_extension(format!("{current_ext}{ext}"));
|
||||
if candidate.is_file() {
|
||||
if found_path.is_some() {
|
||||
if path.components().next_back() != Some(Component::Normal("init".as_ref())) {
|
||||
let current_ext = (path.extension().and_then(|s| s.to_str()))
|
||||
.map(|s| format!("{s}."))
|
||||
.unwrap_or_default();
|
||||
for ext in Self::FILE_EXTENSIONS {
|
||||
let candidate = path.with_extension(format!("{current_ext}{ext}"));
|
||||
if candidate.is_file() && found_path.replace(candidate).is_some() {
|
||||
return Err(NavigateError::Ambiguous);
|
||||
}
|
||||
found_path = Some(candidate);
|
||||
}
|
||||
}
|
||||
if path.is_dir() {
|
||||
if found_path.is_some() {
|
||||
return Err(NavigateError::Ambiguous);
|
||||
}
|
||||
|
||||
for component in ["init.luau", "init.lua"] {
|
||||
for component in Self::FILE_EXTENSIONS.iter().map(|ext| format!("init.{ext}")) {
|
||||
let candidate = path.join(component);
|
||||
if candidate.is_file() {
|
||||
if found_path.is_some() {
|
||||
return Err(NavigateError::Ambiguous);
|
||||
}
|
||||
found_path = Some(candidate);
|
||||
if candidate.is_file() && found_path.replace(candidate).is_some() {
|
||||
return Err(NavigateError::Ambiguous);
|
||||
}
|
||||
}
|
||||
|
||||
if found_path.is_none() {
|
||||
found_path = Some(PathBuf::new());
|
||||
// Directories without init files are considered valid "intermediate" path
|
||||
return Ok(None);
|
||||
}
|
||||
}
|
||||
|
||||
found_path.ok_or(NavigateError::NotFound)
|
||||
Ok(Some(found_path.ok_or(NavigateError::NotFound)?))
|
||||
}
|
||||
}
|
||||
|
||||
impl Require for TextRequirer {
|
||||
fn is_require_allowed(&self, chunk_name: &str) -> bool {
|
||||
chunk_name.starts_with('@')
|
||||
chunk_name.starts_with(Self::CHUNK_PREFIX)
|
||||
}
|
||||
|
||||
fn reset(&self, chunk_name: &str) -> StdResult<(), NavigateError> {
|
||||
if !chunk_name.starts_with('@') {
|
||||
fn reset(&mut self, chunk_name: &str) -> StdResult<(), NavigateError> {
|
||||
if !chunk_name.starts_with(Self::CHUNK_PREFIX) {
|
||||
return Err(NavigateError::NotFound);
|
||||
}
|
||||
let chunk_name = &Self::normalize_chunk_name(chunk_name)[1..];
|
||||
let path = Self::normalize_path(chunk_name.as_ref());
|
||||
let chunk_name = Self::normalize_chunk_name(&chunk_name[1..]);
|
||||
let chunk_path = Self::normalize_path(chunk_name.as_ref());
|
||||
|
||||
if path.extension() == Some("rs".as_ref()) {
|
||||
let cwd = match env::current_dir() {
|
||||
Ok(cwd) => cwd,
|
||||
Err(_) => return Err(NavigateError::NotFound),
|
||||
};
|
||||
self.abs_path.replace(Self::normalize_path(&cwd.join(&path)));
|
||||
self.rel_path.replace(path);
|
||||
self.module_path.replace(PathBuf::new());
|
||||
if chunk_path.extension() == Some("rs".as_ref()) {
|
||||
// Special case for Rust source files, reset to the current directory
|
||||
let chunk_filename = chunk_path.file_name().unwrap();
|
||||
let cwd = env::current_dir().map_err(|_| NavigateError::NotFound)?;
|
||||
self.abs_path = Self::normalize_path(&cwd.join(chunk_filename));
|
||||
self.rel_path = ([Component::CurDir, Component::Normal(chunk_filename)].into_iter()).collect();
|
||||
self.resolved_path = None;
|
||||
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
if path.is_absolute() {
|
||||
let module_path = Self::find_module_path(&path)?;
|
||||
self.abs_path.replace(path.clone());
|
||||
self.rel_path.replace(path);
|
||||
self.module_path.replace(module_path);
|
||||
if chunk_path.is_absolute() {
|
||||
let resolved_path = Self::resolve_module(&chunk_path)?;
|
||||
self.abs_path = chunk_path.clone();
|
||||
self.rel_path = chunk_path;
|
||||
self.resolved_path = resolved_path;
|
||||
} else {
|
||||
// Relative path
|
||||
let cwd = match env::current_dir() {
|
||||
Ok(cwd) => cwd,
|
||||
Err(_) => return Err(NavigateError::NotFound),
|
||||
};
|
||||
let abs_path = cwd.join(&path);
|
||||
let module_path = Self::find_module_path(&abs_path)?;
|
||||
self.abs_path.replace(Self::normalize_path(&abs_path));
|
||||
self.rel_path.replace(path);
|
||||
self.module_path.replace(module_path);
|
||||
let cwd = env::current_dir().map_err(|_| NavigateError::NotFound)?;
|
||||
let abs_path = Self::normalize_path(&cwd.join(&chunk_path));
|
||||
let resolved_path = Self::resolve_module(&abs_path)?;
|
||||
self.abs_path = abs_path;
|
||||
self.rel_path = chunk_path;
|
||||
self.resolved_path = resolved_path;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn jump_to_alias(&self, path: &str) -> StdResult<(), NavigateError> {
|
||||
fn jump_to_alias(&mut self, path: &str) -> StdResult<(), NavigateError> {
|
||||
let path = Self::normalize_path(path.as_ref());
|
||||
let module_path = Self::find_module_path(&path)?;
|
||||
let resolved_path = Self::resolve_module(&path)?;
|
||||
|
||||
self.abs_path.replace(path.clone());
|
||||
self.rel_path.replace(path);
|
||||
self.module_path.replace(module_path);
|
||||
self.abs_path = path.clone();
|
||||
self.rel_path = path;
|
||||
self.resolved_path = resolved_path;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn to_parent(&self) -> StdResult<(), NavigateError> {
|
||||
let mut abs_path = self.abs_path.borrow().clone();
|
||||
fn to_parent(&mut self) -> StdResult<(), NavigateError> {
|
||||
let mut abs_path = self.abs_path.clone();
|
||||
if !abs_path.pop() {
|
||||
// It's important to return `NotFound` if we reached the root, as it's a "recoverable" error if we
|
||||
// cannot go beyond the root directory.
|
||||
// Luau "require-by-string` has a special logic to search for config file to resolve aliases.
|
||||
return Err(NavigateError::NotFound);
|
||||
}
|
||||
let mut rel_parent = self.rel_path.borrow().clone();
|
||||
let mut rel_parent = self.rel_path.clone();
|
||||
rel_parent.pop();
|
||||
let module_path = Self::find_module_path(&abs_path)?;
|
||||
let resolved_path = Self::resolve_module(&abs_path)?;
|
||||
|
||||
self.abs_path.replace(abs_path);
|
||||
self.rel_path.replace(Self::normalize_path(&rel_parent));
|
||||
self.module_path.replace(module_path);
|
||||
self.abs_path = abs_path;
|
||||
self.rel_path = Self::normalize_path(&rel_parent);
|
||||
self.resolved_path = resolved_path;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn to_child(&self, name: &str) -> StdResult<(), NavigateError> {
|
||||
let abs_path = self.abs_path.borrow().join(name);
|
||||
let rel_path = self.rel_path.borrow().join(name);
|
||||
let module_path = Self::find_module_path(&abs_path)?;
|
||||
fn to_child(&mut self, name: &str) -> StdResult<(), NavigateError> {
|
||||
let abs_path = self.abs_path.join(name);
|
||||
let rel_path = self.rel_path.join(name);
|
||||
let resolved_path = Self::resolve_module(&abs_path)?;
|
||||
|
||||
self.abs_path.replace(abs_path);
|
||||
self.rel_path.replace(rel_path);
|
||||
self.module_path.replace(module_path);
|
||||
self.abs_path = abs_path;
|
||||
self.rel_path = rel_path;
|
||||
self.resolved_path = resolved_path;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn is_module_present(&self) -> bool {
|
||||
self.module_path.borrow().is_file()
|
||||
fn has_module(&self) -> bool {
|
||||
(self.resolved_path.as_deref())
|
||||
.map(Path::is_file)
|
||||
.unwrap_or(false)
|
||||
}
|
||||
|
||||
fn contents(&self) -> IoResult<Vec<u8>> {
|
||||
fs::read(&*self.module_path.borrow())
|
||||
fn cache_key(&self) -> String {
|
||||
self.resolved_path.as_deref().unwrap().display().to_string()
|
||||
}
|
||||
|
||||
fn chunk_name(&self) -> String {
|
||||
format!("@{}", self.rel_path.borrow().display())
|
||||
}
|
||||
|
||||
fn cache_key(&self) -> Vec<u8> {
|
||||
self.module_path.borrow().display().to_string().into_bytes()
|
||||
}
|
||||
|
||||
fn is_config_present(&self) -> bool {
|
||||
self.abs_path.borrow().join(".luaurc").is_file()
|
||||
fn has_config(&self) -> bool {
|
||||
self.abs_path.is_dir() && self.abs_path.join(".luaurc").is_file()
|
||||
}
|
||||
|
||||
fn config(&self) -> IoResult<Vec<u8>> {
|
||||
fs::read(self.abs_path.borrow().join(".luaurc"))
|
||||
fs::read(self.abs_path.join(".luaurc"))
|
||||
}
|
||||
|
||||
fn loader(&self, lua: &Lua) -> Result<Function> {
|
||||
let name = format!("@{}", self.rel_path.display());
|
||||
lua.load(self.resolved_path.as_deref().unwrap())
|
||||
.set_name(name)
|
||||
.into_function()
|
||||
}
|
||||
}
|
||||
|
||||
struct Context(Box<dyn Require>);
|
||||
|
||||
impl Deref for Context {
|
||||
type Target = dyn Require;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&*self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl DerefMut for Context {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut *self.0
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! try_borrow {
|
||||
($state:expr, $ctx:expr) => {
|
||||
match (*($ctx as *const RefCell<Context>)).try_borrow() {
|
||||
Ok(ctx) => ctx,
|
||||
Err(_) => ffi::luaL_error($state, cstr!("require context is already borrowed")),
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! try_borrow_mut {
|
||||
($state:expr, $ctx:expr) => {
|
||||
match (*($ctx as *const RefCell<Context>)).try_borrow_mut() {
|
||||
Ok(ctx) => ctx,
|
||||
Err(_) => ffi::luaL_error($state, cstr!("require context is already borrowed")),
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(feature = "luau")]
|
||||
pub(super) unsafe extern "C" fn init_config(config: *mut ffi::luarequire_Configuration) {
|
||||
pub(super) unsafe extern "C-unwind" fn init_config(config: *mut ffi::luarequire_Configuration) {
|
||||
if config.is_null() {
|
||||
return;
|
||||
}
|
||||
|
||||
unsafe extern "C" fn is_require_allowed(
|
||||
_state: *mut ffi::lua_State,
|
||||
unsafe extern "C-unwind" fn is_require_allowed(
|
||||
state: *mut ffi::lua_State,
|
||||
ctx: *mut c_void,
|
||||
requirer_chunkname: *const c_char,
|
||||
) -> bool {
|
||||
@@ -315,119 +359,123 @@ pub(super) unsafe extern "C" fn init_config(config: *mut ffi::luarequire_Configu
|
||||
return false;
|
||||
}
|
||||
|
||||
let this = &*(ctx as *const Box<dyn Require>);
|
||||
let this = try_borrow!(state, ctx);
|
||||
let chunk_name = CStr::from_ptr(requirer_chunkname).to_string_lossy();
|
||||
this.is_require_allowed(&chunk_name)
|
||||
}
|
||||
|
||||
unsafe extern "C" fn reset(
|
||||
_state: *mut ffi::lua_State,
|
||||
unsafe extern "C-unwind" fn reset(
|
||||
state: *mut ffi::lua_State,
|
||||
ctx: *mut c_void,
|
||||
requirer_chunkname: *const c_char,
|
||||
) -> ffi::luarequire_NavigateResult {
|
||||
let this = &*(ctx as *const Box<dyn Require>);
|
||||
let mut this = try_borrow_mut!(state, ctx);
|
||||
let chunk_name = CStr::from_ptr(requirer_chunkname).to_string_lossy();
|
||||
this.reset(&chunk_name).into_nav_result()
|
||||
}
|
||||
|
||||
unsafe extern "C" fn jump_to_alias(
|
||||
_state: *mut ffi::lua_State,
|
||||
ctx: *mut c_void,
|
||||
path: *const c_char,
|
||||
) -> ffi::luarequire_NavigateResult {
|
||||
let this = &*(ctx as *const Box<dyn Require>);
|
||||
let path = CStr::from_ptr(path).to_string_lossy();
|
||||
this.jump_to_alias(&path).into_nav_result()
|
||||
}
|
||||
|
||||
unsafe extern "C" fn to_parent(
|
||||
_state: *mut ffi::lua_State,
|
||||
ctx: *mut c_void,
|
||||
) -> ffi::luarequire_NavigateResult {
|
||||
let this = &*(ctx as *const Box<dyn Require>);
|
||||
this.to_parent().into_nav_result()
|
||||
}
|
||||
|
||||
unsafe extern "C" fn to_child(
|
||||
_state: *mut ffi::lua_State,
|
||||
ctx: *mut c_void,
|
||||
name: *const c_char,
|
||||
) -> ffi::luarequire_NavigateResult {
|
||||
let this = &*(ctx as *const Box<dyn Require>);
|
||||
let name = CStr::from_ptr(name).to_string_lossy();
|
||||
this.to_child(&name).into_nav_result()
|
||||
}
|
||||
|
||||
unsafe extern "C" fn is_module_present(_state: *mut ffi::lua_State, ctx: *mut c_void) -> bool {
|
||||
let this = &*(ctx as *const Box<dyn Require>);
|
||||
this.is_module_present()
|
||||
}
|
||||
|
||||
unsafe extern "C" fn get_contents(
|
||||
state: *mut ffi::lua_State,
|
||||
ctx: *mut c_void,
|
||||
buffer: *mut c_char,
|
||||
buffer_size: usize,
|
||||
size_out: *mut usize,
|
||||
) -> WriteResult {
|
||||
let this = &*(ctx as *const Box<dyn Require>);
|
||||
write_to_buffer(state, buffer, buffer_size, size_out, || this.contents())
|
||||
}
|
||||
|
||||
unsafe extern "C" fn get_chunkname(
|
||||
state: *mut ffi::lua_State,
|
||||
ctx: *mut c_void,
|
||||
buffer: *mut c_char,
|
||||
buffer_size: usize,
|
||||
size_out: *mut usize,
|
||||
) -> WriteResult {
|
||||
let this = &*(ctx as *const Box<dyn Require>);
|
||||
write_to_buffer(state, buffer, buffer_size, size_out, || {
|
||||
Ok(this.chunk_name().into_bytes())
|
||||
callback_error_ext(state, ptr::null_mut(), true, move |_, _| {
|
||||
this.reset(&chunk_name).into_nav_result()
|
||||
})
|
||||
}
|
||||
|
||||
unsafe extern "C" fn get_cache_key(
|
||||
unsafe extern "C-unwind" fn jump_to_alias(
|
||||
state: *mut ffi::lua_State,
|
||||
ctx: *mut c_void,
|
||||
path: *const c_char,
|
||||
) -> ffi::luarequire_NavigateResult {
|
||||
let mut this = try_borrow_mut!(state, ctx);
|
||||
let path = CStr::from_ptr(path).to_string_lossy();
|
||||
callback_error_ext(state, ptr::null_mut(), true, move |_, _| {
|
||||
this.jump_to_alias(&path).into_nav_result()
|
||||
})
|
||||
}
|
||||
|
||||
unsafe extern "C-unwind" fn to_parent(
|
||||
state: *mut ffi::lua_State,
|
||||
ctx: *mut c_void,
|
||||
) -> ffi::luarequire_NavigateResult {
|
||||
let mut this = try_borrow_mut!(state, ctx);
|
||||
callback_error_ext(state, ptr::null_mut(), true, move |_, _| {
|
||||
this.to_parent().into_nav_result()
|
||||
})
|
||||
}
|
||||
|
||||
unsafe extern "C-unwind" fn to_child(
|
||||
state: *mut ffi::lua_State,
|
||||
ctx: *mut c_void,
|
||||
name: *const c_char,
|
||||
) -> ffi::luarequire_NavigateResult {
|
||||
let mut this = try_borrow_mut!(state, ctx);
|
||||
let name = CStr::from_ptr(name).to_string_lossy();
|
||||
callback_error_ext(state, ptr::null_mut(), true, move |_, _| {
|
||||
this.to_child(&name).into_nav_result()
|
||||
})
|
||||
}
|
||||
|
||||
unsafe extern "C-unwind" fn is_module_present(state: *mut ffi::lua_State, ctx: *mut c_void) -> bool {
|
||||
let this = try_borrow!(state, ctx);
|
||||
this.has_module()
|
||||
}
|
||||
|
||||
unsafe extern "C-unwind" fn get_chunkname(
|
||||
_state: *mut ffi::lua_State,
|
||||
_ctx: *mut c_void,
|
||||
buffer: *mut c_char,
|
||||
buffer_size: usize,
|
||||
size_out: *mut usize,
|
||||
) -> WriteResult {
|
||||
write_to_buffer(buffer, buffer_size, size_out, &[])
|
||||
}
|
||||
|
||||
unsafe extern "C-unwind" fn get_loadname(
|
||||
_state: *mut ffi::lua_State,
|
||||
_ctx: *mut c_void,
|
||||
buffer: *mut c_char,
|
||||
buffer_size: usize,
|
||||
size_out: *mut usize,
|
||||
) -> WriteResult {
|
||||
write_to_buffer(buffer, buffer_size, size_out, &[])
|
||||
}
|
||||
|
||||
unsafe extern "C-unwind" fn get_cache_key(
|
||||
state: *mut ffi::lua_State,
|
||||
ctx: *mut c_void,
|
||||
buffer: *mut c_char,
|
||||
buffer_size: usize,
|
||||
size_out: *mut usize,
|
||||
) -> WriteResult {
|
||||
let this = &*(ctx as *const Box<dyn Require>);
|
||||
write_to_buffer(state, buffer, buffer_size, size_out, || Ok(this.cache_key()))
|
||||
let this = try_borrow!(state, ctx);
|
||||
let cache_key = this.cache_key();
|
||||
write_to_buffer(buffer, buffer_size, size_out, cache_key.as_bytes())
|
||||
}
|
||||
|
||||
unsafe extern "C" fn is_config_present(_state: *mut ffi::lua_State, ctx: *mut c_void) -> bool {
|
||||
let this = &*(ctx as *const Box<dyn Require>);
|
||||
this.is_config_present()
|
||||
unsafe extern "C-unwind" fn is_config_present(state: *mut ffi::lua_State, ctx: *mut c_void) -> bool {
|
||||
let this = try_borrow!(state, ctx);
|
||||
this.has_config()
|
||||
}
|
||||
|
||||
unsafe extern "C" fn get_config(
|
||||
unsafe extern "C-unwind" fn get_config(
|
||||
state: *mut ffi::lua_State,
|
||||
ctx: *mut c_void,
|
||||
buffer: *mut c_char,
|
||||
buffer_size: usize,
|
||||
size_out: *mut usize,
|
||||
) -> WriteResult {
|
||||
let this = &*(ctx as *const Box<dyn Require>);
|
||||
write_to_buffer(state, buffer, buffer_size, size_out, || this.config())
|
||||
let this = try_borrow!(state, ctx);
|
||||
let config = callback_error_ext(state, ptr::null_mut(), true, move |_, _| Ok(this.config()?));
|
||||
write_to_buffer(buffer, buffer_size, size_out, &config)
|
||||
}
|
||||
|
||||
unsafe extern "C-unwind" fn load(
|
||||
state: *mut ffi::lua_State,
|
||||
ctx: *mut c_void,
|
||||
path: *const c_char,
|
||||
chunk_name: *const c_char,
|
||||
contents: *const c_char,
|
||||
_path: *const c_char,
|
||||
_chunkname: *const c_char,
|
||||
_loadname: *const c_char,
|
||||
) -> c_int {
|
||||
let this = &*(ctx as *const Box<dyn Require>);
|
||||
let path = CStr::from_ptr(path).to_string_lossy();
|
||||
let chunk_name = CStr::from_ptr(chunk_name).to_string_lossy();
|
||||
let contents = CStr::from_ptr(contents).to_bytes();
|
||||
callback_error_ext(state, ptr::null_mut(), false, move |extra, _| {
|
||||
let this = try_borrow!(state, ctx);
|
||||
callback_error_ext(state, ptr::null_mut(), true, move |extra, _| {
|
||||
let rawlua = (*extra).raw_lua();
|
||||
rawlua.push(this.loader(rawlua.lua(), &path, &chunk_name, contents)?)?;
|
||||
let loader = this.loader(rawlua.lua())?;
|
||||
rawlua.push(loader)?;
|
||||
Ok(1)
|
||||
})
|
||||
}
|
||||
@@ -438,64 +486,41 @@ pub(super) unsafe extern "C" fn init_config(config: *mut ffi::luarequire_Configu
|
||||
(*config).to_parent = to_parent;
|
||||
(*config).to_child = to_child;
|
||||
(*config).is_module_present = is_module_present;
|
||||
(*config).get_contents = get_contents;
|
||||
(*config).get_chunkname = get_chunkname;
|
||||
(*config).get_loadname = get_loadname;
|
||||
(*config).get_cache_key = get_cache_key;
|
||||
(*config).is_config_present = is_config_present;
|
||||
(*config).get_config = get_config;
|
||||
(*config).get_alias = None;
|
||||
(*config).get_config = Some(get_config);
|
||||
(*config).load = load;
|
||||
}
|
||||
|
||||
/// Helper function to write data to a buffer
|
||||
#[cfg(feature = "luau")]
|
||||
unsafe fn write_to_buffer(
|
||||
state: *mut ffi::lua_State,
|
||||
buffer: *mut c_char,
|
||||
buffer_size: usize,
|
||||
size_out: *mut usize,
|
||||
data_fetcher: impl Fn() -> IoResult<Vec<u8>>,
|
||||
data: &[u8],
|
||||
) -> WriteResult {
|
||||
struct DataCache(Option<Vec<u8>>);
|
||||
|
||||
// The initial buffer size can be too small, to avoid making a second data fetch call,
|
||||
// we cache the content in the first call, and then re-use it.
|
||||
|
||||
let lua = Lua::get_or_init_from_ptr(state);
|
||||
match lua.try_app_data_mut::<DataCache>() {
|
||||
Ok(Some(mut data_cache)) => {
|
||||
if let Some(data) = data_cache.0.take() {
|
||||
mlua_assert!(data.len() <= buffer_size, "buffer is too small");
|
||||
*size_out = data.len();
|
||||
ptr::copy_nonoverlapping(data.as_ptr(), buffer as *mut _, data.len());
|
||||
return WriteResult::Success;
|
||||
}
|
||||
}
|
||||
Ok(None) => {
|
||||
// Init the cache
|
||||
_ = lua.try_set_app_data(DataCache(None));
|
||||
}
|
||||
Err(_) => {}
|
||||
// the buffer must be null terminated as it's a c++ `std::string` data() buffer
|
||||
let is_null_terminated = data.last() == Some(&0);
|
||||
*size_out = data.len() + if is_null_terminated { 0 } else { 1 };
|
||||
if *size_out > buffer_size {
|
||||
return WriteResult::BufferTooSmall;
|
||||
}
|
||||
|
||||
match data_fetcher() {
|
||||
Ok(data) => {
|
||||
*size_out = data.len();
|
||||
if *size_out > buffer_size {
|
||||
// Cache the data for the next call to avoid getting the contents again
|
||||
if let Ok(Some(mut data_cache)) = lua.try_app_data_mut::<DataCache>() {
|
||||
data_cache.0 = Some(data);
|
||||
}
|
||||
return WriteResult::BufferTooSmall;
|
||||
}
|
||||
ptr::copy_nonoverlapping(data.as_ptr(), buffer as *mut _, data.len());
|
||||
WriteResult::Success
|
||||
}
|
||||
Err(_) => WriteResult::Failure,
|
||||
ptr::copy_nonoverlapping(data.as_ptr(), buffer as *mut _, data.len());
|
||||
if !is_null_terminated {
|
||||
*buffer.add(data.len()) = 0;
|
||||
}
|
||||
WriteResult::Success
|
||||
}
|
||||
|
||||
#[cfg(feature = "luau")]
|
||||
pub fn create_require_function<R: Require + 'static>(lua: &Lua, require: R) -> Result<Function> {
|
||||
pub(super) fn create_require_function<R: Require + MaybeSend + 'static>(
|
||||
lua: &Lua,
|
||||
require: R,
|
||||
) -> Result<Function> {
|
||||
unsafe extern "C-unwind" fn find_current_file(state: *mut ffi::lua_State) -> c_int {
|
||||
let mut ar: ffi::lua_Debug = mem::zeroed();
|
||||
for level in 2.. {
|
||||
@@ -511,18 +536,20 @@ pub fn create_require_function<R: Require + 'static>(lua: &Lua, require: R) -> R
|
||||
}
|
||||
|
||||
unsafe extern "C-unwind" fn get_cache_key(state: *mut ffi::lua_State) -> c_int {
|
||||
let requirer = ffi::lua_touserdata(state, ffi::lua_upvalueindex(1)) as *const Box<dyn Require>;
|
||||
let cache_key = (*requirer).cache_key();
|
||||
let ctx = ffi::lua_touserdata(state, ffi::lua_upvalueindex(1));
|
||||
let ctx = try_borrow!(state, ctx);
|
||||
let cache_key = ctx.cache_key();
|
||||
ffi::lua_pushlstring(state, cache_key.as_ptr() as *const _, cache_key.len());
|
||||
1
|
||||
}
|
||||
|
||||
let (get_cache_key, find_current_file, proxyrequire, registered_modules, loader_cache) = unsafe {
|
||||
lua.exec_raw::<(Function, Function, Function, Table, Table)>((), move |state| {
|
||||
let requirer_ptr = ffi::lua_newuserdata_t::<Box<dyn Require>>(state, Box::new(require));
|
||||
let context = Context(Box::new(require));
|
||||
let context_ptr = ffi::lua_newuserdata_t(state, RefCell::new(context));
|
||||
ffi::lua_pushcclosured(state, get_cache_key, cstr!("get_cache_key"), 1);
|
||||
ffi::lua_pushcfunctiond(state, find_current_file, cstr!("find_current_file"));
|
||||
ffi::luarequire_pushproxyrequire(state, init_config, requirer_ptr as *mut _);
|
||||
ffi::luarequire_pushproxyrequire(state, init_config, context_ptr as *mut _);
|
||||
ffi::luaL_getsubtable(state, ffi::LUA_REGISTRYINDEX, ffi::LUA_REGISTERED_MODULES_TABLE);
|
||||
ffi::luaL_getsubtable(state, ffi::LUA_REGISTRYINDEX, cstr!("__MLUA_LOADER_CACHE"));
|
||||
})
|
||||
@@ -540,10 +567,26 @@ pub fn create_require_function<R: Require + 'static>(lua: &Lua, require: R) -> R
|
||||
1
|
||||
}
|
||||
|
||||
let (error, r#type) = unsafe {
|
||||
lua.exec_raw::<(Function, Function)>((), move |state| {
|
||||
unsafe extern "C-unwind" fn to_lowercase(state: *mut ffi::lua_State) -> c_int {
|
||||
let s = ffi::luaL_checkstring(state, 1);
|
||||
let s = CStr::from_ptr(s);
|
||||
if !s.to_bytes().iter().any(|&c| c.is_ascii_uppercase()) {
|
||||
// If the string does not contain any uppercase ASCII letters, return it as is
|
||||
return 1;
|
||||
}
|
||||
callback_error_ext(state, ptr::null_mut(), true, |extra, _| {
|
||||
let s = (s.to_bytes().iter())
|
||||
.map(|&c| c.to_ascii_lowercase())
|
||||
.collect::<bstr::BString>();
|
||||
(*extra).raw_lua().push(s).map(|_| 1)
|
||||
})
|
||||
}
|
||||
|
||||
let (error, r#type, to_lowercase) = unsafe {
|
||||
lua.exec_raw::<(Function, Function, Function)>((), move |state| {
|
||||
ffi::lua_pushcfunctiond(state, error, cstr!("error"));
|
||||
ffi::lua_pushcfunctiond(state, r#type, cstr!("type"));
|
||||
ffi::lua_pushcfunctiond(state, to_lowercase, cstr!("to_lowercase"));
|
||||
})
|
||||
}?;
|
||||
|
||||
@@ -556,6 +599,7 @@ pub fn create_require_function<R: Require + 'static>(lua: &Lua, require: R) -> R
|
||||
env.raw_set("LOADER_CACHE", loader_cache)?;
|
||||
env.raw_set("error", error)?;
|
||||
env.raw_set("type", r#type)?;
|
||||
env.raw_set("to_lowercase", to_lowercase)?;
|
||||
|
||||
lua.load(
|
||||
r#"
|
||||
@@ -565,7 +609,7 @@ pub fn create_require_function<R: Require + 'static>(lua: &Lua, require: R) -> R
|
||||
end
|
||||
|
||||
-- Check if the module (path) is explicitly registered
|
||||
local maybe_result = REGISTERED_MODULES[path]
|
||||
local maybe_result = REGISTERED_MODULES[to_lowercase(path)]
|
||||
if maybe_result ~= nil then
|
||||
return maybe_result
|
||||
end
|
||||
|
||||
@@ -297,6 +297,15 @@ impl<T: IntoLua> IntoLuaMulti for Variadic<T> {
|
||||
fn into_lua_multi(self, lua: &Lua) -> Result<MultiValue> {
|
||||
MultiValue::from_lua_iter(lua, self)
|
||||
}
|
||||
|
||||
unsafe fn push_into_stack_multi(self, lua: &RawLua) -> Result<c_int> {
|
||||
let nresults = self.len() as i32;
|
||||
check_stack(lua.state(), nresults + 1)?;
|
||||
for value in self.0 {
|
||||
value.push_into_stack(lua)?;
|
||||
}
|
||||
Ok(nresults)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: FromLua> FromLuaMulti for Variadic<T> {
|
||||
|
||||
+13
-11
@@ -2,15 +2,16 @@
|
||||
|
||||
#[doc(no_inline)]
|
||||
pub use crate::{
|
||||
AnyUserData as LuaAnyUserData, Chunk as LuaChunk, Either as LuaEither, Error as LuaError,
|
||||
ErrorContext as LuaErrorContext, ExternalError as LuaExternalError, ExternalResult as LuaExternalResult,
|
||||
FromLua, FromLuaMulti, Function as LuaFunction, FunctionInfo as LuaFunctionInfo, GCMode as LuaGCMode,
|
||||
Integer as LuaInteger, IntoLua, IntoLuaMulti, LightUserData as LuaLightUserData, Lua, LuaNativeFn,
|
||||
LuaNativeFnMut, LuaOptions, MetaMethod as LuaMetaMethod, MultiValue as LuaMultiValue, Nil as LuaNil,
|
||||
Number as LuaNumber, ObjectLike as LuaObjectLike, RegistryKey as LuaRegistryKey, Result as LuaResult,
|
||||
StdLib as LuaStdLib, String as LuaString, Table as LuaTable, TablePairs as LuaTablePairs,
|
||||
TableSequence as LuaTableSequence, Thread as LuaThread, ThreadStatus as LuaThreadStatus,
|
||||
UserData as LuaUserData, UserDataFields as LuaUserDataFields, UserDataMetatable as LuaUserDataMetatable,
|
||||
AnyUserData as LuaAnyUserData, BorrowedBytes as LuaBorrowedBytes, BorrowedStr as LuaBorrowedStr,
|
||||
Chunk as LuaChunk, Either as LuaEither, Error as LuaError, ErrorContext as LuaErrorContext,
|
||||
ExternalError as LuaExternalError, ExternalResult as LuaExternalResult, FromLua, FromLuaMulti,
|
||||
Function as LuaFunction, FunctionInfo as LuaFunctionInfo, GCMode as LuaGCMode, Integer as LuaInteger,
|
||||
IntoLua, IntoLuaMulti, LightUserData as LuaLightUserData, Lua, LuaNativeFn, LuaNativeFnMut, LuaOptions,
|
||||
MetaMethod as LuaMetaMethod, MultiValue as LuaMultiValue, Nil as LuaNil, Number as LuaNumber,
|
||||
ObjectLike as LuaObjectLike, RegistryKey as LuaRegistryKey, Result as LuaResult, StdLib as LuaStdLib,
|
||||
String as LuaString, Table as LuaTable, TablePairs as LuaTablePairs, TableSequence as LuaTableSequence,
|
||||
Thread as LuaThread, ThreadStatus as LuaThreadStatus, UserData as LuaUserData,
|
||||
UserDataFields as LuaUserDataFields, UserDataMetatable as LuaUserDataMetatable,
|
||||
UserDataMethods as LuaUserDataMethods, UserDataRef as LuaUserDataRef,
|
||||
UserDataRefMut as LuaUserDataRefMut, UserDataRegistry as LuaUserDataRegistry, Value as LuaValue,
|
||||
Variadic as LuaVariadic, VmState as LuaVmState, WeakLua,
|
||||
@@ -24,14 +25,15 @@ pub use crate::HookTriggers as LuaHookTriggers;
|
||||
#[doc(no_inline)]
|
||||
pub use crate::{
|
||||
CompileConstant as LuaCompileConstant, CoverageInfo as LuaCoverageInfo,
|
||||
NavigateError as LuaNavigateError, Require as LuaRequire, Vector as LuaVector,
|
||||
NavigateError as LuaNavigateError, Require as LuaRequire, TextRequirer as LuaTextRequirer,
|
||||
Vector as LuaVector,
|
||||
};
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
#[doc(no_inline)]
|
||||
pub use crate::{AsyncThread as LuaAsyncThread, LuaNativeAsyncFn};
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
#[cfg(feature = "serde")]
|
||||
#[doc(no_inline)]
|
||||
pub use crate::{
|
||||
DeserializeOptions as LuaDeserializeOptions, LuaSerdeExt, SerializeOptions as LuaSerializeOptions,
|
||||
|
||||
+4
-10
@@ -8,9 +8,7 @@ use crate::state::{Lua, LuaGuard, RawLua};
|
||||
use crate::traits::{FromLuaMulti, IntoLuaMulti};
|
||||
use crate::types::{Callback, CallbackUpvalue, ScopedCallback, ValueRef};
|
||||
use crate::userdata::{AnyUserData, UserData, UserDataRegistry, UserDataStorage};
|
||||
use crate::util::{
|
||||
self, assert_stack, check_stack, get_metatable_ptr, get_userdata, take_userdata, StackGuard,
|
||||
};
|
||||
use crate::util::{self, check_stack, get_metatable_ptr, get_userdata, take_userdata, StackGuard};
|
||||
|
||||
/// Constructed by the [`Lua::scope`] method, allows temporarily creating Lua userdata and
|
||||
/// callbacks that are not required to be `Send` or `'static`.
|
||||
@@ -284,22 +282,18 @@ impl<'scope, 'env: 'scope> Scope<'scope, 'env> {
|
||||
/// Shortens the lifetime of the userdata to the lifetime of the scope.
|
||||
fn seal_userdata<T: 'env>(&self, ud: &AnyUserData) {
|
||||
let destructor: DestructorCallback = Box::new(|rawlua, vref| unsafe {
|
||||
let state = rawlua.state();
|
||||
let _sg = StackGuard::new(state);
|
||||
assert_stack(state, 2);
|
||||
|
||||
// Ensure that userdata is not destructed
|
||||
match rawlua.push_userdata_ref(&vref) {
|
||||
match rawlua.get_userdata_ref_type_id(&vref) {
|
||||
Ok(Some(_)) => {}
|
||||
Ok(None) => {
|
||||
// Deregister metatable
|
||||
let mt_ptr = get_metatable_ptr(state, -1);
|
||||
let mt_ptr = get_metatable_ptr(rawlua.ref_thread(), vref.index);
|
||||
rawlua.deregister_userdata_metatable(mt_ptr);
|
||||
}
|
||||
Err(_) => return vec![],
|
||||
}
|
||||
|
||||
let data = take_userdata::<UserDataStorage<T>>(state);
|
||||
let data = take_userdata::<UserDataStorage<T>>(rawlua.ref_thread(), vref.index);
|
||||
vec![Box::new(move || drop(data))]
|
||||
});
|
||||
self.destructors.0.borrow_mut().push((ud.0.clone(), destructor));
|
||||
|
||||
+4
-1
@@ -165,7 +165,10 @@ impl<'de> serde::Deserializer<'de> for Deserializer {
|
||||
serde_userdata(ud, |value| value.deserialize_any(visitor))
|
||||
}
|
||||
#[cfg(feature = "luau")]
|
||||
Value::Buffer(buf) => visitor.visit_bytes(unsafe { buf.as_slice() }),
|
||||
Value::Buffer(buf) => {
|
||||
let lua = buf.0.lua.lock();
|
||||
visitor.visit_bytes(buf.as_slice(&lua))
|
||||
}
|
||||
Value::Function(_)
|
||||
| Value::Thread(_)
|
||||
| Value::UserData(_)
|
||||
|
||||
+1
-1
@@ -13,7 +13,7 @@ use crate::util::check_stack;
|
||||
use crate::value::Value;
|
||||
|
||||
/// Trait for serializing/deserializing Lua values using Serde.
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "serialize")))]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "serde")))]
|
||||
pub trait LuaSerdeExt: Sealed {
|
||||
/// A special value (lightuserdata) to encode/decode optional (none) values.
|
||||
///
|
||||
|
||||
+3
-3
@@ -256,7 +256,7 @@ impl<'a> ser::Serializer for Serializer<'a> {
|
||||
fn serialize_seq(self, len: Option<usize>) -> Result<Self::SerializeSeq> {
|
||||
let table = self.lua.create_table_with_capacity(len.unwrap_or(0), 0)?;
|
||||
if self.options.set_array_metatable {
|
||||
table.set_metatable(Some(self.lua.array_metatable()));
|
||||
table.set_metatable(Some(self.lua.array_metatable()))?;
|
||||
}
|
||||
Ok(SerializeSeq::new(self.lua, table, self.options))
|
||||
}
|
||||
@@ -529,8 +529,8 @@ impl ser::SerializeStruct for SerializeStruct<'_> {
|
||||
fn end(self) -> Result<Value> {
|
||||
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");
|
||||
Some(value @ Value::String(_)) if self.options.detect_serde_json_arbitrary_precision => {
|
||||
let number_s = value.to_string()?;
|
||||
if number_s.contains(['.', 'e', 'E']) {
|
||||
if let Ok(number) = number_s.parse().map(Value::Number) {
|
||||
return Ok(number);
|
||||
|
||||
+213
-73
@@ -8,9 +8,9 @@ use std::result::Result as StdResult;
|
||||
use std::{fmt, mem, ptr};
|
||||
|
||||
use crate::chunk::{AsChunk, Chunk};
|
||||
use crate::debug::Debug;
|
||||
use crate::error::{Error, Result};
|
||||
use crate::function::Function;
|
||||
use crate::hook::Debug;
|
||||
use crate::memory::MemoryState;
|
||||
use crate::multi::MultiValue;
|
||||
use crate::scope::Scope;
|
||||
@@ -24,13 +24,11 @@ use crate::types::{
|
||||
ReentrantMutexGuard, RegistryKey, VmState, XRc, XWeak,
|
||||
};
|
||||
use crate::userdata::{AnyUserData, UserData, UserDataProxy, UserDataRegistry, UserDataStorage};
|
||||
use crate::util::{
|
||||
assert_stack, check_stack, protect_lua_closure, push_string, push_table, rawset_field, StackGuard,
|
||||
};
|
||||
use crate::util::{assert_stack, check_stack, protect_lua_closure, push_string, rawset_field, StackGuard};
|
||||
use crate::value::{Nil, Value};
|
||||
|
||||
#[cfg(not(feature = "luau"))]
|
||||
use crate::{hook::HookTriggers, types::HookKind};
|
||||
use crate::{debug::HookTriggers, types::HookKind};
|
||||
|
||||
#[cfg(any(feature = "luau", doc))]
|
||||
use crate::{buffer::Buffer, chunk::Compiler};
|
||||
@@ -39,9 +37,10 @@ use crate::{buffer::Buffer, chunk::Compiler};
|
||||
use {
|
||||
crate::types::LightUserData,
|
||||
std::future::{self, Future},
|
||||
std::task::Poll,
|
||||
};
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
#[cfg(feature = "serde")]
|
||||
use serde::Serialize;
|
||||
|
||||
pub(crate) use extra::ExtraData;
|
||||
@@ -360,6 +359,8 @@ impl Lua {
|
||||
if cfg!(feature = "luau") && !modname.starts_with('@') {
|
||||
return Err(Error::runtime("module name must begin with '@'"));
|
||||
}
|
||||
#[cfg(feature = "luau")]
|
||||
let modname = modname.to_ascii_lowercase();
|
||||
unsafe {
|
||||
self.exec_raw::<()>(value, |state| {
|
||||
ffi::luaL_getsubtable(state, ffi::LUA_REGISTRYINDEX, LOADED_MODULES_KEY);
|
||||
@@ -377,7 +378,7 @@ impl Lua {
|
||||
///
|
||||
/// This is similar to setting the [`package.preload[modname]`] field.
|
||||
///
|
||||
/// [`package.preload[modname]`]: https://www.lua.org/manual/5.4/manual.html#pdf-package.preload
|
||||
/// [`package.preload[modname]`]: <https://www.lua.org/manual/5.4/manual.html#pdf-package.preload>
|
||||
#[cfg(not(feature = "luau"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(not(feature = "luau"))))]
|
||||
pub fn preload_module(&self, modname: &str, func: Function) -> Result<()> {
|
||||
@@ -488,6 +489,7 @@ impl Lua {
|
||||
/// - Set globals to read-only (and activates safeenv)
|
||||
/// - Setup local environment table that performs writes locally and proxies reads to the global
|
||||
/// environment.
|
||||
/// - Allow only `count` mode in `collectgarbage` function.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
@@ -545,7 +547,7 @@ impl Lua {
|
||||
#[cfg_attr(docsrs, doc(cfg(not(feature = "luau"))))]
|
||||
pub fn set_global_hook<F>(&self, triggers: HookTriggers, callback: F) -> Result<()>
|
||||
where
|
||||
F: Fn(&Lua, Debug) -> Result<VmState> + MaybeSend + 'static,
|
||||
F: Fn(&Lua, &Debug) -> Result<VmState> + MaybeSend + 'static,
|
||||
{
|
||||
let lua = self.lock();
|
||||
unsafe {
|
||||
@@ -578,7 +580,7 @@ impl Lua {
|
||||
/// # fn main() -> Result<()> {
|
||||
/// let lua = Lua::new();
|
||||
/// lua.set_hook(HookTriggers::EVERY_LINE, |_lua, debug| {
|
||||
/// println!("line {}", debug.curr_line());
|
||||
/// println!("line {:?}", debug.current_line());
|
||||
/// Ok(VmState::Continue)
|
||||
/// });
|
||||
///
|
||||
@@ -595,7 +597,7 @@ impl Lua {
|
||||
#[cfg_attr(docsrs, doc(cfg(not(feature = "luau"))))]
|
||||
pub fn set_hook<F>(&self, triggers: HookTriggers, callback: F) -> Result<()>
|
||||
where
|
||||
F: Fn(&Lua, Debug) -> Result<VmState> + MaybeSend + 'static,
|
||||
F: Fn(&Lua, &Debug) -> Result<VmState> + MaybeSend + 'static,
|
||||
{
|
||||
let lua = self.lock();
|
||||
unsafe { lua.set_thread_hook(lua.state(), HookKind::Thread(triggers, XRc::new(callback))) }
|
||||
@@ -630,13 +632,13 @@ impl Lua {
|
||||
///
|
||||
/// Any Luau code is guaranteed to call this handler "eventually"
|
||||
/// (in practice this can happen at any function call or at any loop iteration).
|
||||
/// This is similar to `Lua::set_hook` but in more simplified form.
|
||||
///
|
||||
/// The provided interrupt function can error, and this error will be propagated through
|
||||
/// the Luau code that was executing at the time the interrupt was triggered.
|
||||
/// Also this can be used to implement continuous execution limits by instructing Luau VM to
|
||||
/// yield by returning [`VmState::Yield`].
|
||||
///
|
||||
/// This is similar to [`Lua::set_hook`] but in more simplified form.
|
||||
/// yield by returning [`VmState::Yield`]. The yield will happen only at yieldable points
|
||||
/// of execution (not across metamethod/C-call boundaries).
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
@@ -694,7 +696,10 @@ impl Lua {
|
||||
match result {
|
||||
VmState::Continue => {}
|
||||
VmState::Yield => {
|
||||
ffi::lua_yield(state, 0);
|
||||
// We can yield only at yieldable points, otherwise ignore and continue
|
||||
if ffi::lua_isyieldable(state) != 0 {
|
||||
ffi::lua_yield(state, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -870,18 +875,16 @@ impl Lua {
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets information about the interpreter runtime stack.
|
||||
/// Gets information about the interpreter runtime stack at a given level.
|
||||
///
|
||||
/// This function returns [`Debug`] structure that can be used to get information about the
|
||||
/// function executing at a given level. Level `0` is the current running function, whereas
|
||||
/// level `n+1` is the function that has called level `n` (except for tail calls, which do
|
||||
/// not count in the stack).
|
||||
///
|
||||
/// [`Debug`]: crate::hook::Debug
|
||||
pub fn inspect_stack(&self, level: usize) -> Option<Debug> {
|
||||
/// This function calls callback `f`, passing the [`Debug`] structure that can be used to get
|
||||
/// information about the function executing at a given level.
|
||||
/// Level `0` is the current running function, whereas level `n+1` is the function that has
|
||||
/// called level `n` (except for tail calls, which do not count in the stack).
|
||||
pub fn inspect_stack<R>(&self, level: usize, f: impl FnOnce(&Debug) -> R) -> Option<R> {
|
||||
let lua = self.lock();
|
||||
unsafe {
|
||||
let mut ar: ffi::lua_Debug = mem::zeroed();
|
||||
let mut ar = mem::zeroed::<ffi::lua_Debug>();
|
||||
let level = level as c_int;
|
||||
#[cfg(not(feature = "luau"))]
|
||||
if ffi::lua_getstack(lua.state(), level, &mut ar) == 0 {
|
||||
@@ -891,7 +894,8 @@ impl Lua {
|
||||
if ffi::lua_getinfo(lua.state(), level, cstr!(""), &mut ar) == 0 {
|
||||
return None;
|
||||
}
|
||||
Some(Debug::new_owned(lua, level, ar))
|
||||
|
||||
Some(f(&Debug::new(&lua, level, &mut ar)))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1150,7 +1154,7 @@ impl Lua {
|
||||
}
|
||||
}
|
||||
|
||||
/// Create and return an interned Lua string.
|
||||
/// Creates and returns an interned Lua string.
|
||||
///
|
||||
/// Lua strings can be arbitrary `[u8]` data including embedded nulls, so in addition to `&str`
|
||||
/// and `&String`, you can also pass plain `&[u8]` here.
|
||||
@@ -1159,27 +1163,32 @@ impl Lua {
|
||||
unsafe { self.lock().create_string(s) }
|
||||
}
|
||||
|
||||
/// Create and return a Luau [buffer] object from a byte slice of data.
|
||||
/// Creates and returns a Luau [buffer] object from a byte slice of data.
|
||||
///
|
||||
/// [buffer]: https://luau.org/library#buffer-library
|
||||
#[cfg(any(feature = "luau", doc))]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
|
||||
pub fn create_buffer(&self, buf: impl AsRef<[u8]>) -> Result<Buffer> {
|
||||
pub fn create_buffer(&self, data: impl AsRef<[u8]>) -> Result<Buffer> {
|
||||
let lua = self.lock();
|
||||
let state = lua.state();
|
||||
let data = data.as_ref();
|
||||
unsafe {
|
||||
if lua.unlikely_memory_error() {
|
||||
crate::util::push_buffer(state, buf.as_ref(), false)?;
|
||||
return Ok(Buffer(lua.pop_ref()));
|
||||
}
|
||||
|
||||
let _sg = StackGuard::new(state);
|
||||
check_stack(state, 3)?;
|
||||
crate::util::push_buffer(state, buf.as_ref(), true)?;
|
||||
Ok(Buffer(lua.pop_ref()))
|
||||
let (ptr, buffer) = lua.create_buffer_with_capacity(data.len())?;
|
||||
ptr.copy_from_nonoverlapping(data.as_ptr(), data.len());
|
||||
Ok(buffer)
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates and returns a Luau [buffer] object with the specified size.
|
||||
///
|
||||
/// Size limit is 1GB. All bytes will be initialized to zero.
|
||||
///
|
||||
/// [buffer]: https://luau.org/library#buffer-library
|
||||
#[cfg(any(feature = "luau", doc))]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
|
||||
pub fn create_buffer_with_capacity(&self, size: usize) -> Result<Buffer> {
|
||||
unsafe { Ok(self.lock().create_buffer_with_capacity(size)?.1) }
|
||||
}
|
||||
|
||||
/// Creates and returns a new empty table.
|
||||
#[inline]
|
||||
pub fn create_table(&self) -> Result<Table> {
|
||||
@@ -1202,28 +1211,7 @@ impl Lua {
|
||||
K: IntoLua,
|
||||
V: IntoLua,
|
||||
{
|
||||
let lua = self.lock();
|
||||
let state = lua.state();
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(state);
|
||||
check_stack(state, 6)?;
|
||||
|
||||
let iter = iter.into_iter();
|
||||
let lower_bound = iter.size_hint().0;
|
||||
let protect = !lua.unlikely_memory_error();
|
||||
push_table(state, 0, lower_bound, protect)?;
|
||||
for (k, v) in iter {
|
||||
lua.push(k)?;
|
||||
lua.push(v)?;
|
||||
if protect {
|
||||
protect_lua!(state, 3, 1, fn(state) ffi::lua_rawset(state, -3))?;
|
||||
} else {
|
||||
ffi::lua_rawset(state, -3);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Table(lua.pop_ref()))
|
||||
}
|
||||
unsafe { self.lock().create_table_from(iter) }
|
||||
}
|
||||
|
||||
/// Creates a table from an iterator of values, using `1..` as the keys.
|
||||
@@ -1309,8 +1297,24 @@ impl Lua {
|
||||
/// This function is unsafe because provides a way to execute unsafe C function.
|
||||
pub unsafe fn create_c_function(&self, func: ffi::lua_CFunction) -> Result<Function> {
|
||||
let lua = self.lock();
|
||||
ffi::lua_pushcfunction(lua.ref_thread(), func);
|
||||
Ok(Function(lua.pop_ref_thread()))
|
||||
if cfg!(any(feature = "lua54", feature = "lua53", feature = "lua52")) {
|
||||
ffi::lua_pushcfunction(lua.ref_thread(), func);
|
||||
return Ok(Function(lua.pop_ref_thread()));
|
||||
}
|
||||
|
||||
// Lua <5.2 requires memory allocation to push a C function
|
||||
let state = lua.state();
|
||||
{
|
||||
let _sg = StackGuard::new(state);
|
||||
check_stack(state, 3)?;
|
||||
|
||||
if lua.unlikely_memory_error() {
|
||||
ffi::lua_pushcfunction(state, func);
|
||||
} else {
|
||||
protect_lua!(state, 0, 1, |state| ffi::lua_pushcfunction(state, func))?;
|
||||
}
|
||||
Ok(Function(lua.pop_ref()))
|
||||
}
|
||||
}
|
||||
|
||||
/// Wraps a Rust async function or closure, creating a callable Lua function handle to it.
|
||||
@@ -1390,8 +1394,8 @@ impl Lua {
|
||||
}
|
||||
|
||||
/// Creates a Lua userdata object from a custom serializable userdata type.
|
||||
#[cfg(feature = "serialize")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "serialize")))]
|
||||
#[cfg(feature = "serde")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "serde")))]
|
||||
#[inline]
|
||||
pub fn create_ser_userdata<T>(&self, data: T) -> Result<AnyUserData>
|
||||
where
|
||||
@@ -1418,8 +1422,8 @@ impl Lua {
|
||||
/// Creates a Lua userdata object from a custom serializable Rust type.
|
||||
///
|
||||
/// See [`Lua::create_any_userdata`] for more details.
|
||||
#[cfg(feature = "serialize")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "serialize")))]
|
||||
#[cfg(feature = "serde")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "serde")))]
|
||||
#[inline]
|
||||
pub fn create_ser_any_userdata<T>(&self, data: T) -> Result<AnyUserData>
|
||||
where
|
||||
@@ -1439,7 +1443,7 @@ impl Lua {
|
||||
let lua = self.lock();
|
||||
unsafe {
|
||||
// Deregister the type if it already registered
|
||||
if let Some(&table_id) = (*lua.extra.get()).registered_userdata_t.get(&type_id) {
|
||||
if let Some(table_id) = (*lua.extra.get()).registered_userdata_t.remove(&type_id) {
|
||||
ffi::luaL_unref(lua.state(), ffi::LUA_REGISTRYINDEX, table_id);
|
||||
}
|
||||
|
||||
@@ -1573,6 +1577,39 @@ impl Lua {
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the global environment.
|
||||
///
|
||||
/// This will replace the current global environment with the provided `globals` table.
|
||||
///
|
||||
/// For Lua 5.2+ the globals table is stored in the registry and shared between all threads.
|
||||
/// For Lua 5.1 and Luau the globals table is stored in each thread.
|
||||
///
|
||||
/// Please note that any existing Lua functions have cached global environment and will not
|
||||
/// see the changes made by this method.
|
||||
/// To update the environment for existing Lua functions, use [`Function::set_environment`].
|
||||
pub fn set_globals(&self, globals: Table) -> Result<()> {
|
||||
let lua = self.lock();
|
||||
let state = lua.state();
|
||||
unsafe {
|
||||
#[cfg(feature = "luau")]
|
||||
if (*lua.extra.get()).sandboxed {
|
||||
return Err(Error::runtime("cannot change globals in a sandboxed Lua state"));
|
||||
}
|
||||
|
||||
let _sg = StackGuard::new(state);
|
||||
check_stack(state, 1)?;
|
||||
|
||||
lua.push_ref(&globals.0);
|
||||
|
||||
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
|
||||
ffi::lua_rawseti(state, ffi::LUA_REGISTRYINDEX, ffi::LUA_RIDX_GLOBALS);
|
||||
#[cfg(any(feature = "lua51", feature = "luajit", feature = "luau"))]
|
||||
ffi::lua_replace(state, ffi::LUA_GLOBALSINDEX);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Returns a handle to the active `Thread`.
|
||||
///
|
||||
/// For calls to `Lua` this will be the main Lua thread, for parameters given to a callback,
|
||||
@@ -1978,7 +2015,7 @@ impl Lua {
|
||||
/// Panics if the data object of type `T` is currently mutably borrowed. Multiple immutable
|
||||
/// reads can be taken out at the same time.
|
||||
#[track_caller]
|
||||
pub fn app_data_ref<T: 'static>(&self) -> Option<AppDataRef<T>> {
|
||||
pub fn app_data_ref<T: 'static>(&self) -> Option<AppDataRef<'_, T>> {
|
||||
let guard = self.lock_arc();
|
||||
let extra = unsafe { &*guard.extra.get() };
|
||||
extra.app_data.borrow(Some(guard))
|
||||
@@ -1986,7 +2023,7 @@ impl Lua {
|
||||
|
||||
/// Tries to get a reference to an application data object stored by [`Lua::set_app_data`] of
|
||||
/// type `T`.
|
||||
pub fn try_app_data_ref<T: 'static>(&self) -> StdResult<Option<AppDataRef<T>>, BorrowError> {
|
||||
pub fn try_app_data_ref<T: 'static>(&self) -> StdResult<Option<AppDataRef<'_, T>>, BorrowError> {
|
||||
let guard = self.lock_arc();
|
||||
let extra = unsafe { &*guard.extra.get() };
|
||||
extra.app_data.try_borrow(Some(guard))
|
||||
@@ -1999,7 +2036,7 @@ impl Lua {
|
||||
///
|
||||
/// Panics if the data object of type `T` is currently borrowed.
|
||||
#[track_caller]
|
||||
pub fn app_data_mut<T: 'static>(&self) -> Option<AppDataRefMut<T>> {
|
||||
pub fn app_data_mut<T: 'static>(&self) -> Option<AppDataRefMut<'_, T>> {
|
||||
let guard = self.lock_arc();
|
||||
let extra = unsafe { &*guard.extra.get() };
|
||||
extra.app_data.borrow_mut(Some(guard))
|
||||
@@ -2007,7 +2044,7 @@ impl Lua {
|
||||
|
||||
/// Tries to get a mutable reference to an application data object stored by
|
||||
/// [`Lua::set_app_data`] of type `T`.
|
||||
pub fn try_app_data_mut<T: 'static>(&self) -> StdResult<Option<AppDataRefMut<T>>, BorrowMutError> {
|
||||
pub fn try_app_data_mut<T: 'static>(&self) -> StdResult<Option<AppDataRefMut<'_, T>>, BorrowMutError> {
|
||||
let guard = self.lock_arc();
|
||||
let extra = unsafe { &*guard.extra.get() };
|
||||
extra.app_data.try_borrow_mut(Some(guard))
|
||||
@@ -2036,6 +2073,108 @@ impl Lua {
|
||||
LightUserData(&ASYNC_POLL_PENDING as *const u8 as *mut std::os::raw::c_void)
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
#[inline(always)]
|
||||
pub(crate) fn poll_terminate() -> LightUserData {
|
||||
static ASYNC_POLL_TERMINATE: u8 = 0;
|
||||
LightUserData(&ASYNC_POLL_TERMINATE as *const u8 as *mut std::os::raw::c_void)
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
#[inline(always)]
|
||||
pub(crate) fn poll_yield() -> LightUserData {
|
||||
static ASYNC_POLL_YIELD: u8 = 0;
|
||||
LightUserData(&ASYNC_POLL_YIELD as *const u8 as *mut std::os::raw::c_void)
|
||||
}
|
||||
|
||||
/// Suspends the current async function, returning the provided arguments to caller.
|
||||
///
|
||||
/// This function is similar to [`coroutine.yield`] but allow yeilding Rust functions
|
||||
/// and passing values to the caller.
|
||||
/// Please note that you cannot cross [`Thread`] boundaries (e.g. calling `yield_with` on one
|
||||
/// thread and resuming on another).
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Async iterator:
|
||||
///
|
||||
/// ```
|
||||
/// # use mlua::{Lua, Result};
|
||||
///
|
||||
/// async fn generator(lua: Lua, _: ()) -> Result<()> {
|
||||
/// for i in 0..10 {
|
||||
/// lua.yield_with::<()>(i).await?;
|
||||
/// }
|
||||
/// Ok(())
|
||||
/// }
|
||||
///
|
||||
/// fn main() -> Result<()> {
|
||||
/// let lua = Lua::new();
|
||||
/// lua.globals().set("generator", lua.create_async_function(generator)?)?;
|
||||
///
|
||||
/// lua.load(r#"
|
||||
/// local n = 0
|
||||
/// for i in coroutine.wrap(generator) do
|
||||
/// n = n + i
|
||||
/// end
|
||||
/// assert(n == 45)
|
||||
/// "#)
|
||||
/// .exec()
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Exchange values on yield:
|
||||
///
|
||||
/// ```
|
||||
/// # use mlua::{Lua, Result, Value};
|
||||
///
|
||||
/// async fn pingpong(lua: Lua, mut val: i32) -> Result<()> {
|
||||
/// loop {
|
||||
/// val = lua.yield_with::<i32>(val).await? + 1;
|
||||
/// }
|
||||
/// Ok(())
|
||||
/// }
|
||||
///
|
||||
/// # fn main() -> Result<()> {
|
||||
/// let lua = Lua::new();
|
||||
///
|
||||
/// let co = lua.create_thread(lua.create_async_function(pingpong)?)?;
|
||||
/// assert_eq!(co.resume::<i32>(1)?, 1);
|
||||
/// assert_eq!(co.resume::<i32>(2)?, 3);
|
||||
/// assert_eq!(co.resume::<i32>(3)?, 4);
|
||||
///
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
///
|
||||
/// [`coroutine.yield`]: https://www.lua.org/manual/5.4/manual.html#pdf-coroutine.yield
|
||||
#[cfg(feature = "async")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
|
||||
pub async fn yield_with<R: FromLuaMulti>(&self, args: impl IntoLuaMulti) -> Result<R> {
|
||||
let mut args = Some(args.into_lua_multi(self)?);
|
||||
future::poll_fn(move |_cx| match args.take() {
|
||||
Some(args) => unsafe {
|
||||
let lua = self.lock();
|
||||
lua.push(Self::poll_yield())?; // yield marker
|
||||
if args.len() <= 1 {
|
||||
lua.push(args.front())?;
|
||||
} else {
|
||||
lua.push(lua.create_sequence_from(&args)?)?;
|
||||
}
|
||||
lua.push(args.len())?;
|
||||
Poll::Pending
|
||||
},
|
||||
None => unsafe {
|
||||
let lua = self.lock();
|
||||
let state = lua.state();
|
||||
let _sg = StackGuard::with_top(state, 0);
|
||||
let nvals = ffi::lua_gettop(state);
|
||||
Poll::Ready(R::from_stack_multi(nvals, &lua))
|
||||
},
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
/// Returns a weak reference to the Lua instance.
|
||||
///
|
||||
/// This is useful for creating a reference to the Lua instance that does not prevent it from
|
||||
@@ -2045,7 +2184,6 @@ impl Lua {
|
||||
WeakLua(XRc::downgrade(&self.raw))
|
||||
}
|
||||
|
||||
// Luau version located in `luau/mod.rs`
|
||||
#[cfg(not(feature = "luau"))]
|
||||
fn disable_c_modules(&self) -> Result<()> {
|
||||
let package: Table = self.globals().get("package")?;
|
||||
@@ -2068,13 +2206,15 @@ impl Lua {
|
||||
|
||||
// The third and fourth searchers looks for a loader as a C library
|
||||
searchers.raw_set(3, loader)?;
|
||||
searchers.raw_remove(4)?;
|
||||
if searchers.raw_len() >= 4 {
|
||||
searchers.raw_remove(4)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub(crate) fn lock(&self) -> ReentrantMutexGuard<RawLua> {
|
||||
pub(crate) fn lock(&self) -> ReentrantMutexGuard<'_, RawLua> {
|
||||
let rawlua = self.raw.lock();
|
||||
#[cfg(feature = "luau")]
|
||||
if unsafe { (*rawlua.extra.get()).running_gc } {
|
||||
|
||||
+36
-6
@@ -28,7 +28,7 @@ use super::{Lua, WeakLua};
|
||||
static EXTRA_REGISTRY_KEY: u8 = 0;
|
||||
|
||||
const WRAPPED_FAILURE_POOL_DEFAULT_CAPACITY: usize = 64;
|
||||
const REF_STACK_RESERVE: c_int = 2;
|
||||
const REF_STACK_RESERVE: c_int = 3;
|
||||
|
||||
/// Data associated with the Lua state.
|
||||
pub(crate) struct ExtraData {
|
||||
@@ -44,8 +44,9 @@ pub(crate) struct ExtraData {
|
||||
// When Lua instance dropped, setting `None` would prevent collecting `RegistryKey`s
|
||||
pub(super) registry_unref_list: Arc<Mutex<Option<Vec<c_int>>>>,
|
||||
|
||||
// Container to store arbitrary data (extensions)
|
||||
// Containers to store arbitrary data (extensions)
|
||||
pub(super) app_data: AppData,
|
||||
pub(super) app_data_priv: AppData,
|
||||
|
||||
pub(super) safe: bool,
|
||||
pub(super) libs: StdLib,
|
||||
@@ -63,7 +64,7 @@ pub(crate) struct ExtraData {
|
||||
pub(super) wrapped_failure_top: usize,
|
||||
// Pool of `Thread`s (coroutines) for async execution
|
||||
#[cfg(feature = "async")]
|
||||
pub(super) thread_pool: Vec<c_int>,
|
||||
pub(super) thread_pool: Vec<crate::types::ValueRefIndex>,
|
||||
|
||||
// Address of `WrappedFailure` metatable
|
||||
pub(super) wrapped_failure_mt_ptr: *const c_void,
|
||||
@@ -75,7 +76,7 @@ pub(crate) struct ExtraData {
|
||||
#[cfg(not(feature = "luau"))]
|
||||
pub(super) hook_callback: Option<crate::types::HookCallback>,
|
||||
#[cfg(not(feature = "luau"))]
|
||||
pub(super) hook_triggers: crate::hook::HookTriggers,
|
||||
pub(super) hook_triggers: crate::debug::HookTriggers,
|
||||
#[cfg(feature = "lua54")]
|
||||
pub(super) warn_callback: Option<crate::types::WarnCallback>,
|
||||
#[cfg(feature = "luau")]
|
||||
@@ -88,7 +89,7 @@ pub(crate) struct ExtraData {
|
||||
#[cfg(feature = "luau")]
|
||||
pub(crate) running_gc: bool,
|
||||
#[cfg(feature = "luau")]
|
||||
pub(super) sandboxed: bool,
|
||||
pub(crate) sandboxed: bool,
|
||||
#[cfg(feature = "luau")]
|
||||
pub(super) compiler: Option<Compiler>,
|
||||
#[cfg(feature = "luau-jit")]
|
||||
@@ -159,6 +160,7 @@ impl ExtraData {
|
||||
last_checked_userdata_mt: (ptr::null(), None),
|
||||
registry_unref_list: Arc::new(Mutex::new(Some(Vec::new()))),
|
||||
app_data: AppData::default(),
|
||||
app_data_priv: AppData::default(),
|
||||
safe: false,
|
||||
libs: StdLib::NONE,
|
||||
skip_memory_check: false,
|
||||
@@ -210,7 +212,7 @@ impl ExtraData {
|
||||
self.weak.write(WeakLua(XRc::downgrade(raw)));
|
||||
}
|
||||
|
||||
pub(super) unsafe fn get(state: *mut ffi::lua_State) -> *mut Self {
|
||||
pub(crate) unsafe fn get(state: *mut ffi::lua_State) -> *mut Self {
|
||||
#[cfg(feature = "luau")]
|
||||
if cfg!(not(feature = "module")) {
|
||||
// In the main app we can use `lua_callbacks` to access ExtraData
|
||||
@@ -257,4 +259,32 @@ impl ExtraData {
|
||||
pub(super) unsafe fn weak(&self) -> &WeakLua {
|
||||
self.weak.assume_init_ref()
|
||||
}
|
||||
|
||||
/// Pops a reference from top of the auxiliary stack and move it to a first free slot.
|
||||
pub(super) unsafe fn ref_stack_pop(&mut self) -> c_int {
|
||||
if let Some(free) = self.ref_free.pop() {
|
||||
ffi::lua_replace(self.ref_thread, free);
|
||||
return free;
|
||||
}
|
||||
|
||||
// Try to grow max stack size
|
||||
if self.ref_stack_top >= self.ref_stack_size {
|
||||
let mut inc = self.ref_stack_size; // Try to double stack size
|
||||
while inc > 0 && ffi::lua_checkstack(self.ref_thread, inc + REF_STACK_RESERVE) == 0 {
|
||||
inc /= 2;
|
||||
}
|
||||
if inc == 0 {
|
||||
// Pop item on top of the stack to avoid stack leaking and successfully run destructors
|
||||
// during unwinding.
|
||||
ffi::lua_pop(self.ref_thread, 1);
|
||||
let top = self.ref_stack_top;
|
||||
// It is a user error to create too many references to exhaust the Lua max stack size
|
||||
// for the ref thread.
|
||||
panic!("cannot create a Lua reference, out of auxiliary stack space (used {top} slots)");
|
||||
}
|
||||
self.ref_stack_size += inc;
|
||||
}
|
||||
self.ref_stack_top += 1;
|
||||
self.ref_stack_top
|
||||
}
|
||||
}
|
||||
|
||||
+113
-80
@@ -5,14 +5,13 @@ use std::mem;
|
||||
use std::os::raw::{c_char, c_int, c_void};
|
||||
use std::panic::resume_unwind;
|
||||
use std::ptr::{self, NonNull};
|
||||
use std::result::Result as StdResult;
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::chunk::ChunkMode;
|
||||
use crate::error::{Error, Result};
|
||||
use crate::function::Function;
|
||||
use crate::memory::{MemoryState, ALLOCATOR};
|
||||
use crate::state::util::{callback_error_ext, ref_stack_pop};
|
||||
use crate::state::util::callback_error_ext;
|
||||
use crate::stdlib::StdLib;
|
||||
use crate::string::String;
|
||||
use crate::table::Table;
|
||||
@@ -39,7 +38,7 @@ use super::{Lua, LuaOptions, WeakLua};
|
||||
|
||||
#[cfg(not(feature = "luau"))]
|
||||
use crate::{
|
||||
hook::Debug,
|
||||
debug::Debug,
|
||||
types::{HookCallback, HookKind, VmState},
|
||||
};
|
||||
|
||||
@@ -209,7 +208,7 @@ impl RawLua {
|
||||
}
|
||||
|
||||
// Init serde metatables
|
||||
#[cfg(feature = "serialize")]
|
||||
#[cfg(feature = "serde")]
|
||||
crate::serde::init_metatables(state)?;
|
||||
|
||||
Ok::<_, Error>(())
|
||||
@@ -297,7 +296,7 @@ impl RawLua {
|
||||
if is_safe {
|
||||
let curr_libs = (*self.extra.get()).libs;
|
||||
if (curr_libs ^ (curr_libs | libs)).contains(StdLib::PACKAGE) {
|
||||
mlua_expect!(self.lua().disable_c_modules(), "Error during disabling C modules");
|
||||
mlua_expect!(self.lua().disable_c_modules(), "Error disabling C modules");
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "luau")]
|
||||
@@ -307,27 +306,27 @@ impl RawLua {
|
||||
res
|
||||
}
|
||||
|
||||
/// See [`Lua::try_set_app_data`]
|
||||
/// Private version of [`Lua::try_set_app_data`]
|
||||
#[inline]
|
||||
pub(crate) fn try_set_app_data<T: MaybeSend + 'static>(&self, data: T) -> StdResult<Option<T>, T> {
|
||||
pub(crate) fn set_priv_app_data<T: MaybeSend + 'static>(&self, data: T) -> Option<T> {
|
||||
let extra = unsafe { &*self.extra.get() };
|
||||
extra.app_data.try_insert(data)
|
||||
extra.app_data_priv.insert(data)
|
||||
}
|
||||
|
||||
/// See [`Lua::app_data_ref`]
|
||||
/// Private version of [`Lua::app_data_ref`]
|
||||
#[track_caller]
|
||||
#[inline]
|
||||
pub(crate) fn app_data_ref_unguarded<T: 'static>(&self) -> Option<AppDataRef<T>> {
|
||||
pub(crate) fn priv_app_data_ref<T: 'static>(&self) -> Option<AppDataRef<'_, T>> {
|
||||
let extra = unsafe { &*self.extra.get() };
|
||||
extra.app_data.borrow(None)
|
||||
extra.app_data_priv.borrow(None)
|
||||
}
|
||||
|
||||
/// See [`Lua::app_data_mut`]
|
||||
/// Private version of [`Lua::app_data_mut`]
|
||||
#[track_caller]
|
||||
#[inline]
|
||||
pub(crate) fn app_data_mut_unguarded<T: 'static>(&self) -> Option<AppDataRefMut<T>> {
|
||||
pub(crate) fn priv_app_data_mut<T: 'static>(&self) -> Option<AppDataRefMut<'_, T>> {
|
||||
let extra = unsafe { &*self.extra.get() };
|
||||
extra.app_data.borrow_mut(None)
|
||||
extra.app_data_priv.borrow_mut(None)
|
||||
}
|
||||
|
||||
/// See [`Lua::create_registry_value`]
|
||||
@@ -436,8 +435,8 @@ impl RawLua {
|
||||
match (*extra).hook_callback.clone() {
|
||||
Some(hook_callback) => {
|
||||
let rawlua = (*extra).raw_lua();
|
||||
let debug = Debug::new(rawlua, ar);
|
||||
hook_callback((*extra).lua(), debug)
|
||||
let debug = Debug::new(rawlua, 0, ar);
|
||||
hook_callback((*extra).lua(), &debug)
|
||||
}
|
||||
None => {
|
||||
ffi::lua_sethook(state, None, 0, 0);
|
||||
@@ -466,9 +465,9 @@ impl RawLua {
|
||||
|
||||
let status = callback_error_ext(state, ptr::null_mut(), false, |extra, _| {
|
||||
let rawlua = (*extra).raw_lua();
|
||||
let debug = Debug::new(rawlua, ar);
|
||||
let debug = Debug::new(rawlua, 0, ar);
|
||||
let hook_callback = (*hook_callback_ptr).clone();
|
||||
hook_callback((*extra).lua(), debug)
|
||||
hook_callback((*extra).lua(), &debug)
|
||||
});
|
||||
process_status(state, (*ar).event, status)
|
||||
}
|
||||
@@ -524,6 +523,20 @@ impl RawLua {
|
||||
Ok(String(self.pop_ref()))
|
||||
}
|
||||
|
||||
#[cfg(feature = "luau")]
|
||||
pub(crate) unsafe fn create_buffer_with_capacity(&self, size: usize) -> Result<(*mut u8, crate::Buffer)> {
|
||||
let state = self.state();
|
||||
if self.unlikely_memory_error() {
|
||||
let ptr = crate::util::push_buffer(state, size, false)?;
|
||||
return Ok((ptr, crate::Buffer(self.pop_ref())));
|
||||
}
|
||||
|
||||
let _sg = StackGuard::new(state);
|
||||
check_stack(state, 3)?;
|
||||
let ptr = crate::util::push_buffer(state, size, true)?;
|
||||
Ok((ptr, crate::Buffer(self.pop_ref())))
|
||||
}
|
||||
|
||||
/// See [`Lua::create_table_with_capacity`]
|
||||
pub(crate) unsafe fn create_table_with_capacity(&self, narr: usize, nrec: usize) -> Result<Table> {
|
||||
let state = self.state();
|
||||
@@ -538,6 +551,34 @@ impl RawLua {
|
||||
Ok(Table(self.pop_ref()))
|
||||
}
|
||||
|
||||
/// See [`Lua::create_table_from`]
|
||||
pub(crate) unsafe fn create_table_from<I, K, V>(&self, iter: I) -> Result<Table>
|
||||
where
|
||||
I: IntoIterator<Item = (K, V)>,
|
||||
K: IntoLua,
|
||||
V: IntoLua,
|
||||
{
|
||||
let state = self.state();
|
||||
let _sg = StackGuard::new(state);
|
||||
check_stack(state, 6)?;
|
||||
|
||||
let iter = iter.into_iter();
|
||||
let lower_bound = iter.size_hint().0;
|
||||
let protect = !self.unlikely_memory_error();
|
||||
push_table(state, 0, lower_bound, protect)?;
|
||||
for (k, v) in iter {
|
||||
self.push(k)?;
|
||||
self.push(v)?;
|
||||
if protect {
|
||||
protect_lua!(state, 3, 1, fn(state) ffi::lua_rawset(state, -3))?;
|
||||
} else {
|
||||
ffi::lua_rawset(state, -3);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Table(self.pop_ref()))
|
||||
}
|
||||
|
||||
/// See [`Lua::create_sequence_from`]
|
||||
pub(crate) unsafe fn create_sequence_from<T, I>(&self, iter: I) -> Result<Table>
|
||||
where
|
||||
@@ -597,7 +638,7 @@ impl RawLua {
|
||||
#[cfg(feature = "async")]
|
||||
pub(crate) unsafe fn create_recycled_thread(&self, func: &Function) -> Result<Thread> {
|
||||
if let Some(index) = (*self.extra.get()).thread_pool.pop() {
|
||||
let thread_state = ffi::lua_tothread(self.ref_thread(), index);
|
||||
let thread_state = ffi::lua_tothread(self.ref_thread(), *index.0);
|
||||
ffi::lua_xpush(self.ref_thread(), thread_state, func.0.index);
|
||||
|
||||
#[cfg(feature = "luau")]
|
||||
@@ -613,48 +654,14 @@ impl RawLua {
|
||||
self.create_thread(func)
|
||||
}
|
||||
|
||||
/// Resets thread (coroutine) and returns it to the pool for later use.
|
||||
/// Returns the thread to the pool for later use.
|
||||
#[cfg(feature = "async")]
|
||||
pub(crate) unsafe fn recycle_thread(&self, thread: &mut Thread) {
|
||||
let thread_state = thread.1;
|
||||
let extra = &mut *self.extra.get();
|
||||
if extra.thread_pool.len() == extra.thread_pool.capacity() {
|
||||
#[cfg(feature = "lua54")]
|
||||
if ffi::lua_status(thread_state) != ffi::LUA_OK {
|
||||
// Close all to-be-closed variables without returning thread to the pool
|
||||
#[cfg(not(feature = "vendored"))]
|
||||
ffi::lua_resetthread(thread_state);
|
||||
#[cfg(feature = "vendored")]
|
||||
ffi::lua_closethread(thread_state, self.state());
|
||||
if extra.thread_pool.len() < extra.thread_pool.capacity() {
|
||||
if let Some(index) = thread.0.index_count.take() {
|
||||
extra.thread_pool.push(index);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
let mut reset_ok = false;
|
||||
if ffi::lua_status(thread_state) == ffi::LUA_OK {
|
||||
if ffi::lua_gettop(thread_state) > 0 {
|
||||
ffi::lua_settop(thread_state, 0);
|
||||
}
|
||||
reset_ok = true;
|
||||
}
|
||||
|
||||
#[cfg(feature = "lua54")]
|
||||
if !reset_ok {
|
||||
#[cfg(not(feature = "vendored"))]
|
||||
let status = ffi::lua_resetthread(thread_state);
|
||||
#[cfg(feature = "vendored")]
|
||||
let status = ffi::lua_closethread(thread_state, self.state());
|
||||
reset_ok = status == ffi::LUA_OK;
|
||||
}
|
||||
#[cfg(feature = "luau")]
|
||||
if !reset_ok {
|
||||
ffi::lua_resetthread(thread_state);
|
||||
reset_ok = true;
|
||||
}
|
||||
|
||||
if reset_ok {
|
||||
extra.thread_pool.push(thread.0.index);
|
||||
thread.0.drop = false; // Prevent thread from being garbage collected
|
||||
}
|
||||
}
|
||||
|
||||
@@ -702,7 +709,7 @@ impl RawLua {
|
||||
|
||||
/// Pops a value from the Lua stack.
|
||||
///
|
||||
/// Uses 2 stack spaces, does not call `checkstack`.
|
||||
/// Uses up to 1 stack spaces, does not call `checkstack`.
|
||||
pub(crate) unsafe fn pop_value(&self) -> Value {
|
||||
let value = self.stack_value(-1, None);
|
||||
ffi::lua_pop(self.state(), 1);
|
||||
@@ -711,7 +718,7 @@ impl RawLua {
|
||||
|
||||
/// Returns value at given stack index without popping it.
|
||||
///
|
||||
/// Uses 2 stack spaces, does not call checkstack.
|
||||
/// Uses up to 1 stack spaces, does not call `checkstack`.
|
||||
pub(crate) unsafe fn stack_value(&self, idx: c_int, type_hint: Option<c_int>) -> Value {
|
||||
let state = self.state();
|
||||
match type_hint.unwrap_or_else(|| ffi::lua_type(state, idx)) {
|
||||
@@ -736,7 +743,7 @@ impl RawLua {
|
||||
|
||||
let n = ffi::lua_tonumber(state, idx);
|
||||
match num_traits::cast(n) {
|
||||
Some(i) if (n - (i as Number)).abs() < Number::EPSILON => Value::Integer(i),
|
||||
Some(i) if n.to_bits() == (i as Number).to_bits() => Value::Integer(i),
|
||||
_ => Value::Number(n),
|
||||
}
|
||||
}
|
||||
@@ -824,21 +831,14 @@ impl RawLua {
|
||||
#[inline]
|
||||
pub(crate) unsafe fn pop_ref(&self) -> ValueRef {
|
||||
ffi::lua_xmove(self.state(), self.ref_thread(), 1);
|
||||
let index = ref_stack_pop(self.extra.get());
|
||||
let index = (*self.extra.get()).ref_stack_pop();
|
||||
ValueRef::new(self, index)
|
||||
}
|
||||
|
||||
// Same as `pop_ref` but assumes the value is already on the reference thread
|
||||
#[inline]
|
||||
pub(crate) unsafe fn pop_ref_thread(&self) -> ValueRef {
|
||||
let index = ref_stack_pop(self.extra.get());
|
||||
ValueRef::new(self, index)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) unsafe fn clone_ref(&self, vref: &ValueRef) -> ValueRef {
|
||||
ffi::lua_pushvalue(self.ref_thread(), vref.index);
|
||||
let index = ref_stack_pop(self.extra.get());
|
||||
let index = (*self.extra.get()).ref_stack_pop();
|
||||
ValueRef::new(self, index)
|
||||
}
|
||||
|
||||
@@ -1244,7 +1244,7 @@ impl RawLua {
|
||||
let rawlua = (*extra).raw_lua();
|
||||
|
||||
let func = &*(*upvalue).data;
|
||||
let fut = func(rawlua, nargs);
|
||||
let fut = Some(func(rawlua, nargs));
|
||||
let extra = XRc::clone(&(*upvalue).extra);
|
||||
let protect = !rawlua.unlikely_memory_error();
|
||||
push_internal_userdata(state, AsyncPollUpvalue { data: fut, extra }, protect)?;
|
||||
@@ -1262,20 +1262,34 @@ impl RawLua {
|
||||
|
||||
unsafe extern "C-unwind" fn poll_future(state: *mut ffi::lua_State) -> c_int {
|
||||
let upvalue = get_userdata::<AsyncPollUpvalue>(state, ffi::lua_upvalueindex(1));
|
||||
callback_error_ext(state, (*upvalue).extra.get(), true, |extra, _| {
|
||||
callback_error_ext(state, (*upvalue).extra.get(), true, |extra, nargs| {
|
||||
// Lua ensures that `LUA_MINSTACK` stack spaces are available (after pushing arguments)
|
||||
// The lock must be already held as the future is polled
|
||||
let rawlua = (*extra).raw_lua();
|
||||
|
||||
if nargs == 1 && ffi::lua_tolightuserdata(state, -1) == Lua::poll_terminate().0 {
|
||||
// Destroy the future and terminate the Lua thread
|
||||
(*upvalue).data.take();
|
||||
ffi::lua_pushinteger(state, -1);
|
||||
return Ok(1);
|
||||
}
|
||||
|
||||
let fut = &mut (*upvalue).data;
|
||||
let mut ctx = Context::from_waker(rawlua.waker());
|
||||
match fut.as_mut().poll(&mut ctx) {
|
||||
Poll::Pending => {
|
||||
match fut.as_mut().map(|fut| fut.as_mut().poll(&mut ctx)) {
|
||||
Some(Poll::Pending) => {
|
||||
let fut_nvals = ffi::lua_gettop(state);
|
||||
if fut_nvals >= 3 && ffi::lua_tolightuserdata(state, -3) == Lua::poll_yield().0 {
|
||||
// We have some values to yield
|
||||
ffi::lua_pushnil(state);
|
||||
ffi::lua_replace(state, -4);
|
||||
return Ok(3);
|
||||
}
|
||||
ffi::lua_pushnil(state);
|
||||
ffi::lua_pushlightuserdata(state, Lua::poll_pending().0);
|
||||
Ok(2)
|
||||
}
|
||||
Poll::Ready(nresults) => {
|
||||
Some(Poll::Ready(nresults)) => {
|
||||
match nresults? {
|
||||
nresults if nresults < 3 => {
|
||||
// Fast path for up to 2 results without creating a table
|
||||
@@ -1293,6 +1307,7 @@ impl RawLua {
|
||||
}
|
||||
}
|
||||
}
|
||||
None => Err(Error::CallbackDestructed),
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -1338,8 +1353,9 @@ impl RawLua {
|
||||
lua.load(
|
||||
r#"
|
||||
local poll = get_poll(...)
|
||||
local nres, res, res2 = poll()
|
||||
while true do
|
||||
local nres, res, res2 = poll()
|
||||
-- Poll::Ready branch, `nres` is the number of results
|
||||
if nres ~= nil then
|
||||
if nres == 0 then
|
||||
return
|
||||
@@ -1347,11 +1363,28 @@ impl RawLua {
|
||||
return res
|
||||
elseif nres == 2 then
|
||||
return res, res2
|
||||
elseif nres < 0 then
|
||||
-- Negative `nres` means that the future is terminated
|
||||
-- It must stay yielded and never be resumed again
|
||||
yield()
|
||||
else
|
||||
return unpack(res, nres)
|
||||
end
|
||||
end
|
||||
yield(res) -- `res` is a "pending" value
|
||||
|
||||
-- Poll::Pending branch
|
||||
if res2 == nil then
|
||||
-- `res` is a "pending" value
|
||||
-- `yield` can return a signal to drop the future that we should propagate
|
||||
-- to the poller
|
||||
nres, res, res2 = poll(yield(res))
|
||||
elseif res2 == 0 then
|
||||
nres, res, res2 = poll(yield())
|
||||
elseif res2 == 1 then
|
||||
nres, res, res2 = poll(yield(res))
|
||||
else
|
||||
nres, res, res2 = poll(yield(unpack(res, res2)))
|
||||
end
|
||||
end
|
||||
"#,
|
||||
)
|
||||
@@ -1363,14 +1396,14 @@ impl RawLua {
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
#[inline]
|
||||
pub(crate) unsafe fn waker(&self) -> &Waker {
|
||||
(*self.extra.get()).waker.as_ref()
|
||||
pub(crate) fn waker(&self) -> &Waker {
|
||||
unsafe { (*self.extra.get()).waker.as_ref() }
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
#[inline]
|
||||
pub(crate) unsafe fn set_waker(&self, waker: NonNull<Waker>) -> NonNull<Waker> {
|
||||
mem::replace(&mut (*self.extra.get()).waker, waker)
|
||||
pub(crate) fn set_waker(&self, waker: NonNull<Waker>) -> NonNull<Waker> {
|
||||
unsafe { mem::replace(&mut (*self.extra.get()).waker, waker) }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
-29
@@ -89,7 +89,7 @@ where
|
||||
PreallocatedFailure::New(_) => {
|
||||
ffi::lua_rotate(state, 1, -1);
|
||||
ffi::lua_xmove(state, ref_thread, 1);
|
||||
let index = ref_stack_pop(extra);
|
||||
let index = (*extra).ref_stack_pop();
|
||||
(*extra).wrapped_failure_pool.push(index);
|
||||
(*extra).wrapped_failure_top += 1;
|
||||
}
|
||||
@@ -150,31 +150,3 @@ where
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) unsafe fn ref_stack_pop(extra: *mut ExtraData) -> c_int {
|
||||
let extra = &mut *extra;
|
||||
if let Some(free) = extra.ref_free.pop() {
|
||||
ffi::lua_replace(extra.ref_thread, free);
|
||||
return free;
|
||||
}
|
||||
|
||||
// Try to grow max stack size
|
||||
if extra.ref_stack_top >= extra.ref_stack_size {
|
||||
let mut inc = extra.ref_stack_size; // Try to double stack size
|
||||
while inc > 0 && ffi::lua_checkstack(extra.ref_thread, inc) == 0 {
|
||||
inc /= 2;
|
||||
}
|
||||
if inc == 0 {
|
||||
// Pop item on top of the stack to avoid stack leaking and successfully run destructors
|
||||
// during unwinding.
|
||||
ffi::lua_pop(extra.ref_thread, 1);
|
||||
let top = extra.ref_stack_top;
|
||||
// It is a user error to create enough references to exhaust the Lua max stack size for
|
||||
// the ref thread.
|
||||
panic!("cannot create a Lua reference, out of auxiliary stack space (used {top} slots)");
|
||||
}
|
||||
extra.ref_stack_size += inc;
|
||||
}
|
||||
extra.ref_stack_top += 1;
|
||||
extra.ref_stack_top
|
||||
}
|
||||
|
||||
+9
-9
@@ -11,7 +11,7 @@ use crate::traits::IntoLua;
|
||||
use crate::types::{LuaType, ValueRef};
|
||||
use crate::value::Value;
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
#[cfg(feature = "serde")]
|
||||
use {
|
||||
serde::ser::{Serialize, Serializer},
|
||||
std::result::Result as StdResult,
|
||||
@@ -43,7 +43,7 @@ impl String {
|
||||
/// # }
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn to_str(&self) -> Result<BorrowedStr> {
|
||||
pub fn to_str(&self) -> Result<BorrowedStr<'_>> {
|
||||
BorrowedStr::try_from(self)
|
||||
}
|
||||
|
||||
@@ -86,7 +86,7 @@ impl String {
|
||||
|
||||
/// Get the bytes that make up this string.
|
||||
///
|
||||
/// The returned slice will not contain the terminating nul byte, but will contain any nul
|
||||
/// The returned slice will not contain the terminating null byte, but will contain any null
|
||||
/// bytes embedded into the Lua string.
|
||||
///
|
||||
/// # Examples
|
||||
@@ -102,19 +102,19 @@ impl String {
|
||||
/// # }
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn as_bytes(&self) -> BorrowedBytes {
|
||||
pub fn as_bytes(&self) -> BorrowedBytes<'_> {
|
||||
BorrowedBytes::from(self)
|
||||
}
|
||||
|
||||
/// Get the bytes that make up this string, including the trailing nul byte.
|
||||
pub fn as_bytes_with_nul(&self) -> BorrowedBytes {
|
||||
/// Get the bytes that make up this string, including the trailing null byte.
|
||||
pub fn as_bytes_with_nul(&self) -> BorrowedBytes<'_> {
|
||||
let BorrowedBytes { buf, borrow, _lua } = BorrowedBytes::from(self);
|
||||
// Include the trailing nul byte (it's always present but excluded by default)
|
||||
// Include the trailing null byte (it's always present but excluded by default)
|
||||
let buf = unsafe { slice::from_raw_parts((*buf).as_ptr(), (*buf).len() + 1) };
|
||||
BorrowedBytes { buf, borrow, _lua }
|
||||
}
|
||||
|
||||
// Does not return the terminating nul byte
|
||||
// Does not return the terminating null byte
|
||||
unsafe fn to_slice(&self) -> (&[u8], Lua) {
|
||||
let lua = self.0.lua.upgrade();
|
||||
let slice = {
|
||||
@@ -211,7 +211,7 @@ impl Hash for String {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
#[cfg(feature = "serde")]
|
||||
impl Serialize for String {
|
||||
fn serialize<S>(&self, serializer: S) -> StdResult<S::Ok, S::Error>
|
||||
where
|
||||
|
||||
+30
-38
@@ -13,9 +13,9 @@ use crate::util::{assert_stack, check_stack, get_metatable_ptr, StackGuard};
|
||||
use crate::value::{Nil, Value};
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
use futures_util::future::{self, Either, Future};
|
||||
use crate::function::AsyncCallFuture;
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
#[cfg(feature = "serde")]
|
||||
use {
|
||||
rustc_hash::FxHashSet,
|
||||
serde::ser::{Serialize, SerializeMap, SerializeSeq, Serializer},
|
||||
@@ -211,7 +211,7 @@ impl Table {
|
||||
///
|
||||
/// let always_equals_mt = lua.create_table()?;
|
||||
/// always_equals_mt.set("__eq", lua.create_function(|_, (_t1, _t2): (Table, Table)| Ok(true))?)?;
|
||||
/// table2.set_metatable(Some(always_equals_mt));
|
||||
/// table2.set_metatable(Some(always_equals_mt))?;
|
||||
///
|
||||
/// assert!(table1.equals(&table1.clone())?);
|
||||
/// assert!(table1.equals(&table2)?);
|
||||
@@ -487,16 +487,12 @@ impl Table {
|
||||
/// [`getmetatable`]: https://www.lua.org/manual/5.4/manual.html#pdf-getmetatable
|
||||
pub fn metatable(&self) -> Option<Table> {
|
||||
let lua = self.0.lua.lock();
|
||||
let state = lua.state();
|
||||
let ref_thread = lua.ref_thread();
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(state);
|
||||
assert_stack(state, 2);
|
||||
|
||||
lua.push_ref(&self.0);
|
||||
if ffi::lua_getmetatable(state, -1) == 0 {
|
||||
if ffi::lua_getmetatable(ref_thread, self.0.index) == 0 {
|
||||
None
|
||||
} else {
|
||||
Some(Table(lua.pop_ref()))
|
||||
Some(Table(lua.pop_ref_thread()))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -505,27 +501,23 @@ impl Table {
|
||||
///
|
||||
/// If `metatable` is `None`, the metatable is removed (if no metatable is set, this does
|
||||
/// nothing).
|
||||
pub fn set_metatable(&self, metatable: Option<Table>) {
|
||||
// Workaround to throw readonly error without returning Result
|
||||
pub fn set_metatable(&self, metatable: Option<Table>) -> Result<()> {
|
||||
#[cfg(feature = "luau")]
|
||||
if self.is_readonly() {
|
||||
panic!("attempt to modify a readonly table");
|
||||
return Err(Error::runtime("attempt to modify a readonly table"));
|
||||
}
|
||||
|
||||
let lua = self.0.lua.lock();
|
||||
let state = lua.state();
|
||||
let ref_thread = lua.ref_thread();
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(state);
|
||||
assert_stack(state, 2);
|
||||
|
||||
lua.push_ref(&self.0);
|
||||
if let Some(metatable) = metatable {
|
||||
lua.push_ref(&metatable.0);
|
||||
if let Some(metatable) = &metatable {
|
||||
ffi::lua_pushvalue(ref_thread, metatable.0.index);
|
||||
} else {
|
||||
ffi::lua_pushnil(state);
|
||||
ffi::lua_pushnil(ref_thread);
|
||||
}
|
||||
ffi::lua_setmetatable(state, -2);
|
||||
ffi::lua_setmetatable(ref_thread, self.0.index);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Returns true if the table has metatable attached.
|
||||
@@ -613,7 +605,7 @@ impl Table {
|
||||
/// ```
|
||||
///
|
||||
/// [Lua manual]: http://www.lua.org/manual/5.4/manual.html#pdf-next
|
||||
pub fn pairs<K: FromLua, V: FromLua>(&self) -> TablePairs<K, V> {
|
||||
pub fn pairs<K: FromLua, V: FromLua>(&self) -> TablePairs<'_, K, V> {
|
||||
TablePairs {
|
||||
guard: self.0.lua.lock(),
|
||||
table: self,
|
||||
@@ -678,7 +670,7 @@ impl Table {
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn sequence_values<V: FromLua>(&self) -> TableSequence<V> {
|
||||
pub fn sequence_values<V: FromLua>(&self) -> TableSequence<'_, V> {
|
||||
TableSequence {
|
||||
guard: self.0.lua.lock(),
|
||||
table: self,
|
||||
@@ -735,7 +727,7 @@ impl Table {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
#[cfg(feature = "serde")]
|
||||
pub(crate) fn is_array(&self) -> bool {
|
||||
let lua = self.0.lua.lock();
|
||||
let state = lua.state();
|
||||
@@ -892,16 +884,16 @@ impl ObjectLike for Table {
|
||||
R: FromLuaMulti,
|
||||
{
|
||||
// Convert table to a function and call via pcall that respects the `__call` metamethod.
|
||||
Function(self.0.copy()).call(args)
|
||||
Function(self.0.clone()).call(args)
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
#[inline]
|
||||
fn call_async<R>(&self, args: impl IntoLuaMulti) -> impl Future<Output = Result<R>>
|
||||
fn call_async<R>(&self, args: impl IntoLuaMulti) -> AsyncCallFuture<R>
|
||||
where
|
||||
R: FromLuaMulti,
|
||||
{
|
||||
Function(self.0.copy()).call_async(args)
|
||||
Function(self.0.clone()).call_async(args)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@@ -913,7 +905,7 @@ impl ObjectLike for Table {
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
fn call_async_method<R>(&self, name: &str, args: impl IntoLuaMulti) -> impl Future<Output = Result<R>>
|
||||
fn call_async_method<R>(&self, name: &str, args: impl IntoLuaMulti) -> AsyncCallFuture<R>
|
||||
where
|
||||
R: FromLuaMulti,
|
||||
{
|
||||
@@ -933,35 +925,35 @@ impl ObjectLike for Table {
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
#[inline]
|
||||
fn call_async_function<R>(&self, name: &str, args: impl IntoLuaMulti) -> impl Future<Output = Result<R>>
|
||||
fn call_async_function<R>(&self, name: &str, args: impl IntoLuaMulti) -> AsyncCallFuture<R>
|
||||
where
|
||||
R: FromLuaMulti,
|
||||
{
|
||||
match self.get(name) {
|
||||
Ok(Value::Function(func)) => Either::Left(func.call_async(args)),
|
||||
Ok(Value::Function(func)) => func.call_async(args),
|
||||
Ok(val) => {
|
||||
let msg = format!("attempt to call a {} value (function '{name}')", val.type_name());
|
||||
Either::Right(future::ready(Err(Error::RuntimeError(msg))))
|
||||
AsyncCallFuture::error(Error::RuntimeError(msg))
|
||||
}
|
||||
Err(err) => Either::Right(future::ready(Err(err))),
|
||||
Err(err) => AsyncCallFuture::error(err),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn to_string(&self) -> Result<StdString> {
|
||||
Value::Table(Table(self.0.copy())).to_string()
|
||||
Value::Table(Table(self.0.clone())).to_string()
|
||||
}
|
||||
}
|
||||
|
||||
/// A wrapped [`Table`] with customized serialization behavior.
|
||||
#[cfg(feature = "serialize")]
|
||||
#[cfg(feature = "serde")]
|
||||
pub(crate) struct SerializableTable<'a> {
|
||||
table: &'a Table,
|
||||
options: crate::serde::de::Options,
|
||||
visited: Rc<RefCell<FxHashSet<*const c_void>>>,
|
||||
}
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
#[cfg(feature = "serde")]
|
||||
impl Serialize for Table {
|
||||
#[inline]
|
||||
fn serialize<S: Serializer>(&self, serializer: S) -> StdResult<S::Ok, S::Error> {
|
||||
@@ -969,7 +961,7 @@ impl Serialize for Table {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
#[cfg(feature = "serde")]
|
||||
impl<'a> SerializableTable<'a> {
|
||||
#[inline]
|
||||
pub(crate) fn new(
|
||||
@@ -985,7 +977,7 @@ impl<'a> SerializableTable<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
#[cfg(feature = "serde")]
|
||||
impl Serialize for SerializableTable<'_> {
|
||||
fn serialize<S>(&self, serializer: S) -> StdResult<S::Ok, S::Error>
|
||||
where
|
||||
|
||||
+68
-34
@@ -10,7 +10,7 @@ use crate::util::{check_stack, error_traceback_thread, pop_error, StackGuard};
|
||||
|
||||
#[cfg(not(feature = "luau"))]
|
||||
use crate::{
|
||||
hook::{Debug, HookTriggers},
|
||||
debug::{Debug, HookTriggers},
|
||||
types::HookKind,
|
||||
};
|
||||
|
||||
@@ -86,13 +86,15 @@ unsafe impl Sync for Thread {}
|
||||
#[must_use = "futures do nothing unless you `.await` or poll them"]
|
||||
pub struct AsyncThread<R> {
|
||||
thread: Thread,
|
||||
ret: PhantomData<R>,
|
||||
ret: PhantomData<fn() -> R>,
|
||||
recycle: bool,
|
||||
}
|
||||
|
||||
impl Thread {
|
||||
/// Returns reference to the Lua state that this thread is associated with.
|
||||
#[doc(hidden)]
|
||||
#[inline(always)]
|
||||
fn state(&self) -> *mut ffi::lua_State {
|
||||
pub fn state(&self) -> *mut ffi::lua_State {
|
||||
self.1
|
||||
}
|
||||
|
||||
@@ -154,7 +156,6 @@ impl Thread {
|
||||
let thread_state = self.state();
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(state);
|
||||
let _thread_sg = StackGuard::with_top(thread_state, 0);
|
||||
|
||||
let nargs = args.push_into_stack_multi(&lua)?;
|
||||
if nargs > 0 {
|
||||
@@ -163,6 +164,7 @@ impl Thread {
|
||||
pushed_nargs += nargs;
|
||||
}
|
||||
|
||||
let _thread_sg = StackGuard::with_top(thread_state, 0);
|
||||
let (_, nresults) = self.resume_inner(&lua, pushed_nargs)?;
|
||||
check_stack(state, nresults + 1)?;
|
||||
ffi::lua_xmove(thread_state, state, nresults);
|
||||
@@ -190,12 +192,12 @@ impl Thread {
|
||||
let thread_state = self.state();
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(state);
|
||||
let _thread_sg = StackGuard::with_top(thread_state, 0);
|
||||
|
||||
check_stack(state, 1)?;
|
||||
error.push_into_stack(&lua)?;
|
||||
ffi::lua_xmove(state, thread_state, 1);
|
||||
|
||||
let _thread_sg = StackGuard::with_top(thread_state, 0);
|
||||
let (_, nresults) = self.resume_inner(&lua, ffi::LUA_RESUMEERROR)?;
|
||||
check_stack(state, nresults + 1)?;
|
||||
ffi::lua_xmove(thread_state, state, nresults);
|
||||
@@ -263,11 +265,13 @@ impl Thread {
|
||||
/// You can have multiple hooks for different threads.
|
||||
///
|
||||
/// To remove a hook call [`Thread::remove_hook`].
|
||||
///
|
||||
/// [`Lua::set_hook`]: crate::Lua::set_hook
|
||||
#[cfg(not(feature = "luau"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(not(feature = "luau"))))]
|
||||
pub fn set_hook<F>(&self, triggers: HookTriggers, callback: F) -> Result<()>
|
||||
where
|
||||
F: Fn(&crate::Lua, Debug) -> Result<crate::VmState> + crate::MaybeSend + 'static,
|
||||
F: Fn(&crate::Lua, &Debug) -> Result<crate::VmState> + crate::MaybeSend + 'static,
|
||||
{
|
||||
let lua = self.0.lua.lock();
|
||||
unsafe {
|
||||
@@ -305,29 +309,10 @@ impl Thread {
|
||||
pub fn reset(&self, func: Function) -> Result<()> {
|
||||
let lua = self.0.lua.lock();
|
||||
let thread_state = self.state();
|
||||
match self.status_inner(&lua) {
|
||||
ThreadStatusInner::Running => return Err(Error::runtime("cannot reset a running thread")),
|
||||
// Any Lua can reuse new or finished thread
|
||||
ThreadStatusInner::New(_) => unsafe { ffi::lua_settop(thread_state, 0) },
|
||||
ThreadStatusInner::Finished => {}
|
||||
#[cfg(not(any(feature = "lua54", feature = "luau")))]
|
||||
_ => return Err(Error::runtime("cannot reset non-finished thread")),
|
||||
#[cfg(any(feature = "lua54", feature = "luau"))]
|
||||
_ => unsafe {
|
||||
#[cfg(all(feature = "lua54", not(feature = "vendored")))]
|
||||
let status = ffi::lua_resetthread(thread_state);
|
||||
#[cfg(all(feature = "lua54", feature = "vendored"))]
|
||||
let status = ffi::lua_closethread(thread_state, lua.state());
|
||||
#[cfg(feature = "lua54")]
|
||||
if status != ffi::LUA_OK {
|
||||
return Err(pop_error(thread_state, status));
|
||||
}
|
||||
#[cfg(feature = "luau")]
|
||||
ffi::lua_resetthread(thread_state);
|
||||
},
|
||||
}
|
||||
|
||||
unsafe {
|
||||
let status = self.status_inner(&lua);
|
||||
self.reset_inner(status)?;
|
||||
|
||||
// Push function to the top of the thread stack
|
||||
ffi::lua_xpush(lua.ref_thread(), thread_state, func.0.index);
|
||||
|
||||
@@ -342,6 +327,42 @@ impl Thread {
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn reset_inner(&self, status: ThreadStatusInner) -> Result<()> {
|
||||
match status {
|
||||
ThreadStatusInner::New(_) => {
|
||||
// The thread is new, so we can just set the top to 0
|
||||
ffi::lua_settop(self.state(), 0);
|
||||
Ok(())
|
||||
}
|
||||
ThreadStatusInner::Running => Err(Error::runtime("cannot reset a running thread")),
|
||||
ThreadStatusInner::Finished => Ok(()),
|
||||
#[cfg(not(any(feature = "lua54", feature = "luau")))]
|
||||
ThreadStatusInner::Yielded(_) | ThreadStatusInner::Error => {
|
||||
Err(Error::runtime("cannot reset non-finished thread"))
|
||||
}
|
||||
#[cfg(any(feature = "lua54", feature = "luau"))]
|
||||
ThreadStatusInner::Yielded(_) | ThreadStatusInner::Error => {
|
||||
let thread_state = self.state();
|
||||
|
||||
#[cfg(all(feature = "lua54", not(feature = "vendored")))]
|
||||
let status = ffi::lua_resetthread(thread_state);
|
||||
#[cfg(all(feature = "lua54", feature = "vendored"))]
|
||||
let status = {
|
||||
let lua = self.0.lua.lock();
|
||||
ffi::lua_closethread(thread_state, lua.state())
|
||||
};
|
||||
#[cfg(feature = "lua54")]
|
||||
if status != ffi::LUA_OK {
|
||||
return Err(pop_error(thread_state, status));
|
||||
}
|
||||
#[cfg(feature = "luau")]
|
||||
ffi::lua_resetthread(thread_state);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Converts [`Thread`] to an [`AsyncThread`] which implements [`Future`] and [`Stream`] traits.
|
||||
///
|
||||
/// Only resumable threads can be converted to [`AsyncThread`].
|
||||
@@ -453,7 +474,6 @@ impl Thread {
|
||||
/// ```
|
||||
#[cfg(any(feature = "luau", doc))]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
|
||||
#[doc(hidden)]
|
||||
pub fn sandbox(&self) -> Result<()> {
|
||||
let lua = self.0.lua.lock();
|
||||
let state = lua.state();
|
||||
@@ -505,8 +525,22 @@ impl<R> Drop for AsyncThread<R> {
|
||||
fn drop(&mut self) {
|
||||
if self.recycle {
|
||||
if let Some(lua) = self.thread.0.lua.try_lock() {
|
||||
// For Lua 5.4 this also closes all pending to-be-closed variables
|
||||
unsafe { lua.recycle_thread(&mut self.thread) };
|
||||
unsafe {
|
||||
let mut status = self.thread.status_inner(&lua);
|
||||
if matches!(status, ThreadStatusInner::Yielded(0)) {
|
||||
// The thread is dropped while yielded, resume it with the "terminate" signal
|
||||
ffi::lua_pushlightuserdata(self.thread.1, crate::Lua::poll_terminate().0);
|
||||
if let Ok((new_status, _)) = self.thread.resume_inner(&lua, 1) {
|
||||
// `new_status` should always be `ThreadStatusInner::Yielded(0)`
|
||||
status = new_status;
|
||||
}
|
||||
}
|
||||
|
||||
// For Lua 5.4 this also closes all pending to-be-closed variables
|
||||
if self.thread.reset_inner(status).is_ok() {
|
||||
lua.recycle_thread(&mut self.thread);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -570,7 +604,7 @@ impl<R: FromLuaMulti> Future for AsyncThread<R> {
|
||||
|
||||
if status.is_yielded() {
|
||||
if !(nresults == 1 && is_poll_pending(thread_state)) {
|
||||
// Ignore value returned via yield()
|
||||
// Ignore values returned via yield()
|
||||
cx.waker().wake_by_ref();
|
||||
}
|
||||
return Poll::Pending;
|
||||
@@ -601,7 +635,7 @@ struct WakerGuard<'lua, 'a> {
|
||||
impl<'lua, 'a> WakerGuard<'lua, 'a> {
|
||||
#[inline]
|
||||
pub fn new(lua: &'lua RawLua, waker: &'a Waker) -> Result<WakerGuard<'lua, 'a>> {
|
||||
let prev = unsafe { lua.set_waker(NonNull::from(waker)) };
|
||||
let prev = lua.set_waker(NonNull::from(waker));
|
||||
Ok(WakerGuard {
|
||||
lua,
|
||||
prev,
|
||||
@@ -613,7 +647,7 @@ impl<'lua, 'a> WakerGuard<'lua, 'a> {
|
||||
#[cfg(feature = "async")]
|
||||
impl Drop for WakerGuard<'_, '_> {
|
||||
fn drop(&mut self) {
|
||||
unsafe { self.lua.set_waker(self.prev) };
|
||||
self.lua.set_waker(self.prev);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+4
-4
@@ -11,7 +11,7 @@ use crate::util::{check_stack, short_type_name};
|
||||
use crate::value::Value;
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
use std::future::Future;
|
||||
use {crate::function::AsyncCallFuture, std::future::Future};
|
||||
|
||||
/// Trait for types convertible to [`Value`].
|
||||
pub trait IntoLua: Sized {
|
||||
@@ -162,7 +162,7 @@ pub trait ObjectLike: Sealed {
|
||||
/// arguments.
|
||||
#[cfg(feature = "async")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
|
||||
fn call_async<R>(&self, args: impl IntoLuaMulti) -> impl Future<Output = Result<R>>
|
||||
fn call_async<R>(&self, args: impl IntoLuaMulti) -> AsyncCallFuture<R>
|
||||
where
|
||||
R: FromLuaMulti;
|
||||
|
||||
@@ -178,7 +178,7 @@ pub trait ObjectLike: Sealed {
|
||||
/// This might invoke the `__index` metamethod.
|
||||
#[cfg(feature = "async")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
|
||||
fn call_async_method<R>(&self, name: &str, args: impl IntoLuaMulti) -> impl Future<Output = Result<R>>
|
||||
fn call_async_method<R>(&self, name: &str, args: impl IntoLuaMulti) -> AsyncCallFuture<R>
|
||||
where
|
||||
R: FromLuaMulti;
|
||||
|
||||
@@ -196,7 +196,7 @@ pub trait ObjectLike: Sealed {
|
||||
/// This might invoke the `__index` metamethod.
|
||||
#[cfg(feature = "async")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
|
||||
fn call_async_function<R>(&self, name: &str, args: impl IntoLuaMulti) -> impl Future<Output = Result<R>>
|
||||
fn call_async_function<R>(&self, name: &str, args: impl IntoLuaMulti) -> AsyncCallFuture<R>
|
||||
where
|
||||
R: FromLuaMulti;
|
||||
|
||||
|
||||
+8
-5
@@ -1,9 +1,9 @@
|
||||
use std::cell::UnsafeCell;
|
||||
use std::os::raw::{c_int, c_void};
|
||||
|
||||
use crate::error::Result;
|
||||
#[cfg(not(feature = "luau"))]
|
||||
use crate::hook::{Debug, HookTriggers};
|
||||
use crate::debug::{Debug, HookTriggers};
|
||||
use crate::error::Result;
|
||||
use crate::state::{ExtraData, Lua, RawLua};
|
||||
|
||||
// Re-export mutex wrappers
|
||||
@@ -20,6 +20,9 @@ pub use either::Either;
|
||||
pub use registry_key::RegistryKey;
|
||||
pub(crate) use value_ref::ValueRef;
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
pub(crate) use value_ref::ValueRefIndex;
|
||||
|
||||
/// Type of Lua integer numbers.
|
||||
pub type Integer = ffi::lua_Integer;
|
||||
/// Type of Lua floating point numbers.
|
||||
@@ -61,7 +64,7 @@ pub(crate) type AsyncCallback =
|
||||
pub(crate) type AsyncCallbackUpvalue = Upvalue<AsyncCallback>;
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
pub(crate) type AsyncPollUpvalue = Upvalue<BoxFuture<'static, Result<c_int>>>;
|
||||
pub(crate) type AsyncPollUpvalue = Upvalue<Option<BoxFuture<'static, Result<c_int>>>>;
|
||||
|
||||
/// Type to set next Lua VM action after executing interrupt or hook function.
|
||||
pub enum VmState {
|
||||
@@ -79,10 +82,10 @@ pub(crate) enum HookKind {
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "send", not(feature = "luau")))]
|
||||
pub(crate) type HookCallback = XRc<dyn Fn(&Lua, Debug) -> Result<VmState> + Send>;
|
||||
pub(crate) type HookCallback = XRc<dyn Fn(&Lua, &Debug) -> Result<VmState> + Send>;
|
||||
|
||||
#[cfg(all(not(feature = "send"), not(feature = "luau")))]
|
||||
pub(crate) type HookCallback = XRc<dyn Fn(&Lua, Debug) -> Result<VmState>>;
|
||||
pub(crate) type HookCallback = XRc<dyn Fn(&Lua, &Debug) -> Result<VmState>>;
|
||||
|
||||
#[cfg(all(feature = "send", feature = "luau"))]
|
||||
pub(crate) type InterruptCallback = XRc<dyn Fn(&Lua) -> Result<VmState> + Send>;
|
||||
|
||||
@@ -43,7 +43,7 @@ impl AppData {
|
||||
|
||||
#[inline]
|
||||
#[track_caller]
|
||||
pub(crate) fn borrow<T: 'static>(&self, guard: Option<LuaGuard>) -> Option<AppDataRef<T>> {
|
||||
pub(crate) fn borrow<T: 'static>(&self, guard: Option<LuaGuard>) -> Option<AppDataRef<'_, T>> {
|
||||
match self.try_borrow(guard) {
|
||||
Ok(data) => data,
|
||||
Err(err) => panic!("already mutably borrowed: {err:?}"),
|
||||
@@ -53,7 +53,7 @@ impl AppData {
|
||||
pub(crate) fn try_borrow<T: 'static>(
|
||||
&self,
|
||||
guard: Option<LuaGuard>,
|
||||
) -> Result<Option<AppDataRef<T>>, BorrowError> {
|
||||
) -> Result<Option<AppDataRef<'_, T>>, BorrowError> {
|
||||
let data = unsafe { &*self.container.get() }
|
||||
.get(&TypeId::of::<T>())
|
||||
.map(|c| c.try_borrow())
|
||||
@@ -74,7 +74,7 @@ impl AppData {
|
||||
|
||||
#[inline]
|
||||
#[track_caller]
|
||||
pub(crate) fn borrow_mut<T: 'static>(&self, guard: Option<LuaGuard>) -> Option<AppDataRefMut<T>> {
|
||||
pub(crate) fn borrow_mut<T: 'static>(&self, guard: Option<LuaGuard>) -> Option<AppDataRefMut<'_, T>> {
|
||||
match self.try_borrow_mut(guard) {
|
||||
Ok(data) => data,
|
||||
Err(err) => panic!("already borrowed: {err:?}"),
|
||||
@@ -84,7 +84,7 @@ impl AppData {
|
||||
pub(crate) fn try_borrow_mut<T: 'static>(
|
||||
&self,
|
||||
guard: Option<LuaGuard>,
|
||||
) -> Result<Option<AppDataRefMut<T>>, BorrowMutError> {
|
||||
) -> Result<Option<AppDataRefMut<'_, T>>, BorrowMutError> {
|
||||
let data = unsafe { &*self.container.get() }
|
||||
.get(&TypeId::of::<T>())
|
||||
.map(|c| c.try_borrow_mut())
|
||||
|
||||
+1
-1
@@ -31,7 +31,7 @@ mod inner {
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub(crate) fn lock(&self) -> ReentrantMutexGuard<T> {
|
||||
pub(crate) fn lock(&self) -> ReentrantMutexGuard<'_, T> {
|
||||
ReentrantMutexGuard(&self.0)
|
||||
}
|
||||
|
||||
|
||||
+28
-23
@@ -1,22 +1,39 @@
|
||||
use std::fmt;
|
||||
use std::os::raw::{c_int, c_void};
|
||||
|
||||
use super::XRc;
|
||||
use crate::state::{RawLua, WeakLua};
|
||||
|
||||
/// A reference to a Lua (complex) value stored in the Lua auxiliary thread.
|
||||
#[derive(Clone)]
|
||||
pub struct ValueRef {
|
||||
pub(crate) lua: WeakLua,
|
||||
// Keep index separate to avoid additional indirection when accessing it.
|
||||
pub(crate) index: c_int,
|
||||
pub(crate) drop: bool,
|
||||
// If `index_count` is `None`, the value does not need to be destroyed.
|
||||
pub(crate) index_count: Option<ValueRefIndex>,
|
||||
}
|
||||
|
||||
/// A reference to a Lua value index in the auxiliary thread.
|
||||
/// It's cheap to clone and can be used to track the number of references to a value.
|
||||
#[derive(Clone)]
|
||||
pub(crate) struct ValueRefIndex(pub(crate) XRc<c_int>);
|
||||
|
||||
impl From<c_int> for ValueRefIndex {
|
||||
#[inline]
|
||||
fn from(index: c_int) -> Self {
|
||||
ValueRefIndex(XRc::new(index))
|
||||
}
|
||||
}
|
||||
|
||||
impl ValueRef {
|
||||
#[inline]
|
||||
pub(crate) fn new(lua: &RawLua, index: c_int) -> Self {
|
||||
pub(crate) fn new(lua: &RawLua, index: impl Into<ValueRefIndex>) -> Self {
|
||||
let index = index.into();
|
||||
ValueRef {
|
||||
lua: lua.weak().clone(),
|
||||
index,
|
||||
drop: true,
|
||||
index: *index.0,
|
||||
index_count: Some(index),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,16 +42,6 @@ impl ValueRef {
|
||||
let lua = self.lua.lock();
|
||||
unsafe { ffi::lua_topointer(lua.ref_thread(), self.index) }
|
||||
}
|
||||
|
||||
/// Returns a copy of the value, which is valid as long as the original value is held.
|
||||
#[inline]
|
||||
pub(crate) fn copy(&self) -> Self {
|
||||
ValueRef {
|
||||
lua: self.lua.clone(),
|
||||
index: self.index,
|
||||
drop: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for ValueRef {
|
||||
@@ -43,17 +50,15 @@ impl fmt::Debug for ValueRef {
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for ValueRef {
|
||||
fn clone(&self) -> Self {
|
||||
unsafe { self.lua.lock().clone_ref(self) }
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for ValueRef {
|
||||
fn drop(&mut self) {
|
||||
if self.drop {
|
||||
if let Some(lua) = self.lua.try_lock() {
|
||||
unsafe { lua.drop_ref(self) };
|
||||
if let Some(ValueRefIndex(index)) = self.index_count.take() {
|
||||
// It's guaranteed that the inner value returns exactly once.
|
||||
// This means in particular that the value is not dropped.
|
||||
if XRc::into_inner(index).is_some() {
|
||||
if let Some(lua) = self.lua.try_lock() {
|
||||
unsafe { lua.drop_ref(self) };
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+50
-48
@@ -18,7 +18,7 @@ use crate::value::Value;
|
||||
#[cfg(feature = "async")]
|
||||
use std::future::Future;
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
#[cfg(feature = "serde")]
|
||||
use {
|
||||
serde::ser::{self, Serialize, Serializer},
|
||||
std::result::Result as StdResult,
|
||||
@@ -240,6 +240,13 @@ impl AsRef<str> for MetaMethod {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<MetaMethod> for StdString {
|
||||
#[inline]
|
||||
fn from(method: MetaMethod) -> Self {
|
||||
method.name().to_owned()
|
||||
}
|
||||
}
|
||||
|
||||
/// Method registry for [`UserData`] implementors.
|
||||
pub trait UserDataMethods<T> {
|
||||
/// Add a regular method which accepts a `&T` as the first parameter.
|
||||
@@ -249,7 +256,7 @@ pub trait UserDataMethods<T> {
|
||||
///
|
||||
/// If `add_meta_method` is used to set the `__index` metamethod, the `__index` metamethod will
|
||||
/// be used as a fall-back if no regular method is found.
|
||||
fn add_method<M, A, R>(&mut self, name: impl ToString, method: M)
|
||||
fn add_method<M, A, R>(&mut self, name: impl Into<StdString>, method: M)
|
||||
where
|
||||
M: Fn(&Lua, &T, A) -> Result<R> + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
@@ -260,7 +267,7 @@ pub trait UserDataMethods<T> {
|
||||
/// Refer to [`add_method`] for more information about the implementation.
|
||||
///
|
||||
/// [`add_method`]: UserDataMethods::add_method
|
||||
fn add_method_mut<M, A, R>(&mut self, name: impl ToString, method: M)
|
||||
fn add_method_mut<M, A, R>(&mut self, name: impl Into<StdString>, method: M)
|
||||
where
|
||||
M: FnMut(&Lua, &mut T, A) -> Result<R> + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
@@ -273,7 +280,7 @@ pub trait UserDataMethods<T> {
|
||||
/// [`add_method`]: UserDataMethods::add_method
|
||||
#[cfg(feature = "async")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
|
||||
fn add_async_method<M, A, MR, R>(&mut self, name: impl ToString, method: M)
|
||||
fn add_async_method<M, A, MR, R>(&mut self, name: impl Into<StdString>, method: M)
|
||||
where
|
||||
T: 'static,
|
||||
M: Fn(Lua, UserDataRef<T>, A) -> MR + MaybeSend + 'static,
|
||||
@@ -288,7 +295,7 @@ pub trait UserDataMethods<T> {
|
||||
/// [`add_method`]: UserDataMethods::add_method
|
||||
#[cfg(feature = "async")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
|
||||
fn add_async_method_mut<M, A, MR, R>(&mut self, name: impl ToString, method: M)
|
||||
fn add_async_method_mut<M, A, MR, R>(&mut self, name: impl Into<StdString>, method: M)
|
||||
where
|
||||
T: 'static,
|
||||
M: Fn(Lua, UserDataRefMut<T>, A) -> MR + MaybeSend + 'static,
|
||||
@@ -301,7 +308,7 @@ pub trait UserDataMethods<T> {
|
||||
/// The first argument will be a [`AnyUserData`] of type `T` if the method is called with Lua
|
||||
/// method syntax: `my_userdata:my_method(arg1, arg2)`, or it is passed in as the first
|
||||
/// argument: `my_userdata.my_method(my_userdata, arg1, arg2)`.
|
||||
fn add_function<F, A, R>(&mut self, name: impl ToString, function: F)
|
||||
fn add_function<F, A, R>(&mut self, name: impl Into<StdString>, function: F)
|
||||
where
|
||||
F: Fn(&Lua, A) -> Result<R> + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
@@ -312,7 +319,7 @@ pub trait UserDataMethods<T> {
|
||||
/// This is a version of [`add_function`] that accepts a `FnMut` argument.
|
||||
///
|
||||
/// [`add_function`]: UserDataMethods::add_function
|
||||
fn add_function_mut<F, A, R>(&mut self, name: impl ToString, function: F)
|
||||
fn add_function_mut<F, A, R>(&mut self, name: impl Into<StdString>, function: F)
|
||||
where
|
||||
F: FnMut(&Lua, A) -> Result<R> + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
@@ -326,7 +333,7 @@ pub trait UserDataMethods<T> {
|
||||
/// [`add_function`]: UserDataMethods::add_function
|
||||
#[cfg(feature = "async")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
|
||||
fn add_async_function<F, A, FR, R>(&mut self, name: impl ToString, function: F)
|
||||
fn add_async_function<F, A, FR, R>(&mut self, name: impl Into<StdString>, function: F)
|
||||
where
|
||||
F: Fn(Lua, A) -> FR + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
@@ -341,7 +348,7 @@ pub trait UserDataMethods<T> {
|
||||
/// side has a metatable. To prevent this, use [`add_meta_function`].
|
||||
///
|
||||
/// [`add_meta_function`]: UserDataMethods::add_meta_function
|
||||
fn add_meta_method<M, A, R>(&mut self, name: impl ToString, method: M)
|
||||
fn add_meta_method<M, A, R>(&mut self, name: impl Into<StdString>, method: M)
|
||||
where
|
||||
M: Fn(&Lua, &T, A) -> Result<R> + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
@@ -355,7 +362,7 @@ pub trait UserDataMethods<T> {
|
||||
/// side has a metatable. To prevent this, use [`add_meta_function`].
|
||||
///
|
||||
/// [`add_meta_function`]: UserDataMethods::add_meta_function
|
||||
fn add_meta_method_mut<M, A, R>(&mut self, name: impl ToString, method: M)
|
||||
fn add_meta_method_mut<M, A, R>(&mut self, name: impl Into<StdString>, method: M)
|
||||
where
|
||||
M: FnMut(&Lua, &mut T, A) -> Result<R> + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
@@ -371,7 +378,7 @@ pub trait UserDataMethods<T> {
|
||||
docsrs,
|
||||
doc(cfg(all(feature = "async", not(any(feature = "lua51", feature = "luau")))))
|
||||
)]
|
||||
fn add_async_meta_method<M, A, MR, R>(&mut self, name: impl ToString, method: M)
|
||||
fn add_async_meta_method<M, A, MR, R>(&mut self, name: impl Into<StdString>, method: M)
|
||||
where
|
||||
T: 'static,
|
||||
M: Fn(Lua, UserDataRef<T>, A) -> MR + MaybeSend + 'static,
|
||||
@@ -387,7 +394,7 @@ pub trait UserDataMethods<T> {
|
||||
/// [`add_meta_method_mut`]: UserDataMethods::add_meta_method_mut
|
||||
#[cfg(all(feature = "async", not(any(feature = "lua51", feature = "luau"))))]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
|
||||
fn add_async_meta_method_mut<M, A, MR, R>(&mut self, name: impl ToString, method: M)
|
||||
fn add_async_meta_method_mut<M, A, MR, R>(&mut self, name: impl Into<StdString>, method: M)
|
||||
where
|
||||
T: 'static,
|
||||
M: Fn(Lua, UserDataRefMut<T>, A) -> MR + MaybeSend + 'static,
|
||||
@@ -400,7 +407,7 @@ pub trait UserDataMethods<T> {
|
||||
/// Metamethods for binary operators can be triggered if either the left or right argument to
|
||||
/// the binary operator has a metatable, so the first argument here is not necessarily a
|
||||
/// userdata of type `T`.
|
||||
fn add_meta_function<F, A, R>(&mut self, name: impl ToString, function: F)
|
||||
fn add_meta_function<F, A, R>(&mut self, name: impl Into<StdString>, function: F)
|
||||
where
|
||||
F: Fn(&Lua, A) -> Result<R> + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
@@ -411,7 +418,7 @@ pub trait UserDataMethods<T> {
|
||||
/// This is a version of [`add_meta_function`] that accepts a `FnMut` argument.
|
||||
///
|
||||
/// [`add_meta_function`]: UserDataMethods::add_meta_function
|
||||
fn add_meta_function_mut<F, A, R>(&mut self, name: impl ToString, function: F)
|
||||
fn add_meta_function_mut<F, A, R>(&mut self, name: impl Into<StdString>, function: F)
|
||||
where
|
||||
F: FnMut(&Lua, A) -> Result<R> + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
@@ -427,7 +434,7 @@ pub trait UserDataMethods<T> {
|
||||
docsrs,
|
||||
doc(cfg(all(feature = "async", not(any(feature = "lua51", feature = "luau")))))
|
||||
)]
|
||||
fn add_async_meta_function<F, A, FR, R>(&mut self, name: impl ToString, function: F)
|
||||
fn add_async_meta_function<F, A, FR, R>(&mut self, name: impl Into<StdString>, function: F)
|
||||
where
|
||||
F: Fn(Lua, A) -> FR + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
@@ -446,7 +453,7 @@ pub trait UserDataFields<T> {
|
||||
///
|
||||
/// If `add_meta_method` is used to set the `__index` metamethod, it will
|
||||
/// be used as a fall-back if no regular field or method are found.
|
||||
fn add_field<V>(&mut self, name: impl ToString, value: V)
|
||||
fn add_field<V>(&mut self, name: impl Into<StdString>, value: V)
|
||||
where
|
||||
V: IntoLua + 'static;
|
||||
|
||||
@@ -457,7 +464,7 @@ pub trait UserDataFields<T> {
|
||||
///
|
||||
/// If `add_meta_method` is used to set the `__index` metamethod, the `__index` metamethod will
|
||||
/// be used as a fall-back if no regular field or method are found.
|
||||
fn add_field_method_get<M, R>(&mut self, name: impl ToString, method: M)
|
||||
fn add_field_method_get<M, R>(&mut self, name: impl Into<StdString>, method: M)
|
||||
where
|
||||
M: Fn(&Lua, &T) -> Result<R> + MaybeSend + 'static,
|
||||
R: IntoLua;
|
||||
@@ -470,21 +477,21 @@ pub trait UserDataFields<T> {
|
||||
///
|
||||
/// If `add_meta_method` is used to set the `__newindex` metamethod, the `__newindex` metamethod
|
||||
/// will be used as a fall-back if no regular field is found.
|
||||
fn add_field_method_set<M, A>(&mut self, name: impl ToString, method: M)
|
||||
fn add_field_method_set<M, A>(&mut self, name: impl Into<StdString>, method: M)
|
||||
where
|
||||
M: FnMut(&Lua, &mut T, A) -> Result<()> + MaybeSend + 'static,
|
||||
A: FromLua;
|
||||
|
||||
/// Add a regular field getter as a function which accepts a generic [`AnyUserData`] of type `T`
|
||||
/// argument.
|
||||
fn add_field_function_get<F, R>(&mut self, name: impl ToString, function: F)
|
||||
fn add_field_function_get<F, R>(&mut self, name: impl Into<StdString>, function: F)
|
||||
where
|
||||
F: Fn(&Lua, AnyUserData) -> Result<R> + MaybeSend + 'static,
|
||||
R: IntoLua;
|
||||
|
||||
/// Add a regular field setter as a function which accepts a generic [`AnyUserData`] of type `T`
|
||||
/// first argument.
|
||||
fn add_field_function_set<F, A>(&mut self, name: impl ToString, function: F)
|
||||
fn add_field_function_set<F, A>(&mut self, name: impl Into<StdString>, function: F)
|
||||
where
|
||||
F: FnMut(&Lua, AnyUserData, A) -> Result<()> + MaybeSend + 'static,
|
||||
A: FromLua;
|
||||
@@ -497,7 +504,7 @@ pub trait UserDataFields<T> {
|
||||
///
|
||||
/// `mlua` will trigger an error on an attempt to define a protected metamethod,
|
||||
/// like `__gc` or `__metatable`.
|
||||
fn add_meta_field<V>(&mut self, name: impl ToString, value: V)
|
||||
fn add_meta_field<V>(&mut self, name: impl Into<StdString>, value: V)
|
||||
where
|
||||
V: IntoLua + 'static;
|
||||
|
||||
@@ -509,7 +516,7 @@ pub trait UserDataFields<T> {
|
||||
///
|
||||
/// `mlua` will trigger an error on an attempt to define a protected metamethod,
|
||||
/// like `__gc` or `__metatable`.
|
||||
fn add_meta_field_with<F, R>(&mut self, name: impl ToString, f: F)
|
||||
fn add_meta_field_with<F, R>(&mut self, name: impl Into<StdString>, f: F)
|
||||
where
|
||||
F: FnOnce(&Lua) -> Result<R> + 'static,
|
||||
R: IntoLua;
|
||||
@@ -683,22 +690,16 @@ impl AnyUserData {
|
||||
/// Keeps associated user values unchanged (they will be collected by Lua's GC).
|
||||
pub fn take<T: 'static>(&self) -> Result<T> {
|
||||
let lua = self.0.lua.lock();
|
||||
let state = lua.state();
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(state);
|
||||
check_stack(state, 2)?;
|
||||
|
||||
let type_id = lua.push_userdata_ref(&self.0)?;
|
||||
match type_id {
|
||||
Some(type_id) if type_id == TypeId::of::<T>() => {
|
||||
if (*get_userdata::<UserDataStorage<T>>(state, -1)).has_exclusive_access() {
|
||||
take_userdata::<UserDataStorage<T>>(state).into_inner()
|
||||
} else {
|
||||
Err(Error::UserDataBorrowMutError)
|
||||
}
|
||||
match lua.get_userdata_ref_type_id(&self.0)? {
|
||||
Some(type_id) if type_id == TypeId::of::<T>() => unsafe {
|
||||
let ref_thread = lua.ref_thread();
|
||||
if (*get_userdata::<UserDataStorage<T>>(ref_thread, self.0.index)).has_exclusive_access() {
|
||||
take_userdata::<UserDataStorage<T>>(ref_thread, self.0.index).into_inner()
|
||||
} else {
|
||||
Err(Error::UserDataBorrowMutError)
|
||||
}
|
||||
_ => Err(Error::UserDataTypeMismatch),
|
||||
}
|
||||
},
|
||||
_ => Err(Error::UserDataTypeMismatch),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -887,16 +888,17 @@ impl AnyUserData {
|
||||
self.raw_metatable().map(UserDataMetatable)
|
||||
}
|
||||
|
||||
/// Returns a raw metatable of this [`AnyUserData`].
|
||||
fn raw_metatable(&self) -> Result<Table> {
|
||||
let lua = self.0.lua.lock();
|
||||
let state = lua.state();
|
||||
let ref_thread = lua.ref_thread();
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(state);
|
||||
check_stack(state, 3)?;
|
||||
// Check that userdata is registered and not destructed
|
||||
// All registered userdata types have a non-empty metatable
|
||||
let _type_id = lua.get_userdata_ref_type_id(&self.0)?;
|
||||
|
||||
lua.push_userdata_ref(&self.0)?;
|
||||
ffi::lua_getmetatable(state, -1); // Checked that non-empty on the previous call
|
||||
Ok(Table(lua.pop_ref()))
|
||||
ffi::lua_getmetatable(ref_thread, self.0.index);
|
||||
Ok(Table(lua.pop_ref_thread()))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -963,7 +965,7 @@ impl AnyUserData {
|
||||
|
||||
/// Returns `true` if this [`AnyUserData`] is serializable (e.g. was created using
|
||||
/// [`Lua::create_ser_userdata`]).
|
||||
#[cfg(feature = "serialize")]
|
||||
#[cfg(feature = "serde")]
|
||||
pub(crate) fn is_serializable(&self) -> bool {
|
||||
let lua = self.0.lua.lock();
|
||||
let is_serializable = || unsafe {
|
||||
@@ -1014,7 +1016,7 @@ impl UserDataMetatable {
|
||||
/// The pairs are wrapped in a [`Result`], since they are lazily converted to `V` type.
|
||||
///
|
||||
/// [`Result`]: crate::Result
|
||||
pub fn pairs<V: FromLua>(&self) -> UserDataMetatablePairs<V> {
|
||||
pub fn pairs<V: FromLua>(&self) -> UserDataMetatablePairs<'_, V> {
|
||||
UserDataMetatablePairs(self.0.pairs())
|
||||
}
|
||||
}
|
||||
@@ -1047,7 +1049,7 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
#[cfg(feature = "serde")]
|
||||
impl Serialize for AnyUserData {
|
||||
fn serialize<S>(&self, serializer: S) -> StdResult<S::Ok, S::Error>
|
||||
where
|
||||
@@ -1078,8 +1080,8 @@ impl AnyUserData {
|
||||
/// [`IntoLua`] trait.
|
||||
///
|
||||
/// This function uses [`Lua::create_ser_any_userdata`] under the hood.
|
||||
#[cfg(feature = "serialize")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "serialize")))]
|
||||
#[cfg(feature = "serde")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "serde")))]
|
||||
pub fn wrap_ser<T: Serialize + MaybeSend + 'static>(data: T) -> impl IntoLua {
|
||||
WrappedUserdata(move |lua| lua.create_ser_any_userdata(data))
|
||||
}
|
||||
|
||||
+12
-12
@@ -1,6 +1,6 @@
|
||||
use std::cell::{RefCell, UnsafeCell};
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
#[cfg(feature = "serde")]
|
||||
use serde::ser::{Serialize, Serializer};
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
@@ -9,10 +9,10 @@ use crate::types::XRc;
|
||||
use super::lock::{RawLock, UserDataLock};
|
||||
use super::r#ref::{UserDataRef, UserDataRefMut};
|
||||
|
||||
#[cfg(all(feature = "serialize", not(feature = "send")))]
|
||||
#[cfg(all(feature = "serde", not(feature = "send")))]
|
||||
type DynSerialize = dyn erased_serde::Serialize;
|
||||
|
||||
#[cfg(all(feature = "serialize", feature = "send"))]
|
||||
#[cfg(all(feature = "serde", feature = "send"))]
|
||||
type DynSerialize = dyn erased_serde::Serialize + Send;
|
||||
|
||||
pub(crate) enum UserDataStorage<T> {
|
||||
@@ -24,7 +24,7 @@ pub(crate) enum UserDataStorage<T> {
|
||||
// It's stored inside a Lua VM and protected by the outer `ReentrantMutex`.
|
||||
pub(crate) enum UserDataVariant<T> {
|
||||
Default(XRc<UserDataCell<T>>),
|
||||
#[cfg(feature = "serialize")]
|
||||
#[cfg(feature = "serde")]
|
||||
Serializable(XRc<UserDataCell<Box<DynSerialize>>>, bool), // bool is `is_sync`
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ impl<T> Clone for UserDataVariant<T> {
|
||||
fn clone(&self) -> Self {
|
||||
match self {
|
||||
Self::Default(inner) => Self::Default(XRc::clone(inner)),
|
||||
#[cfg(feature = "serialize")]
|
||||
#[cfg(feature = "serde")]
|
||||
Self::Serializable(inner, is_sync) => Self::Serializable(XRc::clone(inner), *is_sync),
|
||||
}
|
||||
}
|
||||
@@ -79,7 +79,7 @@ impl<T> UserDataVariant<T> {
|
||||
}
|
||||
Ok(match self {
|
||||
Self::Default(inner) => XRc::into_inner(inner).unwrap().value.into_inner(),
|
||||
#[cfg(feature = "serialize")]
|
||||
#[cfg(feature = "serde")]
|
||||
Self::Serializable(inner, _) => unsafe {
|
||||
let raw = Box::into_raw(XRc::into_inner(inner).unwrap().value.into_inner());
|
||||
*Box::from_raw(raw as *mut T)
|
||||
@@ -91,7 +91,7 @@ impl<T> UserDataVariant<T> {
|
||||
fn strong_count(&self) -> usize {
|
||||
match self {
|
||||
Self::Default(inner) => XRc::strong_count(inner),
|
||||
#[cfg(feature = "serialize")]
|
||||
#[cfg(feature = "serde")]
|
||||
Self::Serializable(inner, _) => XRc::strong_count(inner),
|
||||
}
|
||||
}
|
||||
@@ -100,7 +100,7 @@ impl<T> UserDataVariant<T> {
|
||||
pub(super) fn raw_lock(&self) -> &RawLock {
|
||||
match self {
|
||||
Self::Default(inner) => &inner.raw_lock,
|
||||
#[cfg(feature = "serialize")]
|
||||
#[cfg(feature = "serde")]
|
||||
Self::Serializable(inner, _) => &inner.raw_lock,
|
||||
}
|
||||
}
|
||||
@@ -109,13 +109,13 @@ impl<T> UserDataVariant<T> {
|
||||
pub(super) fn as_ptr(&self) -> *mut T {
|
||||
match self {
|
||||
Self::Default(inner) => inner.value.get(),
|
||||
#[cfg(feature = "serialize")]
|
||||
#[cfg(feature = "serde")]
|
||||
Self::Serializable(inner, _) => unsafe { &mut **(inner.value.get() as *mut Box<T>) },
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
#[cfg(feature = "serde")]
|
||||
impl Serialize for UserDataStorage<()> {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error> {
|
||||
match self {
|
||||
@@ -197,7 +197,7 @@ impl<T: 'static> UserDataStorage<T> {
|
||||
Self::Scoped(ScopedUserDataVariant::RefMut(RefCell::new(data)))
|
||||
}
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
#[cfg(feature = "serde")]
|
||||
#[inline(always)]
|
||||
pub(crate) fn new_ser(data: T) -> Self
|
||||
where
|
||||
@@ -209,7 +209,7 @@ impl<T: 'static> UserDataStorage<T> {
|
||||
Self::Owned(variant)
|
||||
}
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
#[cfg(feature = "serde")]
|
||||
#[inline(always)]
|
||||
pub(crate) fn is_serializable(&self) -> bool {
|
||||
matches!(self, Self::Owned(UserDataVariant::Serializable(..)))
|
||||
|
||||
+12
-12
@@ -8,21 +8,21 @@ use crate::value::Value;
|
||||
use crate::Function;
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
use futures_util::future::{self, Either, Future};
|
||||
use crate::function::AsyncCallFuture;
|
||||
|
||||
impl ObjectLike for AnyUserData {
|
||||
#[inline]
|
||||
fn get<V: FromLua>(&self, key: impl IntoLua) -> Result<V> {
|
||||
// `lua_gettable` method used under the hood can work with any Lua value
|
||||
// that has `__index` metamethod
|
||||
Table(self.0.copy()).get_protected(key)
|
||||
Table(self.0.clone()).get_protected(key)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn set(&self, key: impl IntoLua, value: impl IntoLua) -> Result<()> {
|
||||
// `lua_settable` method used under the hood can work with any Lua value
|
||||
// that has `__newindex` metamethod
|
||||
Table(self.0.copy()).set_protected(key, value)
|
||||
Table(self.0.clone()).set_protected(key, value)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@@ -30,16 +30,16 @@ impl ObjectLike for AnyUserData {
|
||||
where
|
||||
R: FromLuaMulti,
|
||||
{
|
||||
Function(self.0.copy()).call(args)
|
||||
Function(self.0.clone()).call(args)
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
#[inline]
|
||||
fn call_async<R>(&self, args: impl IntoLuaMulti) -> impl Future<Output = Result<R>>
|
||||
fn call_async<R>(&self, args: impl IntoLuaMulti) -> AsyncCallFuture<R>
|
||||
where
|
||||
R: FromLuaMulti,
|
||||
{
|
||||
Function(self.0.copy()).call_async(args)
|
||||
Function(self.0.clone()).call_async(args)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@@ -51,7 +51,7 @@ impl ObjectLike for AnyUserData {
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
fn call_async_method<R>(&self, name: &str, args: impl IntoLuaMulti) -> impl Future<Output = Result<R>>
|
||||
fn call_async_method<R>(&self, name: &str, args: impl IntoLuaMulti) -> AsyncCallFuture<R>
|
||||
where
|
||||
R: FromLuaMulti,
|
||||
{
|
||||
@@ -72,22 +72,22 @@ impl ObjectLike for AnyUserData {
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
fn call_async_function<R>(&self, name: &str, args: impl IntoLuaMulti) -> impl Future<Output = Result<R>>
|
||||
fn call_async_function<R>(&self, name: &str, args: impl IntoLuaMulti) -> AsyncCallFuture<R>
|
||||
where
|
||||
R: FromLuaMulti,
|
||||
{
|
||||
match self.get(name) {
|
||||
Ok(Value::Function(func)) => Either::Left(func.call_async(args)),
|
||||
Ok(Value::Function(func)) => func.call_async(args),
|
||||
Ok(val) => {
|
||||
let msg = format!("attempt to call a {} value (function '{name}')", val.type_name());
|
||||
Either::Right(future::ready(Err(Error::RuntimeError(msg))))
|
||||
AsyncCallFuture::error(Error::RuntimeError(msg))
|
||||
}
|
||||
Err(err) => Either::Right(future::ready(Err(err))),
|
||||
Err(err) => AsyncCallFuture::error(err),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn to_string(&self) -> Result<StdString> {
|
||||
Value::UserData(AnyUserData(self.0.copy())).to_string()
|
||||
Value::UserData(AnyUserData(self.0.clone())).to_string()
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -63,7 +63,7 @@ impl<T> TryFrom<UserDataVariant<T>> for UserDataRef<T> {
|
||||
|
||||
#[inline]
|
||||
fn try_from(variant: UserDataVariant<T>) -> Result<Self> {
|
||||
let guard = if !cfg!(feature = "send") || is_sync::<T>() {
|
||||
let guard = if cfg!(not(feature = "send")) || is_sync::<T>() {
|
||||
variant.raw_lock().try_lock_shared_guarded()
|
||||
} else {
|
||||
variant.raw_lock().try_lock_exclusive_guarded()
|
||||
|
||||
+42
-42
@@ -365,101 +365,101 @@ fn get_function_name<T>(name: &str) -> StdString {
|
||||
}
|
||||
|
||||
impl<T> UserDataFields<T> for UserDataRegistry<T> {
|
||||
fn add_field<V>(&mut self, name: impl ToString, value: V)
|
||||
fn add_field<V>(&mut self, name: impl Into<StdString>, value: V)
|
||||
where
|
||||
V: IntoLua + 'static,
|
||||
{
|
||||
let name = name.to_string();
|
||||
let name = name.into();
|
||||
self.raw.fields.push((name, value.into_lua(self.lua.lua())));
|
||||
}
|
||||
|
||||
fn add_field_method_get<M, R>(&mut self, name: impl ToString, method: M)
|
||||
fn add_field_method_get<M, R>(&mut self, name: impl Into<StdString>, method: M)
|
||||
where
|
||||
M: Fn(&Lua, &T) -> Result<R> + MaybeSend + 'static,
|
||||
R: IntoLua,
|
||||
{
|
||||
let name = name.to_string();
|
||||
let name = name.into();
|
||||
let callback = self.box_method(&name, move |lua, data, ()| method(lua, data));
|
||||
self.raw.field_getters.push((name, callback));
|
||||
}
|
||||
|
||||
fn add_field_method_set<M, A>(&mut self, name: impl ToString, method: M)
|
||||
fn add_field_method_set<M, A>(&mut self, name: impl Into<StdString>, method: M)
|
||||
where
|
||||
M: FnMut(&Lua, &mut T, A) -> Result<()> + MaybeSend + 'static,
|
||||
A: FromLua,
|
||||
{
|
||||
let name = name.to_string();
|
||||
let name = name.into();
|
||||
let callback = self.box_method_mut(&name, method);
|
||||
self.raw.field_setters.push((name, callback));
|
||||
}
|
||||
|
||||
fn add_field_function_get<F, R>(&mut self, name: impl ToString, function: F)
|
||||
fn add_field_function_get<F, R>(&mut self, name: impl Into<StdString>, function: F)
|
||||
where
|
||||
F: Fn(&Lua, AnyUserData) -> Result<R> + MaybeSend + 'static,
|
||||
R: IntoLua,
|
||||
{
|
||||
let name = name.to_string();
|
||||
let name = name.into();
|
||||
let callback = self.box_function(&name, function);
|
||||
self.raw.field_getters.push((name, callback));
|
||||
}
|
||||
|
||||
fn add_field_function_set<F, A>(&mut self, name: impl ToString, mut function: F)
|
||||
fn add_field_function_set<F, A>(&mut self, name: impl Into<StdString>, mut function: F)
|
||||
where
|
||||
F: FnMut(&Lua, AnyUserData, A) -> Result<()> + MaybeSend + 'static,
|
||||
A: FromLua,
|
||||
{
|
||||
let name = name.to_string();
|
||||
let name = name.into();
|
||||
let callback = self.box_function_mut(&name, move |lua, (data, val)| function(lua, data, val));
|
||||
self.raw.field_setters.push((name, callback));
|
||||
}
|
||||
|
||||
fn add_meta_field<V>(&mut self, name: impl ToString, value: V)
|
||||
fn add_meta_field<V>(&mut self, name: impl Into<StdString>, value: V)
|
||||
where
|
||||
V: IntoLua + 'static,
|
||||
{
|
||||
let lua = self.lua.lua();
|
||||
let name = name.to_string();
|
||||
let name = name.into();
|
||||
let field = Self::check_meta_field(lua, &name, value).and_then(|v| v.into_lua(lua));
|
||||
self.raw.meta_fields.push((name, field));
|
||||
}
|
||||
|
||||
fn add_meta_field_with<F, R>(&mut self, name: impl ToString, f: F)
|
||||
fn add_meta_field_with<F, R>(&mut self, name: impl Into<StdString>, f: F)
|
||||
where
|
||||
F: FnOnce(&Lua) -> Result<R> + 'static,
|
||||
R: IntoLua,
|
||||
{
|
||||
let lua = self.lua.lua();
|
||||
let name = name.to_string();
|
||||
let name = name.into();
|
||||
let field = f(lua).and_then(|v| Self::check_meta_field(lua, &name, v).and_then(|v| v.into_lua(lua)));
|
||||
self.raw.meta_fields.push((name, field));
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> UserDataMethods<T> for UserDataRegistry<T> {
|
||||
fn add_method<M, A, R>(&mut self, name: impl ToString, method: M)
|
||||
fn add_method<M, A, R>(&mut self, name: impl Into<StdString>, method: M)
|
||||
where
|
||||
M: Fn(&Lua, &T, A) -> Result<R> + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
R: IntoLuaMulti,
|
||||
{
|
||||
let name = name.to_string();
|
||||
let name = name.into();
|
||||
let callback = self.box_method(&name, method);
|
||||
self.raw.methods.push((name, callback));
|
||||
}
|
||||
|
||||
fn add_method_mut<M, A, R>(&mut self, name: impl ToString, method: M)
|
||||
fn add_method_mut<M, A, R>(&mut self, name: impl Into<StdString>, method: M)
|
||||
where
|
||||
M: FnMut(&Lua, &mut T, A) -> Result<R> + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
R: IntoLuaMulti,
|
||||
{
|
||||
let name = name.to_string();
|
||||
let name = name.into();
|
||||
let callback = self.box_method_mut(&name, method);
|
||||
self.raw.methods.push((name, callback));
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
fn add_async_method<M, A, MR, R>(&mut self, name: impl ToString, method: M)
|
||||
fn add_async_method<M, A, MR, R>(&mut self, name: impl Into<StdString>, method: M)
|
||||
where
|
||||
T: 'static,
|
||||
M: Fn(Lua, UserDataRef<T>, A) -> MR + MaybeSend + 'static,
|
||||
@@ -467,13 +467,13 @@ impl<T> UserDataMethods<T> for UserDataRegistry<T> {
|
||||
MR: Future<Output = Result<R>> + MaybeSend + 'static,
|
||||
R: IntoLuaMulti,
|
||||
{
|
||||
let name = name.to_string();
|
||||
let name = name.into();
|
||||
let callback = self.box_async_method(&name, method);
|
||||
self.raw.async_methods.push((name, callback));
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
fn add_async_method_mut<M, A, MR, R>(&mut self, name: impl ToString, method: M)
|
||||
fn add_async_method_mut<M, A, MR, R>(&mut self, name: impl Into<StdString>, method: M)
|
||||
where
|
||||
T: 'static,
|
||||
M: Fn(Lua, UserDataRefMut<T>, A) -> MR + MaybeSend + 'static,
|
||||
@@ -481,70 +481,70 @@ impl<T> UserDataMethods<T> for UserDataRegistry<T> {
|
||||
MR: Future<Output = Result<R>> + MaybeSend + 'static,
|
||||
R: IntoLuaMulti,
|
||||
{
|
||||
let name = name.to_string();
|
||||
let name = name.into();
|
||||
let callback = self.box_async_method_mut(&name, method);
|
||||
self.raw.async_methods.push((name, callback));
|
||||
}
|
||||
|
||||
fn add_function<F, A, R>(&mut self, name: impl ToString, function: F)
|
||||
fn add_function<F, A, R>(&mut self, name: impl Into<StdString>, function: F)
|
||||
where
|
||||
F: Fn(&Lua, A) -> Result<R> + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
R: IntoLuaMulti,
|
||||
{
|
||||
let name = name.to_string();
|
||||
let name = name.into();
|
||||
let callback = self.box_function(&name, function);
|
||||
self.raw.methods.push((name, callback));
|
||||
}
|
||||
|
||||
fn add_function_mut<F, A, R>(&mut self, name: impl ToString, function: F)
|
||||
fn add_function_mut<F, A, R>(&mut self, name: impl Into<StdString>, function: F)
|
||||
where
|
||||
F: FnMut(&Lua, A) -> Result<R> + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
R: IntoLuaMulti,
|
||||
{
|
||||
let name = name.to_string();
|
||||
let name = name.into();
|
||||
let callback = self.box_function_mut(&name, function);
|
||||
self.raw.methods.push((name, callback));
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
fn add_async_function<F, A, FR, R>(&mut self, name: impl ToString, function: F)
|
||||
fn add_async_function<F, A, FR, R>(&mut self, name: impl Into<StdString>, function: F)
|
||||
where
|
||||
F: Fn(Lua, A) -> FR + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
FR: Future<Output = Result<R>> + MaybeSend + 'static,
|
||||
R: IntoLuaMulti,
|
||||
{
|
||||
let name = name.to_string();
|
||||
let name = name.into();
|
||||
let callback = self.box_async_function(&name, function);
|
||||
self.raw.async_methods.push((name, callback));
|
||||
}
|
||||
|
||||
fn add_meta_method<M, A, R>(&mut self, name: impl ToString, method: M)
|
||||
fn add_meta_method<M, A, R>(&mut self, name: impl Into<StdString>, method: M)
|
||||
where
|
||||
M: Fn(&Lua, &T, A) -> Result<R> + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
R: IntoLuaMulti,
|
||||
{
|
||||
let name = name.to_string();
|
||||
let name = name.into();
|
||||
let callback = self.box_method(&name, method);
|
||||
self.raw.meta_methods.push((name, callback));
|
||||
}
|
||||
|
||||
fn add_meta_method_mut<M, A, R>(&mut self, name: impl ToString, method: M)
|
||||
fn add_meta_method_mut<M, A, R>(&mut self, name: impl Into<StdString>, method: M)
|
||||
where
|
||||
M: FnMut(&Lua, &mut T, A) -> Result<R> + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
R: IntoLuaMulti,
|
||||
{
|
||||
let name = name.to_string();
|
||||
let name = name.into();
|
||||
let callback = self.box_method_mut(&name, method);
|
||||
self.raw.meta_methods.push((name, callback));
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "async", not(any(feature = "lua51", feature = "luau"))))]
|
||||
fn add_async_meta_method<M, A, MR, R>(&mut self, name: impl ToString, method: M)
|
||||
fn add_async_meta_method<M, A, MR, R>(&mut self, name: impl Into<StdString>, method: M)
|
||||
where
|
||||
T: 'static,
|
||||
M: Fn(Lua, UserDataRef<T>, A) -> MR + MaybeSend + 'static,
|
||||
@@ -552,13 +552,13 @@ impl<T> UserDataMethods<T> for UserDataRegistry<T> {
|
||||
MR: Future<Output = Result<R>> + MaybeSend + 'static,
|
||||
R: IntoLuaMulti,
|
||||
{
|
||||
let name = name.to_string();
|
||||
let name = name.into();
|
||||
let callback = self.box_async_method(&name, method);
|
||||
self.raw.async_meta_methods.push((name, callback));
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "async", not(any(feature = "lua51", feature = "luau"))))]
|
||||
fn add_async_meta_method_mut<M, A, MR, R>(&mut self, name: impl ToString, method: M)
|
||||
fn add_async_meta_method_mut<M, A, MR, R>(&mut self, name: impl Into<StdString>, method: M)
|
||||
where
|
||||
T: 'static,
|
||||
M: Fn(Lua, UserDataRefMut<T>, A) -> MR + MaybeSend + 'static,
|
||||
@@ -566,42 +566,42 @@ impl<T> UserDataMethods<T> for UserDataRegistry<T> {
|
||||
MR: Future<Output = Result<R>> + MaybeSend + 'static,
|
||||
R: IntoLuaMulti,
|
||||
{
|
||||
let name = name.to_string();
|
||||
let name = name.into();
|
||||
let callback = self.box_async_method_mut(&name, method);
|
||||
self.raw.async_meta_methods.push((name, callback));
|
||||
}
|
||||
|
||||
fn add_meta_function<F, A, R>(&mut self, name: impl ToString, function: F)
|
||||
fn add_meta_function<F, A, R>(&mut self, name: impl Into<StdString>, function: F)
|
||||
where
|
||||
F: Fn(&Lua, A) -> Result<R> + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
R: IntoLuaMulti,
|
||||
{
|
||||
let name = name.to_string();
|
||||
let name = name.into();
|
||||
let callback = self.box_function(&name, function);
|
||||
self.raw.meta_methods.push((name, callback));
|
||||
}
|
||||
|
||||
fn add_meta_function_mut<F, A, R>(&mut self, name: impl ToString, function: F)
|
||||
fn add_meta_function_mut<F, A, R>(&mut self, name: impl Into<StdString>, function: F)
|
||||
where
|
||||
F: FnMut(&Lua, A) -> Result<R> + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
R: IntoLuaMulti,
|
||||
{
|
||||
let name = name.to_string();
|
||||
let name = name.into();
|
||||
let callback = self.box_function_mut(&name, function);
|
||||
self.raw.meta_methods.push((name, callback));
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "async", not(any(feature = "lua51", feature = "luau"))))]
|
||||
fn add_async_meta_function<F, A, FR, R>(&mut self, name: impl ToString, function: F)
|
||||
fn add_async_meta_function<F, A, FR, R>(&mut self, name: impl Into<StdString>, function: F)
|
||||
where
|
||||
F: Fn(Lua, A) -> FR + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
FR: Future<Output = Result<R>> + MaybeSend + 'static,
|
||||
R: IntoLuaMulti,
|
||||
{
|
||||
let name = name.to_string();
|
||||
let name = name.into();
|
||||
let callback = self.box_async_function(&name, function);
|
||||
self.raw.async_meta_methods.push((name, callback));
|
||||
}
|
||||
|
||||
@@ -354,7 +354,7 @@ unsafe fn init_userdata_metatable_index(state: *mut ffi::lua_State) -> Result<()
|
||||
end
|
||||
"#;
|
||||
protect_lua!(state, 0, 1, |state| {
|
||||
let ret = ffi::luaL_loadbuffer(state, code.as_ptr(), code.count_bytes(), cstr!("__mlua_index"));
|
||||
let ret = ffi::luaL_loadbuffer(state, code.as_ptr(), code.count_bytes(), cstr!("=__mlua_index"));
|
||||
if ret != ffi::LUA_OK {
|
||||
ffi::lua_error(state);
|
||||
}
|
||||
@@ -405,7 +405,8 @@ unsafe fn init_userdata_metatable_newindex(state: *mut ffi::lua_State) -> Result
|
||||
end
|
||||
"#;
|
||||
protect_lua!(state, 0, 1, |state| {
|
||||
let ret = ffi::luaL_loadbuffer(state, code.as_ptr(), code.count_bytes(), cstr!("__mlua_newindex"));
|
||||
let code_len = code.count_bytes();
|
||||
let ret = ffi::luaL_loadbuffer(state, code.as_ptr(), code_len, cstr!("=__mlua_newindex"));
|
||||
if ret != ffi::LUA_OK {
|
||||
ffi::lua_error(state);
|
||||
}
|
||||
@@ -454,9 +455,9 @@ pub(crate) unsafe extern "C" fn collect_userdata<T>(
|
||||
// It checks if the userdata is safe to destroy and sets the "destroyed" metatable
|
||||
// to prevent further GC collection.
|
||||
pub(super) unsafe extern "C-unwind" fn destroy_userdata_storage<T>(state: *mut ffi::lua_State) -> c_int {
|
||||
let ud = get_userdata::<UserDataStorage<T>>(state, -1);
|
||||
let ud = get_userdata::<UserDataStorage<T>>(state, 1);
|
||||
if (*ud).is_safe_to_destroy() {
|
||||
take_userdata::<UserDataStorage<T>>(state);
|
||||
take_userdata::<UserDataStorage<T>>(state, 1);
|
||||
ffi::lua_pushboolean(state, 1);
|
||||
} else {
|
||||
ffi::lua_pushboolean(state, 0);
|
||||
|
||||
+5
-1
@@ -349,7 +349,11 @@ pub(crate) unsafe fn init_error_registry(state: *mut ffi::lua_State) -> Result<(
|
||||
state,
|
||||
Some(|state| {
|
||||
ffi::lua_pushcfunction(state, error_tostring);
|
||||
rawset_field(state, -2, "__tostring")
|
||||
ffi::lua_setfield(state, -2, cstr!("__tostring"));
|
||||
|
||||
// This is mostly for Luau typeof() function
|
||||
ffi::lua_pushstring(state, cstr!("error"));
|
||||
ffi::lua_setfield(state, -2, cstr!("__type"));
|
||||
}),
|
||||
)?;
|
||||
|
||||
|
||||
+7
-9
@@ -88,7 +88,7 @@ impl Drop for StackGuard {
|
||||
#[inline(always)]
|
||||
pub(crate) unsafe fn push_string(state: *mut ffi::lua_State, s: &[u8], protect: bool) -> Result<()> {
|
||||
// Always use protected mode if the string is too long
|
||||
if protect || s.len() > (1 << 30) {
|
||||
if protect || s.len() >= const { 1 << 30 } {
|
||||
protect_lua!(state, 0, 1, |state| {
|
||||
ffi::lua_pushlstring(state, s.as_ptr() as *const c_char, s.len());
|
||||
})
|
||||
@@ -101,15 +101,13 @@ pub(crate) unsafe fn push_string(state: *mut ffi::lua_State, s: &[u8], protect:
|
||||
// Uses 3 stack spaces (when protect), does not call checkstack.
|
||||
#[cfg(feature = "luau")]
|
||||
#[inline(always)]
|
||||
pub(crate) 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()))?
|
||||
pub(crate) unsafe fn push_buffer(state: *mut ffi::lua_State, size: usize, protect: bool) -> Result<*mut u8> {
|
||||
let data = if protect || size > const { 1024 * 1024 * 1024 } {
|
||||
protect_lua!(state, 0, 1, |state| ffi::lua_newbuffer(state, size))?
|
||||
} else {
|
||||
ffi::lua_newbuffer(state, b.len())
|
||||
ffi::lua_newbuffer(state, size)
|
||||
};
|
||||
let buf = slice::from_raw_parts_mut(data as *mut u8, b.len());
|
||||
buf.copy_from_slice(b);
|
||||
Ok(())
|
||||
Ok(data as *mut u8)
|
||||
}
|
||||
|
||||
// Uses 3 stack spaces, does not call checkstack.
|
||||
@@ -122,7 +120,7 @@ pub(crate) unsafe fn push_table(
|
||||
) -> Result<()> {
|
||||
let narr: c_int = narr.try_into().unwrap_or(c_int::MAX);
|
||||
let nrec: c_int = nrec.try_into().unwrap_or(c_int::MAX);
|
||||
if protect {
|
||||
if protect || narr >= const { 1 << 26 } || nrec >= const { 1 << 26 } {
|
||||
protect_lua!(state, 0, 1, |state| ffi::lua_createtable(state, narr, nrec))
|
||||
} else {
|
||||
ffi::lua_createtable(state, narr, nrec);
|
||||
|
||||
+18
-8
@@ -1,6 +1,6 @@
|
||||
//! Mostly copied from [bevy_utils]
|
||||
//! Inspired by bevy's [disqualified]
|
||||
//!
|
||||
//! [bevy_utils]: https://github.com/bevyengine/bevy/blob/main/crates/bevy_utils/src/short_names.rs
|
||||
//! [disqualified]: https://github.com/bevyengine/disqualified/blob/main/src/short_name.rs
|
||||
|
||||
use std::any::type_name;
|
||||
|
||||
@@ -23,8 +23,7 @@ pub(crate) fn short_type_name<T: ?Sized>() -> String {
|
||||
while index < end_of_string {
|
||||
let rest_of_string = full_name.get(index..end_of_string).unwrap_or_default();
|
||||
|
||||
// Collapse everything up to the next special character,
|
||||
// then skip over it
|
||||
// Collapse everything up to the next special character, then skip over it
|
||||
if let Some(special_character_index) =
|
||||
rest_of_string.find(|c: char| [' ', '<', '>', '(', ')', '[', ']', ',', ';'].contains(&c))
|
||||
{
|
||||
@@ -32,11 +31,16 @@ pub(crate) fn short_type_name<T: ?Sized>() -> String {
|
||||
parsed_name += collapse_type_name(segment_to_collapse);
|
||||
// Insert the special character
|
||||
let special_character = &rest_of_string[special_character_index..=special_character_index];
|
||||
parsed_name.push_str(special_character);
|
||||
parsed_name += special_character;
|
||||
|
||||
// Remove lifetimes like <'_> or <'_, '_, ...>
|
||||
if parsed_name.ends_with("<'_>") || parsed_name.ends_with("<'_, ") {
|
||||
_ = parsed_name.split_off(parsed_name.len() - 4);
|
||||
}
|
||||
|
||||
match special_character {
|
||||
">" | ")" | "]" if rest_of_string[special_character_index + 1..].starts_with("::") => {
|
||||
parsed_name.push_str("::");
|
||||
parsed_name += "::";
|
||||
// Move the index past the "::"
|
||||
index += special_character_index + 3;
|
||||
}
|
||||
@@ -53,14 +57,18 @@ pub(crate) fn short_type_name<T: ?Sized>() -> String {
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn collapse_type_name(string: &str) -> &str {
|
||||
string.rsplit("::").next().unwrap()
|
||||
fn collapse_type_name(segment: &str) -> &str {
|
||||
segment.rsplit("::").next().unwrap()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::short_type_name;
|
||||
use std::collections::HashMap;
|
||||
use std::marker::PhantomData;
|
||||
|
||||
struct MyData<'a, 'b>(PhantomData<&'a &'b ()>);
|
||||
struct MyDataT<'a, T>(PhantomData<&'a T>);
|
||||
|
||||
#[test]
|
||||
fn tests() {
|
||||
@@ -73,5 +81,7 @@ mod tests {
|
||||
"HashMap<String, Option<[i32; 3]>>"
|
||||
);
|
||||
assert_eq!(short_type_name::<dyn Fn(i32) -> i32>(), "dyn Fn(i32) -> i32");
|
||||
assert_eq!(short_type_name::<MyDataT<&str>>(), "MyDataT<&str>");
|
||||
assert_eq!(short_type_name::<(&MyData, [MyData])>(), "(MyData, [MyData])");
|
||||
}
|
||||
}
|
||||
|
||||
+22
-19
@@ -47,7 +47,7 @@ pub(crate) unsafe fn get_internal_metatable<T: TypeKey>(state: *mut ffi::lua_Sta
|
||||
// Uses 6 stack spaces and calls checkstack.
|
||||
pub(crate) unsafe fn init_internal_metatable<T: TypeKey>(
|
||||
state: *mut ffi::lua_State,
|
||||
customize_fn: Option<fn(*mut ffi::lua_State) -> Result<()>>,
|
||||
customize_fn: Option<fn(*mut ffi::lua_State)>,
|
||||
) -> Result<()> {
|
||||
check_stack(state, 6)?;
|
||||
|
||||
@@ -62,18 +62,18 @@ pub(crate) unsafe fn init_internal_metatable<T: TypeKey>(
|
||||
ffi::lua_pushboolean(state, 0);
|
||||
rawset_field(state, -2, "__metatable")?;
|
||||
|
||||
if let Some(f) = customize_fn {
|
||||
f(state)?;
|
||||
}
|
||||
|
||||
protect_lua!(state, 1, 0, |state| {
|
||||
if let Some(f) = customize_fn {
|
||||
f(state);
|
||||
}
|
||||
|
||||
ffi::lua_rawsetp(state, ffi::LUA_REGISTRYINDEX, T::type_key());
|
||||
})?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Uses 2 stack spaces, does not call checkstack
|
||||
// Uses up to 1 stack space, does not call `checkstack`
|
||||
pub(crate) unsafe fn get_internal_userdata<T: TypeKey>(
|
||||
state: *mut ffi::lua_State,
|
||||
index: c_int,
|
||||
@@ -141,24 +141,27 @@ pub(crate) unsafe fn get_userdata<T>(state: *mut ffi::lua_State, index: c_int) -
|
||||
ud
|
||||
}
|
||||
|
||||
// Pops the userdata off of the top of the stack and returns it to rust, invalidating the lua
|
||||
// userdata and gives it the special "destructed" userdata metatable. Userdata must not have been
|
||||
// previously invalidated, and this method does not check for this.
|
||||
// Uses 1 extra stack space and does not call checkstack.
|
||||
pub(crate) unsafe fn take_userdata<T>(state: *mut ffi::lua_State) -> T {
|
||||
// We set the metatable of userdata on __gc to a special table with no __gc method and with
|
||||
// metamethods that trigger an error on access. We do this so that it will not be double
|
||||
// dropped, and also so that it cannot be used or identified as any particular userdata type
|
||||
// after the first call to __gc.
|
||||
/// Unwraps `T` from the Lua userdata and invalidating it by setting the special "destructed"
|
||||
/// metatable.
|
||||
///
|
||||
/// This method does not check that userdata is of type `T` and was not previously invalidated.
|
||||
///
|
||||
/// Uses 1 extra stack space, does not call checkstack.
|
||||
pub(crate) unsafe fn take_userdata<T>(state: *mut ffi::lua_State, idx: c_int) -> T {
|
||||
#[rustfmt::skip]
|
||||
let idx = if idx < 0 { ffi::lua_absindex(state, idx) } else { idx };
|
||||
|
||||
// Update the metatable of this userdata to a special one with no `__gc` method and with
|
||||
// metamethods that trigger an error on access.
|
||||
// We do this so that it will not be double dropped or used after being dropped.
|
||||
get_destructed_userdata_metatable(state);
|
||||
ffi::lua_setmetatable(state, -2);
|
||||
let ud = get_userdata::<T>(state, -1);
|
||||
ffi::lua_setmetatable(state, idx);
|
||||
let ud = get_userdata::<T>(state, idx);
|
||||
|
||||
// Update userdata tag to disable destructor and mark as destructed
|
||||
#[cfg(feature = "luau")]
|
||||
ffi::lua_setuserdatatag(state, -1, 1);
|
||||
ffi::lua_setuserdatatag(state, idx, 1);
|
||||
|
||||
ffi::lua_pop(state, 1);
|
||||
ptr::read(ud)
|
||||
}
|
||||
|
||||
|
||||
+18
-10
@@ -15,7 +15,7 @@ use crate::types::{Integer, LightUserData, Number, ValueRef};
|
||||
use crate::userdata::AnyUserData;
|
||||
use crate::util::{check_stack, StackGuard};
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
#[cfg(feature = "serde")]
|
||||
use {
|
||||
crate::table::SerializableTable,
|
||||
rustc_hash::FxHashSet,
|
||||
@@ -356,14 +356,22 @@ impl Value {
|
||||
///
|
||||
/// If the value is a Lua [`String`], try to convert it to [`BorrowedStr`] or return `None`
|
||||
/// otherwise.
|
||||
#[deprecated(
|
||||
since = "0.11.0",
|
||||
note = "This method does not follow Rust naming convention. Use `as_string().and_then(|s| s.to_str().ok())` instead."
|
||||
)]
|
||||
#[inline]
|
||||
pub fn as_str(&self) -> Option<BorrowedStr> {
|
||||
pub fn as_str(&self) -> Option<BorrowedStr<'_>> {
|
||||
self.as_string().and_then(|s| s.to_str().ok())
|
||||
}
|
||||
|
||||
/// Cast the value to [`StdString`].
|
||||
///
|
||||
/// If the value is a Lua [`String`], converts it to [`StdString`] or returns `None` otherwise.
|
||||
#[deprecated(
|
||||
since = "0.11.0",
|
||||
note = "This method does not follow Rust naming convention. Use `as_string().map(|s| s.to_string_lossy())` instead."
|
||||
)]
|
||||
#[inline]
|
||||
pub fn as_string_lossy(&self) -> Option<StdString> {
|
||||
self.as_string().map(|s| s.to_string_lossy())
|
||||
@@ -481,10 +489,10 @@ impl Value {
|
||||
/// Wrap reference to this Value into [`SerializableValue`].
|
||||
///
|
||||
/// This allows customizing serialization behavior using serde.
|
||||
#[cfg(feature = "serialize")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "serialize")))]
|
||||
#[cfg(feature = "serde")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "serde")))]
|
||||
#[doc(hidden)]
|
||||
pub fn to_serializable(&self) -> SerializableValue {
|
||||
pub fn to_serializable(&self) -> SerializableValue<'_> {
|
||||
SerializableValue::new(self, Default::default(), None)
|
||||
}
|
||||
|
||||
@@ -630,8 +638,8 @@ impl PartialEq for Value {
|
||||
}
|
||||
|
||||
/// A wrapped [`Value`] with customized serialization behavior.
|
||||
#[cfg(feature = "serialize")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "serialize")))]
|
||||
#[cfg(feature = "serde")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "serde")))]
|
||||
pub struct SerializableValue<'a> {
|
||||
value: &'a Value,
|
||||
options: crate::serde::de::Options,
|
||||
@@ -639,7 +647,7 @@ pub struct SerializableValue<'a> {
|
||||
visited: Option<Rc<RefCell<FxHashSet<*const c_void>>>>,
|
||||
}
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
#[cfg(feature = "serde")]
|
||||
impl Serialize for Value {
|
||||
#[inline]
|
||||
fn serialize<S: Serializer>(&self, serializer: S) -> StdResult<S::Ok, S::Error> {
|
||||
@@ -647,7 +655,7 @@ impl Serialize for Value {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
#[cfg(feature = "serde")]
|
||||
impl<'a> SerializableValue<'a> {
|
||||
#[inline]
|
||||
pub(crate) fn new(
|
||||
@@ -711,7 +719,7 @@ impl<'a> SerializableValue<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
#[cfg(feature = "serde")]
|
||||
impl Serialize for SerializableValue<'_> {
|
||||
fn serialize<S>(&self, serializer: S) -> StdResult<S::Ok, S::Error>
|
||||
where
|
||||
|
||||
+2
-3
@@ -1,6 +1,6 @@
|
||||
use std::fmt;
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
#[cfg(feature = "serde")]
|
||||
use serde::ser::{Serialize, SerializeTupleStruct, Serializer};
|
||||
|
||||
/// A Luau vector type.
|
||||
@@ -38,7 +38,6 @@ impl Vector {
|
||||
}
|
||||
|
||||
/// Creates a new vector with all components set to `0.0`.
|
||||
#[doc(hidden)]
|
||||
pub const fn zero() -> Self {
|
||||
Self([0.0; Self::SIZE])
|
||||
}
|
||||
@@ -66,7 +65,7 @@ impl Vector {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
#[cfg(feature = "serde")]
|
||||
impl Serialize for Vector {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error> {
|
||||
let mut ts = serializer.serialize_tuple_struct("Vector", Self::SIZE)?;
|
||||
|
||||
+7
-7
@@ -1,23 +1,23 @@
|
||||
[lua54]
|
||||
features = "lua54,vendored,async,send,serialize,macros,anyhow,userdata-wrappers"
|
||||
features = "lua54,vendored,async,send,serde,macros,anyhow,userdata-wrappers"
|
||||
|
||||
[lua54_non_send]
|
||||
features = "lua54,vendored,async,serialize,macros,anyhow,userdata-wrappers"
|
||||
features = "lua54,vendored,async,serde,macros,anyhow,userdata-wrappers"
|
||||
|
||||
[lua54_with_memory_limit]
|
||||
features = "lua54,vendored,async,send,serialize,macros,anyhow,userdata-wrappers"
|
||||
features = "lua54,vendored,async,send,serde,macros,anyhow,userdata-wrappers"
|
||||
rustflags = "--cfg force_memory_limit"
|
||||
|
||||
[lua51]
|
||||
features = "lua51,vendored,async,send,serialize,macros"
|
||||
features = "lua51,vendored,async,send,serde,macros"
|
||||
|
||||
[lua51_with_memory_limit]
|
||||
features = "lua51,vendored,async,send,serialize,macros"
|
||||
features = "lua51,vendored,async,send,serde,macros"
|
||||
rustflags = "--cfg force_memory_limit"
|
||||
|
||||
[luau]
|
||||
features = "luau,async,send,serialize,macros"
|
||||
features = "luau,async,send,serde,macros"
|
||||
|
||||
[luau_with_memory_limit]
|
||||
features = "luau,async,send,serialize,macros"
|
||||
features = "luau,async,send,serde,macros"
|
||||
rustflags = "--cfg force_memory_limit"
|
||||
|
||||
+77
-3
@@ -8,8 +8,8 @@ use futures_util::stream::TryStreamExt;
|
||||
use tokio::sync::Mutex;
|
||||
|
||||
use mlua::{
|
||||
Error, Function, Lua, LuaOptions, MultiValue, ObjectLike, Result, StdLib, Table, UserData,
|
||||
UserDataMethods, Value,
|
||||
Error, Function, Lua, LuaOptions, MultiValue, ObjectLike, Result, StdLib, Table, ThreadStatus, UserData,
|
||||
UserDataMethods, UserDataRef, Value,
|
||||
};
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
@@ -386,7 +386,7 @@ async fn test_async_table_object_like() -> Result<()> {
|
||||
table.get::<i64>("val")
|
||||
})?,
|
||||
)?;
|
||||
table.set_metatable(Some(metatable));
|
||||
table.set_metatable(Some(metatable))?;
|
||||
assert_eq!(table.call_async::<i64>(()).await.unwrap(), 15);
|
||||
|
||||
match table.call_async_method::<()>("non_existent", ()).await {
|
||||
@@ -547,6 +547,7 @@ async fn test_async_thread_error() -> Result<()> {
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_async_terminate() -> Result<()> {
|
||||
// Future captures `Lua` instance and dropped all together
|
||||
let mutex = Arc::new(Mutex::new(0u32));
|
||||
{
|
||||
let lua = Lua::new();
|
||||
@@ -565,6 +566,17 @@ async fn test_async_terminate() -> Result<()> {
|
||||
}
|
||||
assert!(mutex.try_lock().is_ok());
|
||||
|
||||
// Future is dropped, but `Lua` instance is still alive
|
||||
let lua = Lua::new();
|
||||
let func = lua.create_async_function(move |_, mutex: UserDataRef<Arc<Mutex<u32>>>| async move {
|
||||
let _guard = mutex.lock().await;
|
||||
sleep_ms(100).await;
|
||||
Ok(())
|
||||
})?;
|
||||
let mutex2 = lua.create_any_userdata(mutex.clone())?;
|
||||
let _ = tokio::time::timeout(Duration::from_millis(30), func.call_async::<()>(mutex2)).await;
|
||||
assert!(mutex.try_lock().is_ok());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -598,6 +610,36 @@ async fn test_async_task() -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_async_task_abort() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let sleep = lua.create_async_function(move |_lua, n: u64| async move {
|
||||
sleep_ms(n).await;
|
||||
Ok(())
|
||||
})?;
|
||||
lua.globals().set("sleep", sleep)?;
|
||||
|
||||
let local = tokio::task::LocalSet::new();
|
||||
local
|
||||
.run_until(async {
|
||||
let lua2 = lua.clone();
|
||||
let jh = tokio::task::spawn_local(async move {
|
||||
lua2.load("sleep(200) result = 'done'")
|
||||
.exec_async()
|
||||
.await
|
||||
.unwrap();
|
||||
});
|
||||
sleep_ms(100).await; // Wait for the task to start
|
||||
jh.abort();
|
||||
})
|
||||
.await;
|
||||
local.await;
|
||||
assert_eq!(lua.globals().get::<Value>("result")?, Value::Nil);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
#[cfg(not(feature = "luau"))]
|
||||
async fn test_async_hook() -> Result<()> {
|
||||
@@ -625,3 +667,35 @@ async fn test_async_hook() -> Result<()> {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_async_yield_with() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let func = lua.create_async_function(|lua, (mut a, mut b): (i32, i32)| async move {
|
||||
let zero = lua.yield_with::<MultiValue>(()).await?;
|
||||
assert!(zero.is_empty());
|
||||
let one = lua.yield_with::<MultiValue>(a + b).await?;
|
||||
assert_eq!(one.len(), 1);
|
||||
|
||||
for _ in 0..3 {
|
||||
(a, b) = lua.yield_with((a + b, a * b)).await?;
|
||||
}
|
||||
Ok((0, 0))
|
||||
})?;
|
||||
|
||||
let thread = lua.create_thread(func)?;
|
||||
|
||||
let zero = thread.resume::<MultiValue>((2, 3))?; // function arguments
|
||||
assert!(zero.is_empty());
|
||||
let one = thread.resume::<i32>(())?; // value of "zero" is passed here
|
||||
assert_eq!(one, 5);
|
||||
|
||||
assert_eq!(thread.resume::<(i32, i32)>(1)?, (5, 6)); // value of "one" is passed here
|
||||
assert_eq!(thread.resume::<(i32, i32)>((10, 11))?, (21, 110));
|
||||
assert_eq!(thread.resume::<(i32, i32)>((11, 12))?, (23, 132));
|
||||
assert_eq!(thread.resume::<(i32, i32)>((12, 13))?, (0, 0));
|
||||
assert_eq!(thread.status(), ThreadStatus::Finished);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
+68
-2
@@ -1,5 +1,7 @@
|
||||
#![cfg(feature = "luau")]
|
||||
|
||||
use std::io::{Read, Seek, SeekFrom, Write};
|
||||
|
||||
use mlua::{Lua, Result, Value};
|
||||
|
||||
#[test]
|
||||
@@ -41,7 +43,7 @@ fn test_buffer() -> Result<()> {
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected = "range end index 14 out of range for slice of length 13")]
|
||||
#[should_panic(expected = "out of range for slice of length 13")]
|
||||
fn test_buffer_out_of_bounds_read() {
|
||||
let lua = Lua::new();
|
||||
let buf = lua.create_buffer(b"hello, world!").unwrap();
|
||||
@@ -49,9 +51,73 @@ fn test_buffer_out_of_bounds_read() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected = "range end index 16 out of range for slice of length 13")]
|
||||
#[should_panic(expected = "out of range for slice of length 13")]
|
||||
fn test_buffer_out_of_bounds_write() {
|
||||
let lua = Lua::new();
|
||||
let buf = lua.create_buffer(b"hello, world!").unwrap();
|
||||
buf.write_bytes(14, b"!!");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn create_large_buffer() {
|
||||
let lua = Lua::new();
|
||||
let err = lua.create_buffer_with_capacity(1_073_741_824 + 1).unwrap_err(); // 1GB
|
||||
assert!(err.to_string().contains("memory allocation error"));
|
||||
|
||||
// Normal buffer is okay
|
||||
let buf = lua.create_buffer_with_capacity(1024 * 1024).unwrap();
|
||||
assert_eq!(buf.len(), 1024 * 1024);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_buffer_cursor() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
let mut cursor = lua.create_buffer(b"hello, world")?.cursor();
|
||||
|
||||
let mut data = Vec::new();
|
||||
cursor.read_to_end(&mut data)?;
|
||||
assert_eq!(data, b"hello, world");
|
||||
|
||||
// No more data to read
|
||||
let mut one = [0u8; 1];
|
||||
assert_eq!(cursor.read(&mut one)?, 0);
|
||||
|
||||
// Seek to start
|
||||
cursor.seek(SeekFrom::Start(0))?;
|
||||
cursor.read_exact(&mut one)?;
|
||||
assert_eq!(one, [b'h']);
|
||||
|
||||
// Seek to end -5
|
||||
cursor.seek(SeekFrom::End(-5))?;
|
||||
let mut five = [0u8; 5];
|
||||
cursor.read_exact(&mut five)?;
|
||||
assert_eq!(&five, b"world");
|
||||
|
||||
// Seek to current -1
|
||||
cursor.seek(SeekFrom::Current(-1))?;
|
||||
cursor.read_exact(&mut one)?;
|
||||
assert_eq!(one, [b'd']);
|
||||
|
||||
// Invalid seek
|
||||
assert!(cursor.seek(SeekFrom::Current(-100)).is_err());
|
||||
assert!(cursor.seek(SeekFrom::End(1)).is_err());
|
||||
|
||||
// Write data
|
||||
let buf = lua.create_buffer_with_capacity(100)?;
|
||||
cursor = buf.clone().cursor();
|
||||
|
||||
cursor.write_all(b"hello, ...")?;
|
||||
cursor.seek(SeekFrom::Current(-3))?;
|
||||
cursor.write_all(b"Rust!")?;
|
||||
|
||||
assert_eq!(&buf.read_bytes::<12>(0), b"hello, Rust!");
|
||||
|
||||
// Writing beyond the end of the buffer does nothing
|
||||
cursor.seek(SeekFrom::End(0))?;
|
||||
assert_eq!(cursor.write(b".")?, 0);
|
||||
|
||||
// Flush is no-op
|
||||
cursor.flush()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
+9
-12
@@ -119,12 +119,11 @@ fn test_compiler() -> Result<()> {
|
||||
.set_debug_level(2)
|
||||
.set_type_info_level(1)
|
||||
.set_coverage_level(2)
|
||||
.set_vector_lib("vector")
|
||||
.set_vector_ctor("new")
|
||||
.set_vector_ctor("vector.new")
|
||||
.set_vector_type("vector")
|
||||
.set_mutable_globals(vec!["mutable_global"])
|
||||
.set_userdata_types(vec!["MyUserdata"])
|
||||
.set_disabled_builtins(vec!["tostring"]);
|
||||
.set_mutable_globals(["mutable_global"])
|
||||
.set_userdata_types(["MyUserdata"])
|
||||
.set_disabled_builtins(["tostring"]);
|
||||
|
||||
assert!(compiler.compile("return tostring(vector.new(1, 2, 3))").is_ok());
|
||||
|
||||
@@ -142,16 +141,14 @@ fn test_compiler() -> Result<()> {
|
||||
#[cfg(feature = "luau")]
|
||||
#[test]
|
||||
fn test_compiler_library_constants() {
|
||||
use mlua::{CompileConstant, Compiler, Vector};
|
||||
use mlua::{Compiler, Vector};
|
||||
|
||||
let compiler = Compiler::new()
|
||||
.set_optimization_level(2)
|
||||
.set_library_constants(vec![
|
||||
("mylib", "const_bool", CompileConstant::Boolean(true)),
|
||||
("mylib", "const_num", CompileConstant::Number(123.0)),
|
||||
("mylib", "const_vec", CompileConstant::Vector(Vector::zero())),
|
||||
("mylib", "const_str", "value1".into()),
|
||||
]);
|
||||
.add_library_constant("mylib.const_bool", true)
|
||||
.add_library_constant("mylib.const_num", 123.0)
|
||||
.add_library_constant("mylib.const_vec", Vector::zero())
|
||||
.add_library_constant("mylib.const_str", "value1");
|
||||
|
||||
let lua = Lua::new();
|
||||
lua.set_compiler(compiler);
|
||||
|
||||
@@ -1,32 +1,28 @@
|
||||
error[E0277]: the type `UnsafeCell<mlua::state::raw::RawLua>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
|
||||
error[E0277]: the type `UnsafeCell<*mut lua_State>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
|
||||
--> tests/compile/lua_norefunwindsafe.rs:7:18
|
||||
|
|
||||
7 | catch_unwind(|| lua.create_table().unwrap());
|
||||
| ------------ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `UnsafeCell<mlua::state::raw::RawLua>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
|
||||
| ------------ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `UnsafeCell<*mut lua_State>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
|
||||
| |
|
||||
| required by a bound introduced by this call
|
||||
|
|
||||
= help: within `Lua`, the trait `RefUnwindSafe` is not implemented for `UnsafeCell<mlua::state::raw::RawLua>`, which is required by `{closure@$DIR/tests/compile/lua_norefunwindsafe.rs:7:18: 7:20}: UnwindSafe`
|
||||
note: required because it appears within the type `lock_api::remutex::ReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId, mlua::state::raw::RawLua>`
|
||||
--> $CARGO/lock_api-0.4.12/src/remutex.rs
|
||||
= help: within `mlua::types::sync::inner::ReentrantMutex<mlua::state::raw::RawLua>`, the trait `RefUnwindSafe` is not implemented for `UnsafeCell<*mut lua_State>`
|
||||
note: required because it appears within the type `Cell<*mut lua_State>`
|
||||
--> $RUST/core/src/cell.rs
|
||||
|
|
||||
| pub struct ReentrantMutex<R, G, T: ?Sized> {
|
||||
| ^^^^^^^^^^^^^^
|
||||
note: required because it appears within the type `alloc::sync::ArcInner<lock_api::remutex::ReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId, mlua::state::raw::RawLua>>`
|
||||
--> $RUST/alloc/src/sync.rs
|
||||
| pub struct Cell<T: ?Sized> {
|
||||
| ^^^^
|
||||
note: required because it appears within the type `mlua::state::raw::RawLua`
|
||||
--> src/state/raw.rs
|
||||
|
|
||||
| struct ArcInner<T: ?Sized> {
|
||||
| ^^^^^^^^
|
||||
note: required because it appears within the type `PhantomData<alloc::sync::ArcInner<lock_api::remutex::ReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId, mlua::state::raw::RawLua>>>`
|
||||
--> $RUST/core/src/marker.rs
|
||||
| pub struct RawLua {
|
||||
| ^^^^^^
|
||||
note: required because it appears within the type `mlua::types::sync::inner::ReentrantMutex<mlua::state::raw::RawLua>`
|
||||
--> src/types/sync.rs
|
||||
|
|
||||
| pub struct PhantomData<T: ?Sized>;
|
||||
| ^^^^^^^^^^^
|
||||
note: required because it appears within the type `Arc<lock_api::remutex::ReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId, mlua::state::raw::RawLua>>`
|
||||
--> $RUST/alloc/src/sync.rs
|
||||
|
|
||||
| pub struct Arc<
|
||||
| ^^^
|
||||
| pub(crate) struct ReentrantMutex<T>(T);
|
||||
| ^^^^^^^^^^^^^^
|
||||
= note: required for `Rc<mlua::types::sync::inner::ReentrantMutex<mlua::state::raw::RawLua>>` to implement `RefUnwindSafe`
|
||||
note: required because it appears within the type `Lua`
|
||||
--> src/state.rs
|
||||
|
|
||||
@@ -44,45 +40,27 @@ note: required by a bound in `std::panic::catch_unwind`
|
||||
| pub fn catch_unwind<F: FnOnce() -> R + UnwindSafe, R>(f: F) -> Result<R> {
|
||||
| ^^^^^^^^^^ required by this bound in `catch_unwind`
|
||||
|
||||
error[E0277]: the type `UnsafeCell<usize>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
|
||||
error[E0277]: the type `UnsafeCell<mlua::state::extra::ExtraData>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
|
||||
--> tests/compile/lua_norefunwindsafe.rs:7:18
|
||||
|
|
||||
7 | catch_unwind(|| lua.create_table().unwrap());
|
||||
| ------------ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `UnsafeCell<usize>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
|
||||
| ------------ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `UnsafeCell<mlua::state::extra::ExtraData>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
|
||||
| |
|
||||
| required by a bound introduced by this call
|
||||
|
|
||||
= help: within `Lua`, the trait `RefUnwindSafe` is not implemented for `UnsafeCell<usize>`, which is required by `{closure@$DIR/tests/compile/lua_norefunwindsafe.rs:7:18: 7:20}: UnwindSafe`
|
||||
note: required because it appears within the type `Cell<usize>`
|
||||
--> $RUST/core/src/cell.rs
|
||||
= help: the trait `RefUnwindSafe` is not implemented for `UnsafeCell<mlua::state::extra::ExtraData>`
|
||||
= note: required for `Rc<UnsafeCell<mlua::state::extra::ExtraData>>` to implement `RefUnwindSafe`
|
||||
note: required because it appears within the type `mlua::state::raw::RawLua`
|
||||
--> src/state/raw.rs
|
||||
|
|
||||
| pub struct Cell<T: ?Sized> {
|
||||
| ^^^^
|
||||
note: required because it appears within the type `lock_api::remutex::RawReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId>`
|
||||
--> $CARGO/lock_api-0.4.12/src/remutex.rs
|
||||
| pub struct RawLua {
|
||||
| ^^^^^^
|
||||
note: required because it appears within the type `mlua::types::sync::inner::ReentrantMutex<mlua::state::raw::RawLua>`
|
||||
--> src/types/sync.rs
|
||||
|
|
||||
| pub struct RawReentrantMutex<R, G> {
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
note: required because it appears within the type `lock_api::remutex::ReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId, mlua::state::raw::RawLua>`
|
||||
--> $CARGO/lock_api-0.4.12/src/remutex.rs
|
||||
|
|
||||
| pub struct ReentrantMutex<R, G, T: ?Sized> {
|
||||
| ^^^^^^^^^^^^^^
|
||||
note: required because it appears within the type `alloc::sync::ArcInner<lock_api::remutex::ReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId, mlua::state::raw::RawLua>>`
|
||||
--> $RUST/alloc/src/sync.rs
|
||||
|
|
||||
| struct ArcInner<T: ?Sized> {
|
||||
| ^^^^^^^^
|
||||
note: required because it appears within the type `PhantomData<alloc::sync::ArcInner<lock_api::remutex::ReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId, mlua::state::raw::RawLua>>>`
|
||||
--> $RUST/core/src/marker.rs
|
||||
|
|
||||
| pub struct PhantomData<T: ?Sized>;
|
||||
| ^^^^^^^^^^^
|
||||
note: required because it appears within the type `Arc<lock_api::remutex::ReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId, mlua::state::raw::RawLua>>`
|
||||
--> $RUST/alloc/src/sync.rs
|
||||
|
|
||||
| pub struct Arc<
|
||||
| ^^^
|
||||
| pub(crate) struct ReentrantMutex<T>(T);
|
||||
| ^^^^^^^^^^^^^^
|
||||
= note: required for `Rc<mlua::types::sync::inner::ReentrantMutex<mlua::state::raw::RawLua>>` to implement `RefUnwindSafe`
|
||||
note: required because it appears within the type `Lua`
|
||||
--> src/state.rs
|
||||
|
|
||||
|
||||
@@ -8,7 +8,7 @@ error[E0277]: `Rc<Cell<i32>>` cannot be sent between threads safely
|
||||
| | within this `{closure@$DIR/tests/compile/non_send.rs:11:25: 11:37}`
|
||||
| required by a bound introduced by this call
|
||||
|
|
||||
= help: within `{closure@$DIR/tests/compile/non_send.rs:11:25: 11:37}`, the trait `Send` is not implemented for `Rc<Cell<i32>>`, which is required by `{closure@$DIR/tests/compile/non_send.rs:11:25: 11:37}: MaybeSend`
|
||||
= help: within `{closure@$DIR/tests/compile/non_send.rs:11:25: 11:37}`, the trait `Send` is not implemented for `Rc<Cell<i32>>`
|
||||
note: required because it's used within this closure
|
||||
--> tests/compile/non_send.rs:11:25
|
||||
|
|
||||
|
||||
@@ -1,38 +1,38 @@
|
||||
error[E0277]: the type `UnsafeCell<mlua::state::raw::RawLua>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
|
||||
error[E0277]: the type `UnsafeCell<usize>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
|
||||
--> tests/compile/ref_nounwindsafe.rs:8:18
|
||||
|
|
||||
8 | catch_unwind(move || table.set("a", "b").unwrap());
|
||||
| ------------ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `UnsafeCell<mlua::state::raw::RawLua>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
|
||||
| ------------ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `UnsafeCell<usize>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
|
||||
| |
|
||||
| required by a bound introduced by this call
|
||||
|
|
||||
= help: within `alloc::sync::ArcInner<lock_api::remutex::ReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId, mlua::state::raw::RawLua>>`, the trait `RefUnwindSafe` is not implemented for `UnsafeCell<mlua::state::raw::RawLua>`, which is required by `{closure@$DIR/tests/compile/ref_nounwindsafe.rs:8:18: 8:25}: UnwindSafe`
|
||||
note: required because it appears within the type `lock_api::remutex::ReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId, mlua::state::raw::RawLua>`
|
||||
--> $CARGO/lock_api-0.4.12/src/remutex.rs
|
||||
= help: within `rc::RcInner<mlua::types::sync::inner::ReentrantMutex<mlua::state::raw::RawLua>>`, the trait `RefUnwindSafe` is not implemented for `UnsafeCell<usize>`
|
||||
note: required because it appears within the type `Cell<usize>`
|
||||
--> $RUST/core/src/cell.rs
|
||||
|
|
||||
| pub struct ReentrantMutex<R, G, T: ?Sized> {
|
||||
| ^^^^^^^^^^^^^^
|
||||
note: required because it appears within the type `alloc::sync::ArcInner<lock_api::remutex::ReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId, mlua::state::raw::RawLua>>`
|
||||
--> $RUST/alloc/src/sync.rs
|
||||
| pub struct Cell<T: ?Sized> {
|
||||
| ^^^^
|
||||
note: required because it appears within the type `rc::RcInner<mlua::types::sync::inner::ReentrantMutex<mlua::state::raw::RawLua>>`
|
||||
--> $RUST/alloc/src/rc.rs
|
||||
|
|
||||
| struct ArcInner<T: ?Sized> {
|
||||
| ^^^^^^^^
|
||||
= note: required for `NonNull<alloc::sync::ArcInner<lock_api::remutex::ReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId, mlua::state::raw::RawLua>>>` to implement `UnwindSafe`
|
||||
note: required because it appears within the type `std::sync::Weak<lock_api::remutex::ReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId, mlua::state::raw::RawLua>>`
|
||||
--> $RUST/alloc/src/sync.rs
|
||||
| struct RcInner<T: ?Sized> {
|
||||
| ^^^^^^^
|
||||
= note: required for `NonNull<rc::RcInner<mlua::types::sync::inner::ReentrantMutex<mlua::state::raw::RawLua>>>` to implement `UnwindSafe`
|
||||
note: required because it appears within the type `std::rc::Weak<mlua::types::sync::inner::ReentrantMutex<mlua::state::raw::RawLua>>`
|
||||
--> $RUST/alloc/src/rc.rs
|
||||
|
|
||||
| pub struct Weak<
|
||||
| ^^^^
|
||||
note: required because it appears within the type `mlua::state::WeakLua`
|
||||
note: required because it appears within the type `WeakLua`
|
||||
--> src/state.rs
|
||||
|
|
||||
| pub(crate) struct WeakLua(XWeak<ReentrantMutex<RawLua>>);
|
||||
| ^^^^^^^
|
||||
note: required because it appears within the type `mlua::types::ValueRef`
|
||||
--> src/types.rs
|
||||
| pub struct WeakLua(XWeak<ReentrantMutex<RawLua>>);
|
||||
| ^^^^^^^
|
||||
note: required because it appears within the type `mlua::types::value_ref::ValueRef`
|
||||
--> src/types/value_ref.rs
|
||||
|
|
||||
| pub(crate) struct ValueRef {
|
||||
| ^^^^^^^^
|
||||
| pub struct ValueRef {
|
||||
| ^^^^^^^^
|
||||
note: required because it appears within the type `LuaTable`
|
||||
--> src/table.rs
|
||||
|
|
||||
@@ -49,51 +49,108 @@ note: required by a bound in `std::panic::catch_unwind`
|
||||
| pub fn catch_unwind<F: FnOnce() -> R + UnwindSafe, R>(f: F) -> Result<R> {
|
||||
| ^^^^^^^^^^ required by this bound in `catch_unwind`
|
||||
|
||||
error[E0277]: the type `UnsafeCell<usize>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
|
||||
error[E0277]: the type `UnsafeCell<*mut lua_State>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
|
||||
--> tests/compile/ref_nounwindsafe.rs:8:18
|
||||
|
|
||||
8 | catch_unwind(move || table.set("a", "b").unwrap());
|
||||
| ------------ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `UnsafeCell<usize>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
|
||||
| ------------ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `UnsafeCell<*mut lua_State>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
|
||||
| |
|
||||
| required by a bound introduced by this call
|
||||
|
|
||||
= help: within `alloc::sync::ArcInner<lock_api::remutex::ReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId, mlua::state::raw::RawLua>>`, the trait `RefUnwindSafe` is not implemented for `UnsafeCell<usize>`, which is required by `{closure@$DIR/tests/compile/ref_nounwindsafe.rs:8:18: 8:25}: UnwindSafe`
|
||||
note: required because it appears within the type `Cell<usize>`
|
||||
= help: within `rc::RcInner<mlua::types::sync::inner::ReentrantMutex<mlua::state::raw::RawLua>>`, the trait `RefUnwindSafe` is not implemented for `UnsafeCell<*mut lua_State>`
|
||||
note: required because it appears within the type `Cell<*mut lua_State>`
|
||||
--> $RUST/core/src/cell.rs
|
||||
|
|
||||
| pub struct Cell<T: ?Sized> {
|
||||
| ^^^^
|
||||
note: required because it appears within the type `lock_api::remutex::RawReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId>`
|
||||
--> $CARGO/lock_api-0.4.12/src/remutex.rs
|
||||
note: required because it appears within the type `mlua::state::raw::RawLua`
|
||||
--> src/state/raw.rs
|
||||
|
|
||||
| pub struct RawReentrantMutex<R, G> {
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
note: required because it appears within the type `lock_api::remutex::ReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId, mlua::state::raw::RawLua>`
|
||||
--> $CARGO/lock_api-0.4.12/src/remutex.rs
|
||||
| pub struct RawLua {
|
||||
| ^^^^^^
|
||||
note: required because it appears within the type `mlua::types::sync::inner::ReentrantMutex<mlua::state::raw::RawLua>`
|
||||
--> src/types/sync.rs
|
||||
|
|
||||
| pub struct ReentrantMutex<R, G, T: ?Sized> {
|
||||
| ^^^^^^^^^^^^^^
|
||||
note: required because it appears within the type `alloc::sync::ArcInner<lock_api::remutex::ReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId, mlua::state::raw::RawLua>>`
|
||||
--> $RUST/alloc/src/sync.rs
|
||||
| pub(crate) struct ReentrantMutex<T>(T);
|
||||
| ^^^^^^^^^^^^^^
|
||||
note: required because it appears within the type `rc::RcInner<mlua::types::sync::inner::ReentrantMutex<mlua::state::raw::RawLua>>`
|
||||
--> $RUST/alloc/src/rc.rs
|
||||
|
|
||||
| struct ArcInner<T: ?Sized> {
|
||||
| ^^^^^^^^
|
||||
= note: required for `NonNull<alloc::sync::ArcInner<lock_api::remutex::ReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId, mlua::state::raw::RawLua>>>` to implement `UnwindSafe`
|
||||
note: required because it appears within the type `std::sync::Weak<lock_api::remutex::ReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId, mlua::state::raw::RawLua>>`
|
||||
--> $RUST/alloc/src/sync.rs
|
||||
| struct RcInner<T: ?Sized> {
|
||||
| ^^^^^^^
|
||||
= note: required for `NonNull<rc::RcInner<mlua::types::sync::inner::ReentrantMutex<mlua::state::raw::RawLua>>>` to implement `UnwindSafe`
|
||||
note: required because it appears within the type `std::rc::Weak<mlua::types::sync::inner::ReentrantMutex<mlua::state::raw::RawLua>>`
|
||||
--> $RUST/alloc/src/rc.rs
|
||||
|
|
||||
| pub struct Weak<
|
||||
| ^^^^
|
||||
note: required because it appears within the type `mlua::state::WeakLua`
|
||||
note: required because it appears within the type `WeakLua`
|
||||
--> src/state.rs
|
||||
|
|
||||
| pub(crate) struct WeakLua(XWeak<ReentrantMutex<RawLua>>);
|
||||
| ^^^^^^^
|
||||
note: required because it appears within the type `mlua::types::ValueRef`
|
||||
--> src/types.rs
|
||||
| pub struct WeakLua(XWeak<ReentrantMutex<RawLua>>);
|
||||
| ^^^^^^^
|
||||
note: required because it appears within the type `mlua::types::value_ref::ValueRef`
|
||||
--> src/types/value_ref.rs
|
||||
|
|
||||
| pub(crate) struct ValueRef {
|
||||
| ^^^^^^^^
|
||||
| pub struct ValueRef {
|
||||
| ^^^^^^^^
|
||||
note: required because it appears within the type `LuaTable`
|
||||
--> src/table.rs
|
||||
|
|
||||
| pub struct Table(pub(crate) ValueRef);
|
||||
| ^^^^^
|
||||
note: required because it's used within this closure
|
||||
--> tests/compile/ref_nounwindsafe.rs:8:18
|
||||
|
|
||||
8 | catch_unwind(move || table.set("a", "b").unwrap());
|
||||
| ^^^^^^^
|
||||
note: required by a bound in `std::panic::catch_unwind`
|
||||
--> $RUST/std/src/panic.rs
|
||||
|
|
||||
| pub fn catch_unwind<F: FnOnce() -> R + UnwindSafe, R>(f: F) -> Result<R> {
|
||||
| ^^^^^^^^^^ required by this bound in `catch_unwind`
|
||||
|
||||
error[E0277]: the type `UnsafeCell<mlua::state::extra::ExtraData>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
|
||||
--> tests/compile/ref_nounwindsafe.rs:8:18
|
||||
|
|
||||
8 | catch_unwind(move || table.set("a", "b").unwrap());
|
||||
| ------------ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `UnsafeCell<mlua::state::extra::ExtraData>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
|
||||
| |
|
||||
| required by a bound introduced by this call
|
||||
|
|
||||
= help: the trait `RefUnwindSafe` is not implemented for `UnsafeCell<mlua::state::extra::ExtraData>`
|
||||
= note: required for `Rc<UnsafeCell<mlua::state::extra::ExtraData>>` to implement `RefUnwindSafe`
|
||||
note: required because it appears within the type `mlua::state::raw::RawLua`
|
||||
--> src/state/raw.rs
|
||||
|
|
||||
| pub struct RawLua {
|
||||
| ^^^^^^
|
||||
note: required because it appears within the type `mlua::types::sync::inner::ReentrantMutex<mlua::state::raw::RawLua>`
|
||||
--> src/types/sync.rs
|
||||
|
|
||||
| pub(crate) struct ReentrantMutex<T>(T);
|
||||
| ^^^^^^^^^^^^^^
|
||||
note: required because it appears within the type `rc::RcInner<mlua::types::sync::inner::ReentrantMutex<mlua::state::raw::RawLua>>`
|
||||
--> $RUST/alloc/src/rc.rs
|
||||
|
|
||||
| struct RcInner<T: ?Sized> {
|
||||
| ^^^^^^^
|
||||
= note: required for `NonNull<rc::RcInner<mlua::types::sync::inner::ReentrantMutex<mlua::state::raw::RawLua>>>` to implement `UnwindSafe`
|
||||
note: required because it appears within the type `std::rc::Weak<mlua::types::sync::inner::ReentrantMutex<mlua::state::raw::RawLua>>`
|
||||
--> $RUST/alloc/src/rc.rs
|
||||
|
|
||||
| pub struct Weak<
|
||||
| ^^^^
|
||||
note: required because it appears within the type `WeakLua`
|
||||
--> src/state.rs
|
||||
|
|
||||
| pub struct WeakLua(XWeak<ReentrantMutex<RawLua>>);
|
||||
| ^^^^^^^
|
||||
note: required because it appears within the type `mlua::types::value_ref::ValueRef`
|
||||
--> src/types/value_ref.rs
|
||||
|
|
||||
| pub struct ValueRef {
|
||||
| ^^^^^^^^
|
||||
note: required because it appears within the type `LuaTable`
|
||||
--> src/table.rs
|
||||
|
|
||||
|
||||
@@ -2,7 +2,7 @@ error[E0373]: closure may outlive the current function, but it borrows `inner`,
|
||||
--> tests/compile/scope_callback_capture.rs:7:43
|
||||
|
|
||||
5 | lua.scope(|scope| {
|
||||
| ----- has type `&'1 mut mlua::scope::Scope<'1, '_>`
|
||||
| ----- has type `&'1 mlua::Scope<'1, '_>`
|
||||
6 | let mut inner: Option<Table> = None;
|
||||
7 | let f = scope.create_function_mut(|_, t: Table| {
|
||||
| ^^^^^^^^^^^^^ may outlive borrowed value `inner`
|
||||
|
||||
@@ -2,7 +2,7 @@ error[E0373]: closure may outlive the current function, but it borrows `test.fie
|
||||
--> tests/compile/scope_invariance.rs:13:39
|
||||
|
|
||||
9 | lua.scope(|scope| {
|
||||
| ----- has type `&'1 mut mlua::scope::Scope<'1, '_>`
|
||||
| ----- has type `&'1 mlua::Scope<'1, '_>`
|
||||
...
|
||||
13 | scope.create_function_mut(|_, ()| {
|
||||
| ^^^^^^^ may outlive borrowed value `test.field`
|
||||
|
||||
@@ -2,7 +2,7 @@ error[E0499]: cannot borrow `i` as mutable more than once at a time
|
||||
--> tests/compile/scope_mutable_aliasing.rs:12:51
|
||||
|
|
||||
10 | lua.scope(|scope| {
|
||||
| ----- has type `&mut mlua::scope::Scope<'_, '1>`
|
||||
| ----- has type `&mlua::Scope<'_, '1>`
|
||||
11 | let _a = scope.create_userdata(MyUserData(&mut i)).unwrap();
|
||||
| -----------------------------------------
|
||||
| | |
|
||||
|
||||
@@ -2,7 +2,7 @@ error[E0597]: `ibad` does not live long enough
|
||||
--> tests/compile/scope_userdata_borrow.rs:15:46
|
||||
|
|
||||
11 | lua.scope(|scope| {
|
||||
| ----- has type `&mut mlua::scope::Scope<'_, '1>`
|
||||
| ----- has type `&mlua::Scope<'_, '1>`
|
||||
...
|
||||
14 | let ibad = 42;
|
||||
| ---- binding `ibad` declared here
|
||||
|
||||
+6
-6
@@ -267,7 +267,7 @@ fn test_registry_value_into_lua() -> Result<()> {
|
||||
let r = lua.create_registry_value(&s)?;
|
||||
let value1 = lua.pack(&r)?;
|
||||
let value2 = lua.pack(r)?;
|
||||
assert_eq!(value1.as_str().as_deref(), Some("hello, world"));
|
||||
assert_eq!(value1.to_string()?, "hello, world");
|
||||
assert_eq!(value1.to_pointer(), value2.to_pointer());
|
||||
|
||||
// Push into stack
|
||||
@@ -560,11 +560,11 @@ fn test_osstring_into_from_lua() -> Result<()> {
|
||||
|
||||
let v = lua.pack(s.as_os_str())?;
|
||||
assert!(v.is_string());
|
||||
assert_eq!(v.as_str().unwrap(), "hello, world");
|
||||
assert_eq!(v.as_string().unwrap(), "hello, world");
|
||||
|
||||
let v = lua.pack(s)?;
|
||||
assert!(v.is_string());
|
||||
assert_eq!(v.as_str().unwrap(), "hello, world");
|
||||
assert_eq!(v.as_string().unwrap(), "hello, world");
|
||||
|
||||
let s = lua.create_string("hello, world")?;
|
||||
let bstr = lua.unpack::<OsString>(Value::String(s))?;
|
||||
@@ -588,11 +588,11 @@ fn test_pathbuf_into_from_lua() -> Result<()> {
|
||||
|
||||
let v = lua.pack(pb.as_path())?;
|
||||
assert!(v.is_string());
|
||||
assert_eq!(v.as_str().unwrap(), pb_str);
|
||||
assert_eq!(v.to_string().unwrap(), pb_str);
|
||||
|
||||
let v = lua.pack(pb.clone())?;
|
||||
assert!(v.is_string());
|
||||
assert_eq!(v.as_str().unwrap(), pb_str);
|
||||
assert_eq!(v.to_string().unwrap(), pb_str);
|
||||
|
||||
let s = lua.create_string(pb_str)?;
|
||||
let bstr = lua.unpack::<PathBuf>(Value::String(s))?;
|
||||
@@ -724,7 +724,7 @@ fn test_char_into_lua() -> Result<()> {
|
||||
|
||||
let v = '🦀';
|
||||
let v2 = v.into_lua(&lua)?;
|
||||
assert_eq!(Some(v.to_string()), v2.as_string_lossy());
|
||||
assert_eq!(*v2.as_string().unwrap(), v.to_string());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
+2
-2
@@ -306,7 +306,7 @@ fn test_function_deep_clone() -> Result<()> {
|
||||
|
||||
lua.globals().set("a", 1)?;
|
||||
let func1 = lua.load("a += 1; return a").into_function()?;
|
||||
let func2 = func1.deep_clone();
|
||||
let func2 = func1.deep_clone()?;
|
||||
|
||||
assert_ne!(func1.to_pointer(), func2.to_pointer());
|
||||
assert_eq!(func1.call::<i32>(())?, 2);
|
||||
@@ -314,7 +314,7 @@ fn test_function_deep_clone() -> Result<()> {
|
||||
|
||||
// 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();
|
||||
let rust_func2 = rust_func.deep_clone()?;
|
||||
assert_eq!(rust_func.to_pointer(), rust_func2.to_pointer());
|
||||
|
||||
Ok(())
|
||||
|
||||
+2
-2
@@ -24,7 +24,7 @@ fn test_line_counts() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
lua.set_hook(HookTriggers::EVERY_LINE, move |_lua, debug| {
|
||||
assert_eq!(debug.event(), DebugEvent::Line);
|
||||
hook_output.lock().unwrap().push(debug.curr_line());
|
||||
hook_output.lock().unwrap().push(debug.current_line().unwrap());
|
||||
Ok(VmState::Continue)
|
||||
})?;
|
||||
lua.load(
|
||||
@@ -240,7 +240,7 @@ fn test_hook_threads() -> Result<()> {
|
||||
let hook_output = output.clone();
|
||||
co.set_hook(HookTriggers::EVERY_LINE, move |_lua, debug| {
|
||||
assert_eq!(debug.event(), DebugEvent::Line);
|
||||
hook_output.lock().unwrap().push(debug.curr_line());
|
||||
hook_output.lock().unwrap().push(debug.current_line().unwrap());
|
||||
Ok(VmState::Continue)
|
||||
})?;
|
||||
|
||||
|
||||
+62
-7
@@ -3,11 +3,12 @@
|
||||
use std::cell::Cell;
|
||||
use std::fmt::Debug;
|
||||
use std::os::raw::c_void;
|
||||
use std::panic::{catch_unwind, AssertUnwindSafe};
|
||||
use std::sync::atomic::{AtomicBool, AtomicPtr, AtomicU64, Ordering};
|
||||
use std::sync::Arc;
|
||||
|
||||
use mlua::{Compiler, Error, Lua, LuaOptions, Result, StdLib, Table, ThreadStatus, Value, Vector, VmState};
|
||||
use mlua::{
|
||||
Compiler, Error, Function, Lua, LuaOptions, Result, StdLib, Table, ThreadStatus, Value, Vector, VmState,
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn test_version() -> Result<()> {
|
||||
@@ -118,11 +119,13 @@ fn test_vector_metatable() -> Result<()> {
|
||||
"#,
|
||||
)
|
||||
.eval::<Table>()?;
|
||||
vector_mt.set_metatable(Some(vector_mt.clone()));
|
||||
vector_mt.set_metatable(Some(vector_mt.clone()))?;
|
||||
lua.set_type_metatable::<Vector>(Some(vector_mt.clone()));
|
||||
lua.globals().set("Vector3", vector_mt)?;
|
||||
|
||||
let compiler = Compiler::new().set_vector_lib("Vector3").set_vector_ctor("new");
|
||||
let compiler = Compiler::new()
|
||||
.set_vector_ctor("Vector3.new")
|
||||
.set_vector_type("Vector3");
|
||||
|
||||
// Test vector methods (fastcall)
|
||||
lua.load(
|
||||
@@ -165,9 +168,9 @@ fn test_readonly_table() -> Result<()> {
|
||||
check_readonly_error(t.raw_pop::<Value>());
|
||||
|
||||
// Special case
|
||||
match catch_unwind(AssertUnwindSafe(|| t.set_metatable(None))) {
|
||||
Ok(_) => panic!("expected panic, got nothing"),
|
||||
Err(_) => {}
|
||||
match t.set_metatable(None) {
|
||||
Err(Error::RuntimeError(e)) if e.contains("attempt to modify a readonly table") => {}
|
||||
r => panic!("expected RuntimeError(...) with a specific message, got {r:?}"),
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@@ -194,6 +197,14 @@ fn test_sandbox() -> Result<()> {
|
||||
co.sandbox()?;
|
||||
assert_eq!(co.resume::<Option<i32>>(())?, Some(123));
|
||||
|
||||
// collectgarbage should be restricted in sandboxed mode
|
||||
let collectgarbage = lua.globals().get::<Function>("collectgarbage")?;
|
||||
for arg in ["collect", "stop", "restart", "step", "isrunning"] {
|
||||
let err = collectgarbage.call::<()>(arg).err().unwrap().to_string();
|
||||
assert!(err.contains("collectgarbage called with invalid option"));
|
||||
}
|
||||
assert!(collectgarbage.call::<u64>("count").unwrap() > 0);
|
||||
|
||||
lua.sandbox(false)?;
|
||||
|
||||
// Previously set variable `global` should be cleared now
|
||||
@@ -203,6 +214,11 @@ fn test_sandbox() -> Result<()> {
|
||||
let table = lua.globals().get::<Table>("table")?;
|
||||
table.set("test", "test")?;
|
||||
|
||||
// collectgarbage should work now
|
||||
for arg in ["collect", "stop", "restart", "count", "step", "isrunning"] {
|
||||
collectgarbage.call::<()>(arg).unwrap();
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -314,6 +330,15 @@ fn test_interrupts() -> Result<()> {
|
||||
assert_eq!(yield_count.load(Ordering::Relaxed), 7);
|
||||
assert_eq!(co.status(), ThreadStatus::Finished);
|
||||
|
||||
// Test no yielding at non-yieldable points
|
||||
yield_count.store(0, Ordering::Relaxed);
|
||||
let co = lua.create_thread(lua.create_function(|lua, arg: Value| {
|
||||
(lua.load("return (function(x) return x end)(...)")).call::<Value>(arg)
|
||||
})?)?;
|
||||
let res = co.resume::<String>("abc")?;
|
||||
assert_eq!(res, "abc".to_string());
|
||||
assert_eq!(yield_count.load(Ordering::Relaxed), 3);
|
||||
|
||||
//
|
||||
// Test errors in interrupts
|
||||
//
|
||||
@@ -415,5 +440,35 @@ fn test_thread_events() -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_loadstring() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let f = lua.load(r#"loadstring("return 123")"#).eval::<Function>()?;
|
||||
assert_eq!(f.call::<i32>(())?, 123);
|
||||
|
||||
let err = lua
|
||||
.load(r#"loadstring("retur 123", "chunk")"#)
|
||||
.exec()
|
||||
.err()
|
||||
.unwrap();
|
||||
assert!(err.to_string().contains(
|
||||
r#"syntax error: [string "chunk"]:1: Incomplete statement: expected assignment or a function call"#
|
||||
));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_typeof_error() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let err = Error::runtime("just a test error");
|
||||
let res = lua.load("return typeof(...)").call::<String>(err)?;
|
||||
assert_eq!(res, "error");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[path = "luau/require.rs"]
|
||||
mod require;
|
||||
|
||||
+122
-17
@@ -1,9 +1,16 @@
|
||||
use mlua::{IntoLua, Lua, Result, Value};
|
||||
use std::io::Result as IoResult;
|
||||
use std::result::Result as StdResult;
|
||||
|
||||
use mlua::{Error, IntoLua, Lua, MultiValue, NavigateError, Require, Result, TextRequirer, Value};
|
||||
|
||||
fn run_require(lua: &Lua, path: impl IntoLua) -> Result<Value> {
|
||||
lua.load(r#"return require(...)"#).call(path)
|
||||
}
|
||||
|
||||
fn run_require_pcall(lua: &Lua, path: impl IntoLua) -> Result<MultiValue> {
|
||||
lua.load(r#"return pcall(require, ...)"#).call(path)
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
fn get_str(value: &Value, key: impl IntoLua) -> String {
|
||||
value.as_table().unwrap().get::<String>(key).unwrap()
|
||||
@@ -32,6 +39,65 @@ fn test_require_errors() {
|
||||
assert!(res.is_err());
|
||||
assert!((res.unwrap_err().to_string())
|
||||
.contains("bad argument #1 to 'require' (string expected, got boolean)"));
|
||||
|
||||
// Require from loadstring
|
||||
let res = lua
|
||||
.load(r#"return loadstring("require('./a/relative/path')")()"#)
|
||||
.eval::<Value>();
|
||||
assert!(res.is_err());
|
||||
assert!((res.unwrap_err().to_string()).contains("require is not supported in this context"));
|
||||
|
||||
// Test throwing mlua::Error
|
||||
struct MyRequire(TextRequirer);
|
||||
|
||||
impl Require for MyRequire {
|
||||
fn is_require_allowed(&self, chunk_name: &str) -> bool {
|
||||
self.0.is_require_allowed(chunk_name)
|
||||
}
|
||||
|
||||
fn reset(&mut self, _chunk_name: &str) -> StdResult<(), NavigateError> {
|
||||
Err(Error::runtime("test error"))?
|
||||
}
|
||||
|
||||
fn jump_to_alias(&mut self, path: &str) -> StdResult<(), NavigateError> {
|
||||
self.0.jump_to_alias(path)
|
||||
}
|
||||
|
||||
fn to_parent(&mut self) -> StdResult<(), NavigateError> {
|
||||
self.0.to_parent()
|
||||
}
|
||||
|
||||
fn to_child(&mut self, name: &str) -> StdResult<(), NavigateError> {
|
||||
self.0.to_child(name)
|
||||
}
|
||||
|
||||
fn has_module(&self) -> bool {
|
||||
self.0.has_module()
|
||||
}
|
||||
|
||||
fn cache_key(&self) -> String {
|
||||
self.0.cache_key()
|
||||
}
|
||||
|
||||
fn has_config(&self) -> bool {
|
||||
self.0.has_config()
|
||||
}
|
||||
|
||||
fn config(&self) -> IoResult<Vec<u8>> {
|
||||
self.0.config()
|
||||
}
|
||||
|
||||
fn loader(&self, lua: &Lua) -> Result<mlua::Function> {
|
||||
self.0.loader(lua)
|
||||
}
|
||||
}
|
||||
|
||||
let require = lua
|
||||
.create_require_function(MyRequire(TextRequirer::new()))
|
||||
.unwrap();
|
||||
lua.globals().set("require", require).unwrap();
|
||||
let res = lua.load(r#"return require('./a/relative/path')"#).exec();
|
||||
assert!((res.unwrap_err().to_string()).contains("test error"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -39,44 +105,69 @@ fn test_require_without_config() {
|
||||
let lua = Lua::new();
|
||||
|
||||
// RequireSimpleRelativePath
|
||||
let res = run_require(&lua, "./require/without_config/dependency").unwrap();
|
||||
let res = run_require(&lua, "./tests/luau/require/without_config/dependency").unwrap();
|
||||
assert_eq!("result from dependency", get_str(&res, 1));
|
||||
|
||||
// RequireSimpleRelativePathWithinPcall
|
||||
let res = run_require_pcall(&lua, "./tests/luau/require/without_config/dependency").unwrap();
|
||||
assert!(res[0].as_boolean().unwrap());
|
||||
assert_eq!("result from dependency", get_str(&res[1], 1));
|
||||
|
||||
// RequireRelativeToRequiringFile
|
||||
let res = run_require(&lua, "./require/without_config/module").unwrap();
|
||||
let res = run_require(&lua, "./tests/luau/require/without_config/module").unwrap();
|
||||
assert_eq!("result from dependency", get_str(&res, 1));
|
||||
assert_eq!("required into module", get_str(&res, 2));
|
||||
|
||||
// RequireLua
|
||||
let res = run_require(&lua, "./require/without_config/lua_dependency").unwrap();
|
||||
let res = run_require(&lua, "./tests/luau/require/without_config/lua_dependency").unwrap();
|
||||
assert_eq!("result from lua_dependency", get_str(&res, 1));
|
||||
|
||||
// RequireInitLuau
|
||||
let res = run_require(&lua, "./require/without_config/luau").unwrap();
|
||||
let res = run_require(&lua, "./tests/luau/require/without_config/luau").unwrap();
|
||||
assert_eq!("result from init.luau", get_str(&res, 1));
|
||||
|
||||
// RequireInitLua
|
||||
let res = run_require(&lua, "./require/without_config/lua").unwrap();
|
||||
let res = run_require(&lua, "./tests/luau/require/without_config/lua").unwrap();
|
||||
assert_eq!("result from init.lua", get_str(&res, 1));
|
||||
|
||||
// RequireSubmoduleUsingSelf
|
||||
let res = run_require(&lua, "./require/without_config/nested_module_requirer").unwrap();
|
||||
// RequireSubmoduleUsingSelfIndirectly
|
||||
let res = run_require(&lua, "./tests/luau/require/without_config/nested_module_requirer").unwrap();
|
||||
assert_eq!("result from submodule", get_str(&res, 1));
|
||||
|
||||
// RequireSubmoduleUsingSelfDirectly
|
||||
let res = run_require(&lua, "./tests/luau/require/without_config/nested").unwrap();
|
||||
assert_eq!("result from submodule", get_str(&res, 1));
|
||||
|
||||
// CannotRequireInitLuauDirectly
|
||||
let res = run_require(&lua, "./tests/luau/require/without_config/nested/init");
|
||||
assert!(res.is_err());
|
||||
assert!((res.unwrap_err().to_string()).contains("could not resolve child component \"init\""));
|
||||
|
||||
// RequireNestedInits
|
||||
let res = run_require(&lua, "./tests/luau/require/without_config/nested_inits_requirer").unwrap();
|
||||
assert_eq!("result from nested_inits/init", get_str(&res, 1));
|
||||
assert_eq!("required into module", get_str(&res, 2));
|
||||
|
||||
// RequireWithFileAmbiguity
|
||||
let res = run_require(&lua, "./require/without_config/ambiguous_file_requirer");
|
||||
let res = run_require(
|
||||
&lua,
|
||||
"./tests/luau/require/without_config/ambiguous_file_requirer",
|
||||
);
|
||||
assert!(res.is_err());
|
||||
assert!((res.unwrap_err().to_string())
|
||||
.contains("could not resolve child component \"dependency\" (ambiguous)"));
|
||||
|
||||
// RequireWithDirectoryAmbiguity
|
||||
let res = run_require(&lua, "./require/without_config/ambiguous_directory_requirer");
|
||||
let res = run_require(
|
||||
&lua,
|
||||
"./tests/luau/require/without_config/ambiguous_directory_requirer",
|
||||
);
|
||||
assert!(res.is_err());
|
||||
assert!((res.unwrap_err().to_string())
|
||||
.contains("could not resolve child component \"dependency\" (ambiguous)"));
|
||||
|
||||
// CheckCachedResult
|
||||
let res = run_require(&lua, "./require/without_config/validate_cache").unwrap();
|
||||
let res = run_require(&lua, "./tests/luau/require/without_config/validate_cache").unwrap();
|
||||
assert!(res.is_table());
|
||||
}
|
||||
|
||||
@@ -85,15 +176,24 @@ fn test_require_with_config() {
|
||||
let lua = Lua::new();
|
||||
|
||||
// RequirePathWithAlias
|
||||
let res = run_require(&lua, "./require/with_config/src/alias_requirer").unwrap();
|
||||
let res = run_require(&lua, "./tests/luau/require/with_config/src/alias_requirer").unwrap();
|
||||
assert_eq!("result from dependency", get_str(&res, 1));
|
||||
|
||||
// RequirePathWithAlias (case-insensitive)
|
||||
let res2 = run_require(&lua, "./tests/luau/require/with_config/src/alias_requirer_uc").unwrap();
|
||||
assert_eq!("result from dependency", get_str(&res2, 1));
|
||||
assert_eq!(res.to_pointer(), res2.to_pointer());
|
||||
|
||||
// RequirePathWithParentAlias
|
||||
let res = run_require(&lua, "./require/with_config/src/parent_alias_requirer").unwrap();
|
||||
let res = run_require(&lua, "./tests/luau/require/with_config/src/parent_alias_requirer").unwrap();
|
||||
assert_eq!("result from other_dependency", get_str(&res, 1));
|
||||
|
||||
// RequirePathWithAliasPointingToDirectory
|
||||
let res = run_require(&lua, "./require/with_config/src/directory_alias_requirer").unwrap();
|
||||
let res = run_require(
|
||||
&lua,
|
||||
"./tests/luau/require/with_config/src/directory_alias_requirer",
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!("result from subdirectory_dependency", get_str(&res, 1));
|
||||
|
||||
// RequireAliasThatDoesNotExist
|
||||
@@ -107,7 +207,7 @@ fn test_require_with_config() {
|
||||
assert!((res.unwrap_err().to_string()).contains("@ is not a valid alias"));
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
#[cfg(all(feature = "async", not(windows)))]
|
||||
#[tokio::test]
|
||||
async fn test_async_require() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
@@ -130,14 +230,19 @@ async fn test_async_require() -> Result<()> {
|
||||
Ok(())
|
||||
})?,
|
||||
)?;
|
||||
lua.globals().set("tmp_dir", temp_dir.path().to_str().unwrap())?;
|
||||
lua.globals().set(
|
||||
"curr_dir_components",
|
||||
std::env::current_dir().unwrap().components().count(),
|
||||
)?;
|
||||
|
||||
lua.load(
|
||||
r#"
|
||||
local result = require("./async_chunk")
|
||||
local path_to_root = string.rep("/..", curr_dir_components - 1)
|
||||
local result = require(`.{path_to_root}{tmp_dir}/async_chunk`)
|
||||
assert(result == "result_after_async_sleep")
|
||||
"#,
|
||||
)
|
||||
.set_name(format!("@{}", temp_dir.path().join("require.rs").display()))
|
||||
.exec_async()
|
||||
.await
|
||||
}
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
return require("@DeP")
|
||||
@@ -0,0 +1,2 @@
|
||||
local result = require("@self/init")
|
||||
return result
|
||||
@@ -0,0 +1 @@
|
||||
return {"result from nested_inits/init"}
|
||||
@@ -0,0 +1,3 @@
|
||||
local result = require("./nested_inits")
|
||||
result[#result+1] = "required into module"
|
||||
return result
|
||||
+2
-2
@@ -43,12 +43,12 @@ fn test_result_conversions() -> Result<()> {
|
||||
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");
|
||||
assert_eq!(multi_err1[1].as_string().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(), "!");
|
||||
assert_eq!(multi_ok2[0].as_string().unwrap(), "!");
|
||||
let err2 = Err::<String, _>("failure2".into_lua_err());
|
||||
let multi_err2 = err2.into_lua_multi(&lua)?;
|
||||
assert_eq!(multi_err2.len(), 2);
|
||||
|
||||
+4
-2
@@ -382,7 +382,8 @@ fn test_scope_userdata_ref() -> Result<()> {
|
||||
modify_userdata(&lua, &ud)?;
|
||||
|
||||
// We can only borrow userdata scoped
|
||||
assert!((matches!(ud.borrow::<MyUserData>(), Err(Error::UserDataTypeMismatch))));
|
||||
#[rustfmt::skip]
|
||||
assert!(matches!(ud.borrow::<MyUserData>(), Err(Error::UserDataTypeMismatch)));
|
||||
ud.borrow_scoped::<MyUserData, ()>(|ud_inst| {
|
||||
assert_eq!(ud_inst.0.get(), 2);
|
||||
})?;
|
||||
@@ -419,7 +420,8 @@ fn test_scope_userdata_ref_mut() -> Result<()> {
|
||||
let ud = scope.create_userdata_ref_mut(&mut data)?;
|
||||
modify_userdata(&lua, &ud)?;
|
||||
|
||||
assert!((matches!(ud.borrow_mut::<MyUserData>(), Err(Error::UserDataTypeMismatch))));
|
||||
#[rustfmt::skip]
|
||||
assert!(matches!(ud.borrow_mut::<MyUserData>(), Err(Error::UserDataTypeMismatch)));
|
||||
ud.borrow_mut_scoped::<MyUserData, ()>(|ud_inst| {
|
||||
ud_inst.0 += 10;
|
||||
})?;
|
||||
|
||||
+3
-3
@@ -1,4 +1,4 @@
|
||||
#![cfg(feature = "serialize")]
|
||||
#![cfg(feature = "serde")]
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::error::Error as StdError;
|
||||
@@ -25,7 +25,7 @@ fn test_serialize() -> Result<(), Box<dyn StdError>> {
|
||||
globals.set("null", lua.null())?;
|
||||
|
||||
let empty_array = lua.create_table()?;
|
||||
empty_array.set_metatable(Some(lua.array_metatable()));
|
||||
empty_array.set_metatable(Some(lua.array_metatable()))?;
|
||||
globals.set("empty_array", empty_array)?;
|
||||
|
||||
let val = lua
|
||||
@@ -173,7 +173,7 @@ fn test_serialize_sorted() -> LuaResult<()> {
|
||||
globals.set("null", lua.null())?;
|
||||
|
||||
let empty_array = lua.create_table()?;
|
||||
empty_array.set_metatable(Some(lua.array_metatable()));
|
||||
empty_array.set_metatable(Some(lua.array_metatable()))?;
|
||||
globals.set("empty_array", empty_array)?;
|
||||
|
||||
let value = lua
|
||||
|
||||
+11
-2
@@ -61,6 +61,15 @@ fn test_table() -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(target_os = "linux")] // Linux allow overcommiting the memory (relevant for CI)
|
||||
fn test_table_with_large_capacity() {
|
||||
let lua = Lua::new();
|
||||
|
||||
let t = lua.create_table_with_capacity(1 << 26, 1 << 26);
|
||||
assert!(t.is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_table_push_pop() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
@@ -298,10 +307,10 @@ fn test_metatable() -> Result<()> {
|
||||
let table = lua.create_table()?;
|
||||
let metatable = lua.create_table()?;
|
||||
metatable.set("__index", lua.create_function(|_, ()| Ok("index_value"))?)?;
|
||||
table.set_metatable(Some(metatable));
|
||||
table.set_metatable(Some(metatable))?;
|
||||
assert_eq!(table.get::<String>("any_key")?, "index_value");
|
||||
assert_eq!(table.raw_get::<Value>("any_key")?, Value::Nil);
|
||||
table.set_metatable(None);
|
||||
table.set_metatable(None)?;
|
||||
assert_eq!(table.get::<Value>("any_key")?, Value::Nil);
|
||||
|
||||
Ok(())
|
||||
|
||||
+81
-8
@@ -147,6 +147,31 @@ fn test_eval() -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_replace_globals() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let globals = lua.create_table()?;
|
||||
globals.set("foo", "bar")?;
|
||||
|
||||
lua.set_globals(globals.clone())?;
|
||||
let val = lua.load("return foo").eval::<StdString>()?;
|
||||
assert_eq!(val, "bar");
|
||||
|
||||
// Updating globals in sandboxed Lua state is not allowed
|
||||
#[cfg(feature = "luau")]
|
||||
{
|
||||
lua.sandbox(true)?;
|
||||
match lua.set_globals(globals) {
|
||||
Err(Error::RuntimeError(msg))
|
||||
if msg.contains("cannot change globals in a sandboxed Lua state") => {}
|
||||
r => panic!("expected RuntimeError(...) with a specific error message, got {r:?}"),
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_load_mode() -> Result<()> {
|
||||
let lua = unsafe { Lua::unsafe_new() };
|
||||
@@ -577,6 +602,21 @@ fn test_num_conversion() -> Result<()> {
|
||||
|
||||
assert_eq!(lua.unpack::<i128>(lua.pack(1i128 << 64)?)?, 1i128 << 64);
|
||||
|
||||
// Negative zero
|
||||
let negative_zero = lua.load("-0.0").eval::<f64>()?;
|
||||
assert_eq!(negative_zero, 0.0);
|
||||
// LuaJIT treats -0.0 as a positive zero
|
||||
#[cfg(not(feature = "luajit"))]
|
||||
assert!(negative_zero.is_sign_negative());
|
||||
|
||||
// In Lua <5.3 all numbers are floats
|
||||
#[cfg(not(any(feature = "lua54", feature = "lua53", feature = "luajit")))]
|
||||
{
|
||||
let negative_zero = lua.load("-0").eval::<f64>()?;
|
||||
assert_eq!(negative_zero, 0.0);
|
||||
assert!(negative_zero.is_sign_negative());
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1202,6 +1242,17 @@ fn test_register_module() -> Result<()> {
|
||||
res.unwrap_err().to_string(),
|
||||
"runtime error: module name must begin with '@'"
|
||||
);
|
||||
|
||||
// Luau registered modules (aliases) are case-insensitive
|
||||
let res = lua.register_module("@My_Module", &t);
|
||||
assert!(res.is_ok());
|
||||
lua.load(
|
||||
r#"
|
||||
local my_module = require("@MY_MODule")
|
||||
assert(my_module.name == "my_module")
|
||||
"#,
|
||||
)
|
||||
.exec()?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@@ -1251,14 +1302,18 @@ fn test_inspect_stack() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
// Not inside any function
|
||||
assert!(lua.inspect_stack(0).is_none());
|
||||
assert!(lua.inspect_stack(0, |_| ()).is_none());
|
||||
|
||||
let logline = lua.create_function(|lua, msg: StdString| {
|
||||
let debug = lua.inspect_stack(1).unwrap(); // caller
|
||||
let source = debug.source().short_src;
|
||||
let source = source.as_deref().unwrap_or("?");
|
||||
let line = debug.curr_line();
|
||||
Ok(format!("{}:{} {}", source, line, msg))
|
||||
let r = lua
|
||||
.inspect_stack(1, |debug| {
|
||||
let source = debug.source().short_src;
|
||||
let source = source.as_deref().unwrap_or("?");
|
||||
let line = debug.current_line().unwrap();
|
||||
format!("{}:{} {}", source, line, msg)
|
||||
})
|
||||
.unwrap();
|
||||
Ok(r)
|
||||
})?;
|
||||
lua.globals().set("logline", logline)?;
|
||||
|
||||
@@ -1281,8 +1336,7 @@ fn test_inspect_stack() -> Result<()> {
|
||||
.exec()?;
|
||||
|
||||
let stack_info = lua.create_function(|lua, ()| {
|
||||
let debug = lua.inspect_stack(1).unwrap(); // caller
|
||||
let stack_info = debug.stack();
|
||||
let stack_info = lua.inspect_stack(1, |debug| debug.stack()).unwrap();
|
||||
Ok(format!("{stack_info:?}"))
|
||||
})?;
|
||||
lua.globals().set("stack_info", stack_info)?;
|
||||
@@ -1312,6 +1366,25 @@ fn test_inspect_stack() -> Result<()> {
|
||||
)
|
||||
.exec()?;
|
||||
|
||||
// Test retrieving currently running function
|
||||
let running_function =
|
||||
lua.create_function(|lua, ()| Ok(lua.inspect_stack(1, |debug| debug.function())))?;
|
||||
lua.globals().set("running_function", running_function)?;
|
||||
lua.load(
|
||||
r#"
|
||||
local function baz()
|
||||
return running_function()
|
||||
end
|
||||
if jit == nil then
|
||||
assert(baz() == baz)
|
||||
else
|
||||
-- luajit inline the "baz" function and returns the chunk itself
|
||||
assert(baz() == running_function())
|
||||
end
|
||||
"#,
|
||||
)
|
||||
.exec()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
+22
-1
@@ -1,6 +1,6 @@
|
||||
use std::panic::catch_unwind;
|
||||
|
||||
use mlua::{Error, Function, Lua, Result, Thread, ThreadStatus};
|
||||
use mlua::{Error, Function, IntoLua, Lua, Result, Thread, ThreadStatus, Value};
|
||||
|
||||
#[test]
|
||||
fn test_thread() -> Result<()> {
|
||||
@@ -252,3 +252,24 @@ fn test_thread_resume_error() -> Result<()> {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_thread_resume_bad_arg() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
struct BadArg;
|
||||
|
||||
impl IntoLua for BadArg {
|
||||
fn into_lua(self, _lua: &Lua) -> Result<Value> {
|
||||
Err(Error::runtime("bad arg"))
|
||||
}
|
||||
}
|
||||
|
||||
let f = lua.create_thread(lua.create_function(|_, ()| Ok("okay"))?)?;
|
||||
let res = f.resume::<()>((123, BadArg));
|
||||
assert!(matches!(res, Err(Error::RuntimeError(msg)) if msg == "bad arg"));
|
||||
let res = f.resume::<String>(()).unwrap();
|
||||
assert_eq!(res, "okay");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
+14
-4
@@ -38,7 +38,7 @@ fn test_userdata() -> Result<()> {
|
||||
|
||||
#[test]
|
||||
fn test_methods() -> Result<()> {
|
||||
#[cfg_attr(feature = "serialize", derive(serde::Serialize))]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
|
||||
struct MyUserData(i64);
|
||||
|
||||
impl UserData for MyUserData {
|
||||
@@ -81,7 +81,7 @@ fn test_methods() -> Result<()> {
|
||||
check_methods(&lua, lua.create_userdata(MyUserData(42))?)?;
|
||||
|
||||
// Additionally check serializable userdata
|
||||
#[cfg(feature = "serialize")]
|
||||
#[cfg(feature = "serde")]
|
||||
check_methods(&lua, lua.create_ser_userdata(MyUserData(42))?)?;
|
||||
|
||||
Ok(())
|
||||
@@ -306,7 +306,7 @@ fn test_userdata_take() -> Result<()> {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
#[cfg(feature = "serde")]
|
||||
impl serde::Serialize for MyUserdata {
|
||||
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
|
||||
where
|
||||
@@ -364,7 +364,7 @@ fn test_userdata_take() -> Result<()> {
|
||||
check_userdata_take(&lua, userdata, rc)?;
|
||||
|
||||
// Additionally check serializable userdata
|
||||
#[cfg(feature = "serialize")]
|
||||
#[cfg(feature = "serde")]
|
||||
{
|
||||
let rc = Arc::new(18);
|
||||
let userdata = lua.create_ser_userdata(MyUserdata(rc.clone()))?;
|
||||
@@ -525,6 +525,11 @@ fn test_fields() -> Result<()> {
|
||||
Ok(())
|
||||
});
|
||||
|
||||
// Field that emulates method
|
||||
fields.add_field_function_get("val_fget", |lua, ud| {
|
||||
lua.create_function(move |_, ()| Ok(ud.borrow::<MyUserData>()?.0))
|
||||
});
|
||||
|
||||
// Use userdata "uservalue" storage
|
||||
fields.add_field_function_get("uval", |_, ud| ud.user_value::<Option<String>>());
|
||||
fields.add_field_function_set("uval", |_, ud, s: Option<String>| ud.set_user_value(s));
|
||||
@@ -537,6 +542,10 @@ fn test_fields() -> Result<()> {
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
fn add_methods<M: UserDataMethods<Self>>(methods: &mut M) {
|
||||
methods.add_method("dummy", |_, _, ()| Ok(()));
|
||||
}
|
||||
}
|
||||
|
||||
globals.set("ud", MyUserData(7))?;
|
||||
@@ -546,6 +555,7 @@ fn test_fields() -> Result<()> {
|
||||
assert(ud.val == 7)
|
||||
ud.val = 10
|
||||
assert(ud.val == 10)
|
||||
assert(ud:val_fget() == 10)
|
||||
|
||||
assert(ud.uval == nil)
|
||||
ud.uval = "hello"
|
||||
|
||||
+1
-10
@@ -255,16 +255,7 @@ fn test_value_conversions() -> Result<()> {
|
||||
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_eq!(Value::String(lua.create_string("hello")?).to_string()?, "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());
|
||||
|
||||
Reference in New Issue
Block a user