mirror of
https://github.com/mlua-rs/mlua
synced 2026-06-08 16:05:43 +00:00
Compare commits
48 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 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,22 @@
|
||||
## 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.0-beta.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-beta.2", 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.0", 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.6", features = ["async_tokio"] }
|
||||
rustyline = "16.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"
|
||||
|
||||
@@ -23,12 +23,12 @@
|
||||
>
|
||||
> See v0.10 [release notes](https://github.com/mlua-rs/mlua/blob/main/docs/release_notes/v0.10.md).
|
||||
|
||||
`mlua` is bindings to [Lua](https://www.lua.org) programming language for Rust with a goal to provide
|
||||
_safe_ (as far as it's possible), high level, easy to use, practical and flexible API.
|
||||
`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.
|
||||
|
||||
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.
|
||||
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` 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 +39,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 +51,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 +78,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,7 +93,7 @@ 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
|
||||
@@ -102,7 +102,7 @@ curl -v http://localhost:3000
|
||||
|
||||
### Serialization (serde) support
|
||||
|
||||
With `serialize` feature flag enabled, `mlua` allows you to serialize/deserialize any type that implements [`serde::Serialize`] and [`serde::Deserialize`] into/from [`mlua::Value`]. In addition `mlua` provides [`serde::Serialize`] trait implementation for it (including `UserData` support).
|
||||
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 it (including `UserData` support).
|
||||
|
||||
[Example](examples/serialize.rs)
|
||||
|
||||
@@ -114,24 +114,24 @@ 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]
|
||||
@@ -159,11 +159,11 @@ 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]
|
||||
@@ -173,7 +173,7 @@ crate-type = ["cdylib"]
|
||||
mlua = { version = "0.10", features = ["lua54", "module"] }
|
||||
```
|
||||
|
||||
`lib.rs` :
|
||||
`lib.rs`:
|
||||
|
||||
``` rust
|
||||
use mlua::prelude::*;
|
||||
@@ -216,14 +216,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 +236,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 +247,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 +267,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 +289,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).
|
||||
|
||||
+3
-3
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "mlua-sys"
|
||||
version = "0.7.0"
|
||||
version = "0.8.1"
|
||||
authors = ["Aleksandr Orlenko <zxteam@pm.me>"]
|
||||
rust-version = "1.71"
|
||||
edition = "2021"
|
||||
@@ -38,9 +38,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.0", optional = true }
|
||||
|
||||
[lints.rust]
|
||||
unexpected_cfgs = { level = "allow", check-cfg = ['cfg(raw_dylib)'] }
|
||||
|
||||
@@ -388,7 +388,7 @@ pub unsafe fn luaL_loadbufferenv(
|
||||
}
|
||||
}
|
||||
|
||||
if chunk_is_text {
|
||||
let status = if chunk_is_text {
|
||||
if env < 0 {
|
||||
env -= 1;
|
||||
}
|
||||
@@ -397,14 +397,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
|
||||
}
|
||||
|
||||
|
||||
@@ -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-beta.2"
|
||||
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()))
|
||||
}
|
||||
}
|
||||
|
||||
+2
-3
@@ -1,4 +1,4 @@
|
||||
#[cfg(feature = "serialize")]
|
||||
#[cfg(feature = "serde")]
|
||||
use serde::ser::{Serialize, Serializer};
|
||||
|
||||
use crate::types::ValueRef;
|
||||
@@ -25,7 +25,6 @@ impl Buffer {
|
||||
}
|
||||
|
||||
/// Returns `true` if the buffer is empty.
|
||||
#[doc(hidden)]
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.len() == 0
|
||||
}
|
||||
@@ -73,7 +72,7 @@ impl Buffer {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
#[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() })
|
||||
|
||||
+36
-15
@@ -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> {
|
||||
@@ -616,7 +637,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 +654,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);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
+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())
|
||||
|
||||
+15
-7
@@ -253,7 +253,7 @@ impl Function {
|
||||
"#,
|
||||
)
|
||||
.try_cache()
|
||||
.set_name("__mlua_bind")
|
||||
.set_name("=__mlua_bind")
|
||||
.call((self, args_wrapper))
|
||||
}
|
||||
|
||||
@@ -492,16 +492,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()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -86,7 +86,7 @@ impl<'a> Debug<'a> {
|
||||
}
|
||||
|
||||
/// Corresponds to the `n` what mask.
|
||||
pub fn names(&self) -> DebugNames {
|
||||
pub fn names(&self) -> DebugNames<'_> {
|
||||
unsafe {
|
||||
#[cfg(not(feature = "luau"))]
|
||||
mlua_assert!(
|
||||
@@ -113,7 +113,7 @@ impl<'a> Debug<'a> {
|
||||
}
|
||||
|
||||
/// Corresponds to the `S` what mask.
|
||||
pub fn source(&self) -> DebugSource {
|
||||
pub fn source(&self) -> DebugSource<'_> {
|
||||
unsafe {
|
||||
#[cfg(not(feature = "luau"))]
|
||||
mlua_assert!(
|
||||
|
||||
+7
-7
@@ -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
|
||||
//!
|
||||
@@ -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;
|
||||
|
||||
|
||||
+28
-7
@@ -1,11 +1,14 @@
|
||||
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};
|
||||
|
||||
pub use require::{NavigateError, Require};
|
||||
pub use require::{NavigateError, Require, TextRequirer};
|
||||
|
||||
// Since Luau has some missing standard functions, we re-implement them here
|
||||
|
||||
@@ -22,6 +25,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 +45,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 +65,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 +79,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;
|
||||
|
||||
+243
-240
@@ -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.
|
||||
#[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.
|
||||
#[cfg(any(feature = "luau", doc))]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
|
||||
pub trait Require: MaybeSend {
|
||||
/// 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 {
|
||||
@@ -102,15 +104,17 @@ 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>,
|
||||
#[doc(hidden)]
|
||||
#[derive(Default, Debug)]
|
||||
pub struct TextRequirer {
|
||||
abs_path: PathBuf,
|
||||
rel_path: PathBuf,
|
||||
module_path: PathBuf,
|
||||
}
|
||||
|
||||
impl TextRequirer {
|
||||
pub(super) fn new() -> Self {
|
||||
/// Creates a new `TextRequirer` instance.
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
@@ -152,33 +156,25 @@ impl TextRequirer {
|
||||
components.into_iter().collect()
|
||||
}
|
||||
|
||||
fn find_module_path(path: &Path) -> StdResult<PathBuf, NavigateError> {
|
||||
fn find_module(path: &Path) -> StdResult<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 ["luau", "lua"] {
|
||||
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"] {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -196,118 +192,145 @@ impl Require for TextRequirer {
|
||||
chunk_name.starts_with('@')
|
||||
}
|
||||
|
||||
fn reset(&self, chunk_name: &str) -> StdResult<(), NavigateError> {
|
||||
fn reset(&mut self, chunk_name: &str) -> StdResult<(), NavigateError> {
|
||||
if !chunk_name.starts_with('@') {
|
||||
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.module_path = PathBuf::new();
|
||||
|
||||
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 module_path = Self::find_module(&chunk_path)?;
|
||||
self.abs_path = chunk_path.clone();
|
||||
self.rel_path = chunk_path;
|
||||
self.module_path = module_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 module_path = Self::find_module(&abs_path)?;
|
||||
self.abs_path = abs_path;
|
||||
self.rel_path = chunk_path;
|
||||
self.module_path = module_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 module_path = Self::find_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.module_path = module_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() {
|
||||
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 module_path = Self::find_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.module_path = module_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 module_path = Self::find_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.module_path = module_path;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn is_module_present(&self) -> bool {
|
||||
self.module_path.borrow().is_file()
|
||||
fn has_module(&self) -> bool {
|
||||
self.module_path.is_file()
|
||||
}
|
||||
|
||||
fn contents(&self) -> IoResult<Vec<u8>> {
|
||||
fs::read(&*self.module_path.borrow())
|
||||
fn cache_key(&self) -> String {
|
||||
self.module_path.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.module_path).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 +338,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,60 +465,34 @@ 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")]
|
||||
@@ -511,18 +512,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"));
|
||||
})
|
||||
|
||||
+11
-10
@@ -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,
|
||||
@@ -31,7 +32,7 @@ pub use crate::{
|
||||
#[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));
|
||||
|
||||
+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.
|
||||
///
|
||||
|
||||
+23
-38
@@ -24,9 +24,7 @@ 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"))]
|
||||
@@ -41,7 +39,7 @@ use {
|
||||
std::future::{self, Future},
|
||||
};
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
#[cfg(feature = "serde")]
|
||||
use serde::Serialize;
|
||||
|
||||
pub(crate) use extra::ExtraData;
|
||||
@@ -377,7 +375,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 +486,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
|
||||
///
|
||||
@@ -636,7 +635,7 @@ impl Lua {
|
||||
/// 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.
|
||||
/// This is similar to `Lua::set_hook` but in more simplified form.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
@@ -878,7 +877,7 @@ impl Lua {
|
||||
/// not count in the stack).
|
||||
///
|
||||
/// [`Debug`]: crate::hook::Debug
|
||||
pub fn inspect_stack(&self, level: usize) -> Option<Debug> {
|
||||
pub fn inspect_stack(&self, level: usize) -> Option<Debug<'_>> {
|
||||
let lua = self.lock();
|
||||
unsafe {
|
||||
let mut ar: ffi::lua_Debug = mem::zeroed();
|
||||
@@ -1202,28 +1201,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.
|
||||
@@ -1390,8 +1368,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 +1396,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
|
||||
@@ -1978,7 +1956,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 +1964,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 +1977,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 +1985,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 +2014,13 @@ 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)
|
||||
}
|
||||
|
||||
/// 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
|
||||
@@ -2074,7 +2059,7 @@ impl Lua {
|
||||
}
|
||||
|
||||
#[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 } {
|
||||
|
||||
+34
-4
@@ -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,
|
||||
@@ -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) == 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
|
||||
}
|
||||
}
|
||||
|
||||
+68
-61
@@ -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;
|
||||
@@ -209,7 +208,7 @@ impl RawLua {
|
||||
}
|
||||
|
||||
// Init serde metatables
|
||||
#[cfg(feature = "serialize")]
|
||||
#[cfg(feature = "serde")]
|
||||
crate::serde::init_metatables(state)?;
|
||||
|
||||
Ok::<_, Error>(())
|
||||
@@ -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`]
|
||||
@@ -538,6 +537,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
|
||||
@@ -613,46 +640,11 @@ 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());
|
||||
}
|
||||
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 {
|
||||
if extra.thread_pool.len() < extra.thread_pool.capacity() {
|
||||
extra.thread_pool.push(thread.0.index);
|
||||
thread.0.drop = false; // Prevent thread from being garbage collected
|
||||
}
|
||||
@@ -702,7 +694,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 +703,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)) {
|
||||
@@ -824,21 +816,21 @@ 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());
|
||||
let index = (*self.extra.get()).ref_stack_pop();
|
||||
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 +1236,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 +1254,27 @@ 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) => {
|
||||
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 +1292,7 @@ impl RawLua {
|
||||
}
|
||||
}
|
||||
}
|
||||
None => Err(Error::CallbackDestructed),
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -1338,8 +1338,8 @@ impl RawLua {
|
||||
lua.load(
|
||||
r#"
|
||||
local poll = get_poll(...)
|
||||
local nres, res, res2 = poll()
|
||||
while true do
|
||||
local nres, res, res2 = poll()
|
||||
if nres ~= nil then
|
||||
if nres == 0 then
|
||||
return
|
||||
@@ -1347,11 +1347,18 @@ 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
|
||||
-- `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))
|
||||
end
|
||||
"#,
|
||||
)
|
||||
|
||||
+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
|
||||
}
|
||||
|
||||
+5
-5
@@ -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)
|
||||
}
|
||||
|
||||
@@ -102,12 +102,12 @@ 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 {
|
||||
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)
|
||||
let buf = unsafe { slice::from_raw_parts((*buf).as_ptr(), (*buf).len() + 1) };
|
||||
@@ -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
|
||||
|
||||
+8
-8
@@ -15,7 +15,7 @@ use crate::value::{Nil, Value};
|
||||
#[cfg(feature = "async")]
|
||||
use futures_util::future::{self, Either, Future};
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
#[cfg(feature = "serde")]
|
||||
use {
|
||||
rustc_hash::FxHashSet,
|
||||
serde::ser::{Serialize, SerializeMap, SerializeSeq, Serializer},
|
||||
@@ -613,7 +613,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 +678,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 +735,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();
|
||||
@@ -954,14 +954,14 @@ impl ObjectLike for Table {
|
||||
}
|
||||
|
||||
/// 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 +969,7 @@ impl Serialize for Table {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
#[cfg(feature = "serde")]
|
||||
impl<'a> SerializableTable<'a> {
|
||||
#[inline]
|
||||
pub(crate) fn new(
|
||||
@@ -985,7 +985,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
|
||||
|
||||
+57
-25
@@ -263,6 +263,8 @@ 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<()>
|
||||
@@ -305,29 +307,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 +325,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 +472,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 +523,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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -61,7 +61,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 {
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
+15
-21
@@ -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,
|
||||
@@ -683,22 +683,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),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -963,7 +957,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 +1008,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 +1041,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 +1072,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(..)))
|
||||
|
||||
@@ -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"));
|
||||
}),
|
||||
)?;
|
||||
|
||||
|
||||
+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)
|
||||
}
|
||||
|
||||
|
||||
+10
-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,
|
||||
@@ -357,7 +357,7 @@ impl Value {
|
||||
/// If the value is a Lua [`String`], try to convert it to [`BorrowedStr`] or return `None`
|
||||
/// otherwise.
|
||||
#[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())
|
||||
}
|
||||
|
||||
@@ -481,10 +481,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 +630,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 +639,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 +647,7 @@ impl Serialize for Value {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
#[cfg(feature = "serde")]
|
||||
impl<'a> SerializableValue<'a> {
|
||||
#[inline]
|
||||
pub(crate) fn new(
|
||||
@@ -711,7 +711,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"
|
||||
|
||||
+43
-1
@@ -9,7 +9,7 @@ use tokio::sync::Mutex;
|
||||
|
||||
use mlua::{
|
||||
Error, Function, Lua, LuaOptions, MultiValue, ObjectLike, Result, StdLib, Table, UserData,
|
||||
UserDataMethods, Value,
|
||||
UserDataMethods, UserDataRef, Value,
|
||||
};
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
@@ -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<()> {
|
||||
|
||||
@@ -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
|
||||
|
||||
+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(())
|
||||
|
||||
+46
-1
@@ -7,7 +7,9 @@ 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<()> {
|
||||
@@ -194,6 +196,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 +213,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(())
|
||||
}
|
||||
|
||||
@@ -415,5 +430,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;
|
||||
|
||||
+117
-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,19 @@ 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));
|
||||
|
||||
// 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 +202,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 +225,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,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
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
#![cfg(feature = "serialize")]
|
||||
#![cfg(feature = "serde")]
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::error::Error as StdError;
|
||||
|
||||
+4
-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()))?;
|
||||
|
||||
Reference in New Issue
Block a user