mirror of
https://github.com/mlua-rs/mlua
synced 2026-06-08 16:05:43 +00:00
Compare commits
77 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 7c2e9b5a7c | |||
| 5db545e7b4 | |||
| b88228b3d4 | |||
| 8677b57847 | |||
| 7543b0674e | |||
| 8e111058c3 | |||
| e1c0aa8491 | |||
| 7957c6868d | |||
| 9c86eefb76 | |||
| 7272e40c23 | |||
| c6ef393ce9 | |||
| d25f2fc07c | |||
| 104e242ddd | |||
| 825bdbfa04 | |||
| 1634c43f0a | |||
| d6b27de34e | |||
| 4018a17e26 | |||
| 5ebbc0868c | |||
| 3774296835 | |||
| ece66c46bf | |||
| 66b4a865c2 | |||
| 21149106ee | |||
| 74bebe6da3 | |||
| 9891e86d16 | |||
| e3c5cfdf19 | |||
| 4977b91a98 | |||
| 6317b8e0c8 | |||
| ecc09c4387 | |||
| 7bfd32750d | |||
| 23d4e2519b | |||
| 2857cb76c6 | |||
| fdc50bffc9 | |||
| 9931709ecd | |||
| 8092f00930 | |||
| d2e87943ac | |||
| 26b9bdb362 | |||
| c58f67b140 | |||
| 0c08cdaf7c | |||
| 10999babe0 | |||
| f0a995a357 | |||
| aa47324ee9 | |||
| ac6a391426 | |||
| 8e14b6e40b | |||
| c117a4c1af | |||
| 4082b354fe | |||
| 3641c98959 | |||
| b7d170ab9b | |||
| 5acf9d758d | |||
| 833790967b | |||
| a86d6ab330 | |||
| 2f8755dcc7 | |||
| 94415065c0 | |||
| bba644e83f | |||
| d5173380e3 | |||
| d9941ef409 | |||
| c715aec1f7 | |||
| cd3f45f31f | |||
| b4892c228c | |||
| 7a75c73052 | |||
| 658f2a13ea | |||
| c1395ab543 | |||
| cd6d86a5ce | |||
| 8b2d067196 | |||
| 07a5538e50 | |||
| d17dc54645 | |||
| baa8895cfb | |||
| 550d6b2991 | |||
| b3649a44e0 | |||
| 313117095c | |||
| 7221051683 | |||
| 1eb2ecb3b0 | |||
| 24b0672d99 | |||
| 08c7429531 | |||
| aa05eb4c81 | |||
| 4aa178fcc0 | |||
| 98ca880f8a | |||
| fe21ef43ba |
+13
-13
@@ -27,7 +27,8 @@ jobs:
|
||||
- name: Build ${{ matrix.lua }} vendored
|
||||
run: |
|
||||
cargo build --features "${{ matrix.lua }},vendored"
|
||||
cargo build --features "${{ matrix.lua }},vendored,async,send,serialize,macros,parking_lot,unstable"
|
||||
cargo build --features "${{ matrix.lua }},vendored,async,serialize,macros"
|
||||
cargo build --features "${{ matrix.lua }},vendored,async,serialize,macros,send"
|
||||
shell: bash
|
||||
- name: Build ${{ matrix.lua }} pkg-config
|
||||
if: ${{ matrix.os == 'ubuntu-22.04' }}
|
||||
@@ -50,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,parking_lot,unstable"
|
||||
run: cargo build --target aarch64-apple-darwin --features "${{ matrix.lua }},vendored,async,send,serialize,macros"
|
||||
|
||||
build_aarch64_cross_ubuntu:
|
||||
name: Cross-compile to aarch64-unknown-linux-gnu
|
||||
@@ -71,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,parking_lot,unstable"
|
||||
run: cargo build --target aarch64-unknown-linux-gnu --features "${{ matrix.lua }},vendored,async,send,serialize,macros"
|
||||
shell: bash
|
||||
|
||||
build_armv7_cross_ubuntu:
|
||||
@@ -93,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,parking_lot,unstable"
|
||||
run: cargo build --target armv7-unknown-linux-gnueabihf --features "${{ matrix.lua }},vendored,async,send,serialize,macros"
|
||||
shell: bash
|
||||
|
||||
test:
|
||||
@@ -122,14 +123,14 @@ jobs:
|
||||
- name: Run ${{ matrix.lua }} tests
|
||||
run: |
|
||||
cargo test --features "${{ matrix.lua }},vendored"
|
||||
cargo test --features "${{ matrix.lua }},vendored,async,send,serialize,macros,parking_lot"
|
||||
cargo test --features "${{ matrix.lua }},vendored,async,serialize,macros,parking_lot,unstable"
|
||||
cargo test --features "${{ matrix.lua }},vendored,async,serialize,macros"
|
||||
cargo test --features "${{ matrix.lua }},vendored,async,serialize,macros,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,parking_lot,unstable" -- --ignored
|
||||
TRYBUILD=overwrite cargo test --features "${{ matrix.lua }},vendored,async,send,serialize,macros" -- --ignored
|
||||
shell: bash
|
||||
|
||||
test_with_sanitizer:
|
||||
@@ -153,7 +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,send,serialize,macros,parking_lot,unstable" --target x86_64-unknown-linux-gnu -- --skip test_too_many_recursions
|
||||
cargo test --tests --features "${{ matrix.lua }},vendored,async,serialize,macros" --target x86_64-unknown-linux-gnu -- --skip test_too_many_recursions
|
||||
cargo test --tests --features "${{ matrix.lua }},vendored,async,serialize,macros,send" --target x86_64-unknown-linux-gnu -- --skip test_too_many_recursions
|
||||
shell: bash
|
||||
env:
|
||||
RUSTFLAGS: -Z sanitizer=address
|
||||
@@ -226,17 +228,15 @@ jobs:
|
||||
- name: Run ${{ matrix.lua }} tests
|
||||
run: |
|
||||
cargo test --tests --features "${{ matrix.lua }},vendored"
|
||||
cargo test --tests --features "${{ matrix.lua }},vendored,async,send,serialize,macros,parking_lot"
|
||||
cargo test --tests --features "${{ matrix.lua }},vendored,async,serialize,macros,parking_lot,unstable"
|
||||
cargo test --tests --features "${{ matrix.lua }},vendored,async,serialize,macros"
|
||||
|
||||
rustfmt:
|
||||
name: Rustfmt
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
- uses: dtolnay/rust-toolchain@nightly
|
||||
with:
|
||||
toolchain: stable
|
||||
components: rustfmt
|
||||
- run: cargo fmt -- --check
|
||||
|
||||
@@ -255,4 +255,4 @@ jobs:
|
||||
- uses: giraffate/clippy-action@v1
|
||||
with:
|
||||
reporter: 'github-pr-review'
|
||||
clippy_flags: --features "${{ matrix.lua }},vendored,async,send,serialize,macros,parking_lot,unstable"
|
||||
clippy_flags: --features "${{ matrix.lua }},vendored,async,send,serialize,macros"
|
||||
|
||||
@@ -1,3 +1,29 @@
|
||||
## v0.10.0-beta.2
|
||||
|
||||
- Updated `ThreadStatus` enum to include `Running` and `Finished` variants.
|
||||
- `Error::CoroutineInactive` renamed to `Error::CoroutineUnresumable`.
|
||||
- `IntoLua`/`IntoLuaMulti` now uses `impl trait` syntax for args (shorten from `a.get::<_, T>` to `a.get::<T>`).
|
||||
- Removed undocumented `Lua::into_static`/`from_static` methods.
|
||||
- Futures now require `Send` bound if `send` feature is enabled.
|
||||
- Dropped lifetime from `UserDataMethods` and `UserDataFields` traits.
|
||||
- `Compiler::compile()` now returns `Result` (Luau).
|
||||
- Removed `Clone` requirement from `UserDataFields::add_field()`.
|
||||
- `TableExt` and `AnyUserDataExt` traits were combined into `ObjectLike` trait.
|
||||
- Disabled `send` feature in module mode (since we don't have exclusive access to Lua).
|
||||
- `Chunk::set_environment()` takes `Table` instead of `IntoLua` type.
|
||||
- Reduced the compile time contribution of `next_key_seed` and `next_value_seed`.
|
||||
- Reduced the compile time contribution of `serde_userdata`.
|
||||
- Performance improvements.
|
||||
|
||||
## v0.10.0-beta.1
|
||||
|
||||
- Dropped `'lua` lifetime (subtypes now store a weak reference to Lua)
|
||||
- Removed (experimental) owned types (they no longer needed)
|
||||
- Make Lua types truly `Send` and `Sync` (when enabling `send` feature flag)
|
||||
- Removed `UserData` impl for Rc/Arc types ("any" userdata functions can be used instead)
|
||||
- `Lua::replace_registry_value` takes `&mut RegistryKey`
|
||||
- `Lua::scope` temporary disabled (will be re-added in the next release)
|
||||
|
||||
## v0.9.9
|
||||
|
||||
- Minimal Luau updated to 0.629
|
||||
|
||||
+9
-12
@@ -1,8 +1,8 @@
|
||||
[package]
|
||||
name = "mlua"
|
||||
version = "0.9.9" # remember to update mlua_derive
|
||||
version = "0.10.0-beta.2" # remember to update mlua_derive
|
||||
authors = ["Aleksandr Orlenko <zxteam@pm.me>", "kyren <catherine@kyju.org>"]
|
||||
rust-version = "1.71"
|
||||
rust-version = "1.79.0"
|
||||
edition = "2021"
|
||||
repository = "https://github.com/khvzak/mlua"
|
||||
documentation = "https://docs.rs/mlua"
|
||||
@@ -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", "parking_lot", "unstable"]
|
||||
features = ["lua54", "vendored", "async", "send", "serialize", "macros"]
|
||||
rustdoc-args = ["--cfg", "docsrs"]
|
||||
|
||||
[workspace]
|
||||
@@ -38,31 +38,28 @@ luau-vector4 = ["luau", "ffi/luau-vector4"]
|
||||
vendored = ["ffi/vendored"]
|
||||
module = ["dep:mlua_derive", "ffi/module"]
|
||||
async = ["dep:futures-util"]
|
||||
send = []
|
||||
send = ["parking_lot/send_guard"]
|
||||
serialize = ["dep:serde", "dep:erased-serde", "dep:serde-value"]
|
||||
macros = ["mlua_derive/macros"]
|
||||
unstable = []
|
||||
|
||||
[dependencies]
|
||||
mlua_derive = { version = "=0.9.3", optional = true, path = "mlua_derive" }
|
||||
mlua_derive = { version = "=0.10.0-beta.1", optional = true, path = "mlua_derive" }
|
||||
bstr = { version = "1.0", features = ["std"], default-features = false }
|
||||
once_cell = { version = "1.0" }
|
||||
num-traits = { version = "0.2.14" }
|
||||
rustc-hash = "2.0"
|
||||
futures-util = { version = "0.3", optional = true, default-features = false, features = ["std"] }
|
||||
serde = { version = "1.0", optional = true }
|
||||
erased-serde = { version = "0.4", optional = true }
|
||||
serde-value = { version = "0.7", optional = true }
|
||||
parking_lot = { version = "0.12", optional = true }
|
||||
parking_lot = { version = "0.12", features = ["arc_lock"] }
|
||||
|
||||
ffi = { package = "mlua-sys", version = "0.6.1", path = "mlua-sys" }
|
||||
ffi = { package = "mlua-sys", version = "0.6.3", path = "mlua-sys" }
|
||||
|
||||
[target.'cfg(unix)'.dependencies]
|
||||
libloading = { version = "0.8", optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
trybuild = "1.0"
|
||||
futures = "0.3.5"
|
||||
hyper = { version = "1.2", features = ["full"] }
|
||||
hyper-util = { version = "0.1.3", features = ["full"] }
|
||||
http-body-util = "0.1.1"
|
||||
@@ -102,11 +99,11 @@ required-features = ["async", "serialize", "macros"]
|
||||
|
||||
[[example]]
|
||||
name = "async_http_server"
|
||||
required-features = ["async", "macros"]
|
||||
required-features = ["async", "macros", "send"]
|
||||
|
||||
[[example]]
|
||||
name = "async_tcp_server"
|
||||
required-features = ["async", "macros"]
|
||||
required-features = ["async", "macros", "send"]
|
||||
|
||||
[[example]]
|
||||
name = "guided_tour"
|
||||
|
||||
@@ -17,9 +17,7 @@
|
||||
[Benchmarks]: https://github.com/khvzak/script-bench-rs
|
||||
[FAQ]: FAQ.md
|
||||
|
||||
> **Note**
|
||||
>
|
||||
> See v0.9 [release notes](https://github.com/khvzak/mlua/blob/main/docs/release_notes/v0.9.md).
|
||||
# The main branch is the v0.10, development version of `mlua`. Please see the [v0.9](https://github.com/mlua-rs/mlua/tree/v0.9) branch for the stable versions of `mlua`.
|
||||
|
||||
`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.
|
||||
@@ -40,23 +38,21 @@ WebAssembly (WASM) is supported through `wasm32-unknown-emscripten` target for a
|
||||
`mlua` uses feature flags to reduce the amount of dependencies, 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`: activate Lua [5.4] support
|
||||
* `lua53`: activate Lua [5.3] support
|
||||
* `lua52`: activate Lua [5.2] support
|
||||
* `lua51`: activate Lua [5.1] support
|
||||
* `luajit`: activate [LuaJIT] support
|
||||
* `luajit52`: activate [LuaJIT] support with partial compatibility with Lua 5.2
|
||||
* `luau`: activate [Luau] support (auto vendored mode)
|
||||
* `luau-jit`: activate [Luau] support with JIT backend.
|
||||
* `luau-vector4`: activate [Luau] support with 4-dimensional vector.
|
||||
* `lua54`: enable Lua [5.4] support
|
||||
* `lua53`: enable Lua [5.3] support
|
||||
* `lua52`: enable Lua [5.2] support
|
||||
* `lua51`: enable Lua [5.1] support
|
||||
* `luajit`: enable [LuaJIT] support
|
||||
* `luajit52`: enable [LuaJIT] support with partial compatibility with Lua 5.2
|
||||
* `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
|
||||
* `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` transferable across thread boundaries (adds [`Send`] requirement to `mlua::Function` and `mlua::UserData`)
|
||||
* `send`: make `mlua::Lua: Send + Sync` (adds [`Send`] requirement to `mlua::Function` and `mlua::UserData`)
|
||||
* `serialize`: add serialization and deserialization support to `mlua` types using [serde] framework
|
||||
* `macros`: enable procedural macros (such as `chunk!`)
|
||||
* `parking_lot`: support UserData types wrapped in [parking_lot]'s primitives (`Arc<Mutex>` and `Arc<RwLock>`)
|
||||
* `unstable`: enable **unstable** features. The public API of these features may break between releases.
|
||||
|
||||
[5.4]: https://www.lua.org/manual/5.4/manual.html
|
||||
[5.3]: https://www.lua.org/manual/5.3/manual.html
|
||||
@@ -70,7 +66,6 @@ Below is a list of the available feature flags. By default `mlua` does not enabl
|
||||
[async-std]: https://github.com/async-rs/async-std
|
||||
[`Send`]: https://doc.rust-lang.org/std/marker/trait.Send.html
|
||||
[serde]: https://github.com/serde-rs/serde
|
||||
[parking_lot]: https://github.com/Amanieu/parking_lot
|
||||
|
||||
### Async/await support
|
||||
|
||||
@@ -94,7 +89,7 @@ cargo run --example async_http_client --features=lua54,async,macros
|
||||
cargo run --example async_http_reqwest --features=lua54,async,macros,serialize
|
||||
|
||||
# async http server
|
||||
cargo run --example async_http_server --features=lua54,async,macros
|
||||
cargo run --example async_http_server --features=lua54,async,macros,send
|
||||
curl -v http://localhost:3000
|
||||
```
|
||||
|
||||
@@ -198,7 +193,7 @@ $ lua5.4 -e 'require("my_module").hello("world")'
|
||||
hello, world!
|
||||
```
|
||||
|
||||
On macOS, you need to set additional linker arguments. One option is to compile with `cargo rustc --release -- -C link-arg=-undefined -C link-arg=dynamic_lookup`, the other is to create a `.cargo/config` with the following content:
|
||||
On macOS, you need to set additional linker arguments. One option is to compile with `cargo rustc --release -- -C link-arg=-undefined -C link-arg=dynamic_lookup`, the other is to create a `.cargo/config.toml` with the following content:
|
||||
``` toml
|
||||
[target.x86_64-apple-darwin]
|
||||
rustflags = [
|
||||
|
||||
+14
-29
@@ -74,7 +74,7 @@ fn table_get_set(c: &mut Criterion) {
|
||||
.enumerate()
|
||||
{
|
||||
table.raw_set(s, i).unwrap();
|
||||
assert_eq!(table.raw_get::<_, usize>(s).unwrap(), i);
|
||||
assert_eq!(table.raw_get::<usize>(s).unwrap(), i);
|
||||
}
|
||||
},
|
||||
BatchSize::SmallInput,
|
||||
@@ -153,7 +153,7 @@ fn function_call_sum(c: &mut Criterion) {
|
||||
b.iter_batched(
|
||||
|| collect_gc_twice(&lua),
|
||||
|_| {
|
||||
assert_eq!(sum.call::<_, i64>((10, 20, 30)).unwrap(), 0);
|
||||
assert_eq!(sum.call::<i64>((10, 20, 30)).unwrap(), 0);
|
||||
},
|
||||
BatchSize::SmallInput,
|
||||
);
|
||||
@@ -172,7 +172,7 @@ fn function_call_lua_sum(c: &mut Criterion) {
|
||||
b.iter_batched(
|
||||
|| collect_gc_twice(&lua),
|
||||
|_| {
|
||||
assert_eq!(sum.call::<_, i64>((10, 20, 30)).unwrap(), 0);
|
||||
assert_eq!(sum.call::<i64>((10, 20, 30)).unwrap(), 0);
|
||||
},
|
||||
BatchSize::SmallInput,
|
||||
);
|
||||
@@ -183,9 +183,7 @@ fn function_call_concat(c: &mut Criterion) {
|
||||
let lua = Lua::new();
|
||||
|
||||
let concat = lua
|
||||
.create_function(|_, (a, b): (LuaString, LuaString)| {
|
||||
Ok(format!("{}{}", a.to_str()?, b.to_str()?))
|
||||
})
|
||||
.create_function(|_, (a, b): (LuaString, LuaString)| Ok(format!("{}{}", a.to_str()?, b.to_str()?)))
|
||||
.unwrap();
|
||||
let i = AtomicUsize::new(0);
|
||||
|
||||
@@ -196,10 +194,7 @@ fn function_call_concat(c: &mut Criterion) {
|
||||
i.fetch_add(1, Ordering::Relaxed)
|
||||
},
|
||||
|i| {
|
||||
assert_eq!(
|
||||
concat.call::<_, LuaString>(("num:", i)).unwrap(),
|
||||
format!("num:{i}")
|
||||
);
|
||||
assert_eq!(concat.call::<LuaString>(("num:", i)).unwrap(), format!("num:{i}"));
|
||||
},
|
||||
BatchSize::SmallInput,
|
||||
);
|
||||
@@ -222,10 +217,7 @@ fn function_call_lua_concat(c: &mut Criterion) {
|
||||
i.fetch_add(1, Ordering::Relaxed)
|
||||
},
|
||||
|i| {
|
||||
assert_eq!(
|
||||
concat.call::<_, LuaString>(("num:", i)).unwrap(),
|
||||
format!("num:{i}")
|
||||
);
|
||||
assert_eq!(concat.call::<LuaString>(("num:", i)).unwrap(), format!("num:{i}"));
|
||||
},
|
||||
BatchSize::SmallInput,
|
||||
);
|
||||
@@ -248,7 +240,7 @@ fn function_async_call_sum(c: &mut Criterion) {
|
||||
b.to_async(rt).iter_batched(
|
||||
|| collect_gc_twice(&lua),
|
||||
|_| async {
|
||||
assert_eq!(sum.call_async::<_, i64>((10, 20, 30)).await.unwrap(), 0);
|
||||
assert_eq!(sum.call_async::<i64>((10, 20, 30)).await.unwrap(), 0);
|
||||
},
|
||||
BatchSize::SmallInput,
|
||||
);
|
||||
@@ -305,7 +297,7 @@ fn userdata_create(c: &mut Criterion) {
|
||||
fn userdata_call_index(c: &mut Criterion) {
|
||||
struct UserData(#[allow(unused)] i64);
|
||||
impl LuaUserData for UserData {
|
||||
fn add_methods<'lua, M: LuaUserDataMethods<'lua, Self>>(methods: &mut M) {
|
||||
fn add_methods<M: LuaUserDataMethods<Self>>(methods: &mut M) {
|
||||
methods.add_meta_method(LuaMetaMethod::Index, move |_, _, key: LuaString| Ok(key));
|
||||
}
|
||||
}
|
||||
@@ -321,7 +313,7 @@ fn userdata_call_index(c: &mut Criterion) {
|
||||
b.iter_batched(
|
||||
|| collect_gc_twice(&lua),
|
||||
|_| {
|
||||
assert_eq!(index.call::<_, LuaString>(&ud).unwrap(), "test");
|
||||
assert_eq!(index.call::<LuaString>(&ud).unwrap(), "test");
|
||||
},
|
||||
BatchSize::SmallInput,
|
||||
);
|
||||
@@ -331,7 +323,7 @@ fn userdata_call_index(c: &mut Criterion) {
|
||||
fn userdata_call_method(c: &mut Criterion) {
|
||||
struct UserData(i64);
|
||||
impl LuaUserData for UserData {
|
||||
fn add_methods<'lua, M: LuaUserDataMethods<'lua, Self>>(methods: &mut M) {
|
||||
fn add_methods<M: LuaUserDataMethods<Self>>(methods: &mut M) {
|
||||
methods.add_method("add", |_, this, i: i64| Ok(this.0 + i));
|
||||
}
|
||||
}
|
||||
@@ -351,7 +343,7 @@ fn userdata_call_method(c: &mut Criterion) {
|
||||
i.fetch_add(1, Ordering::Relaxed)
|
||||
},
|
||||
|i| {
|
||||
assert_eq!(method.call::<_, usize>((&ud, i)).unwrap(), 123 + i);
|
||||
assert_eq!(method.call::<usize>((&ud, i)).unwrap(), 123 + i);
|
||||
},
|
||||
BatchSize::SmallInput,
|
||||
);
|
||||
@@ -361,7 +353,7 @@ fn userdata_call_method(c: &mut Criterion) {
|
||||
fn userdata_async_call_method(c: &mut Criterion) {
|
||||
struct UserData(i64);
|
||||
impl LuaUserData for UserData {
|
||||
fn add_methods<'lua, M: LuaUserDataMethods<'lua, Self>>(methods: &mut M) {
|
||||
fn add_methods<M: LuaUserDataMethods<Self>>(methods: &mut M) {
|
||||
methods.add_async_method("add", |_, this, i: i64| async move {
|
||||
task::yield_now().await;
|
||||
Ok(this.0 + i)
|
||||
@@ -383,17 +375,10 @@ fn userdata_async_call_method(c: &mut Criterion) {
|
||||
b.to_async(rt).iter_batched(
|
||||
|| {
|
||||
collect_gc_twice(&lua);
|
||||
(
|
||||
method.clone(),
|
||||
ud.clone(),
|
||||
i.fetch_add(1, Ordering::Relaxed),
|
||||
)
|
||||
(method.clone(), ud.clone(), i.fetch_add(1, Ordering::Relaxed))
|
||||
},
|
||||
|(method, ud, i)| async move {
|
||||
assert_eq!(
|
||||
method.call_async::<_, usize>((ud, i)).await.unwrap(),
|
||||
123 + i
|
||||
);
|
||||
assert_eq!(method.call_async::<usize>((ud, i)).await.unwrap(), 123 + i);
|
||||
},
|
||||
BatchSize::SmallInput,
|
||||
);
|
||||
|
||||
+2
-2
@@ -37,7 +37,7 @@ fn encode_json(c: &mut Criterion) {
|
||||
b.iter_batched(
|
||||
|| collect_gc_twice(&lua),
|
||||
|_| {
|
||||
encode.call::<_, LuaString>(&table).unwrap();
|
||||
encode.call::<LuaString>(&table).unwrap();
|
||||
},
|
||||
BatchSize::SmallInput,
|
||||
);
|
||||
@@ -69,7 +69,7 @@ fn decode_json(c: &mut Criterion) {
|
||||
b.iter_batched(
|
||||
|| collect_gc_twice(&lua),
|
||||
|_| {
|
||||
decode.call::<_, LuaTable>(json).unwrap();
|
||||
decode.call::<LuaTable>(json).unwrap();
|
||||
},
|
||||
BatchSize::SmallInput,
|
||||
);
|
||||
|
||||
@@ -10,9 +10,9 @@ use mlua::{chunk, ExternalResult, Lua, Result, UserData, UserDataMethods};
|
||||
struct BodyReader(Incoming);
|
||||
|
||||
impl UserData for BodyReader {
|
||||
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
|
||||
fn add_methods<M: UserDataMethods<Self>>(methods: &mut M) {
|
||||
// Every call returns a next chunk
|
||||
methods.add_async_method_mut("read", |lua, reader, ()| async move {
|
||||
methods.add_async_method_mut("read", |lua, mut reader, ()| async move {
|
||||
if let Some(bytes) = reader.0.frame().await {
|
||||
if let Some(bytes) = bytes.into_lua_err()?.data_ref() {
|
||||
return Some(lua.create_string(&bytes)).transpose();
|
||||
|
||||
@@ -1,27 +1,23 @@
|
||||
use std::convert::Infallible;
|
||||
use std::future::Future;
|
||||
use std::net::SocketAddr;
|
||||
use std::rc::Rc;
|
||||
use std::pin::Pin;
|
||||
|
||||
use futures::future::LocalBoxFuture;
|
||||
use http_body_util::{combinators::BoxBody, BodyExt as _, Empty, Full};
|
||||
use http_body_util::combinators::BoxBody;
|
||||
use http_body_util::{BodyExt as _, Empty, Full};
|
||||
use hyper::body::{Bytes, Incoming};
|
||||
use hyper::server::conn::http1;
|
||||
use hyper::{Request, Response};
|
||||
use hyper_util::rt::TokioIo;
|
||||
use hyper_util::server::conn::auto::Builder as ServerConnBuilder;
|
||||
use tokio::net::TcpListener;
|
||||
use tokio::task::LocalSet;
|
||||
|
||||
use mlua::{
|
||||
chunk, Error as LuaError, Function, Lua, RegistryKey, String as LuaString, Table, UserData,
|
||||
UserDataMethods,
|
||||
};
|
||||
use mlua::{chunk, Error as LuaError, Function, Lua, String as LuaString, Table, UserData, UserDataMethods};
|
||||
|
||||
/// Wrapper around incoming request that implements UserData
|
||||
struct LuaRequest(SocketAddr, Request<Incoming>);
|
||||
|
||||
impl UserData for LuaRequest {
|
||||
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
|
||||
fn add_methods<M: UserDataMethods<Self>>(methods: &mut M) {
|
||||
methods.add_method("remote_addr", |_, req, ()| Ok((req.0).to_string()));
|
||||
methods.add_method("method", |_, req, ()| Ok((req.1).method().to_string()));
|
||||
methods.add_method("path", |_, req, ()| Ok(req.1.uri().path().to_string()));
|
||||
@@ -31,50 +27,43 @@ impl UserData for LuaRequest {
|
||||
/// Service that handles incoming requests
|
||||
#[derive(Clone)]
|
||||
pub struct Svc {
|
||||
lua: Rc<Lua>,
|
||||
handler: Rc<RegistryKey>,
|
||||
handler: Function,
|
||||
peer_addr: SocketAddr,
|
||||
}
|
||||
|
||||
impl Svc {
|
||||
pub fn new(lua: Rc<Lua>, handler: Rc<RegistryKey>, peer_addr: SocketAddr) -> Self {
|
||||
Self {
|
||||
lua,
|
||||
handler,
|
||||
peer_addr,
|
||||
}
|
||||
pub fn new(handler: Function, peer_addr: SocketAddr) -> Self {
|
||||
Self { handler, peer_addr }
|
||||
}
|
||||
}
|
||||
|
||||
impl hyper::service::Service<Request<Incoming>> for Svc {
|
||||
type Response = Response<BoxBody<Bytes, Infallible>>;
|
||||
type Error = LuaError;
|
||||
type Future = LocalBoxFuture<'static, Result<Self::Response, Self::Error>>;
|
||||
type Future = Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>> + Send>>;
|
||||
|
||||
fn call(&self, req: Request<Incoming>) -> Self::Future {
|
||||
// If handler returns an error then generate 5xx response
|
||||
let lua = self.lua.clone();
|
||||
let handler_key = self.handler.clone();
|
||||
let handler = self.handler.clone();
|
||||
let lua_req = LuaRequest(self.peer_addr, req);
|
||||
Box::pin(async move {
|
||||
let handler: Function = lua.registry_value(&handler_key)?;
|
||||
match handler.call_async::<_, Table>(lua_req).await {
|
||||
match handler.call_async::<Table>(lua_req).await {
|
||||
Ok(lua_resp) => {
|
||||
let status = lua_resp.get::<_, Option<u16>>("status")?.unwrap_or(200);
|
||||
let status = lua_resp.get::<Option<u16>>("status")?.unwrap_or(200);
|
||||
let mut resp = Response::builder().status(status);
|
||||
|
||||
// Set headers
|
||||
if let Some(headers) = lua_resp.get::<_, Option<Table>>("headers")? {
|
||||
if let Some(headers) = lua_resp.get::<Option<Table>>("headers")? {
|
||||
for pair in headers.pairs::<String, LuaString>() {
|
||||
let (h, v) = pair?;
|
||||
resp = resp.header(&h, v.as_bytes());
|
||||
resp = resp.header(&h, &*v.as_bytes());
|
||||
}
|
||||
}
|
||||
|
||||
// Set body
|
||||
let body = lua_resp
|
||||
.get::<_, Option<LuaString>>("body")?
|
||||
.map(|b| Full::new(Bytes::copy_from_slice(b.as_bytes())).boxed())
|
||||
.get::<Option<LuaString>>("body")?
|
||||
.map(|b| Full::new(Bytes::copy_from_slice(&b.as_bytes())).boxed())
|
||||
.unwrap_or_else(|| Empty::<Bytes>::new().boxed());
|
||||
|
||||
Ok(resp.body(body).unwrap())
|
||||
@@ -93,10 +82,10 @@ impl hyper::service::Service<Request<Incoming>> for Svc {
|
||||
|
||||
#[tokio::main(flavor = "current_thread")]
|
||||
async fn main() {
|
||||
let lua = Rc::new(Lua::new());
|
||||
let lua = Lua::new();
|
||||
|
||||
// Create Lua handler function
|
||||
let handler: RegistryKey = lua
|
||||
let handler = lua
|
||||
.load(chunk! {
|
||||
function(req)
|
||||
return {
|
||||
@@ -110,15 +99,13 @@ async fn main() {
|
||||
}
|
||||
end
|
||||
})
|
||||
.eval()
|
||||
.eval::<Function>()
|
||||
.expect("Failed to create Lua handler");
|
||||
let handler = Rc::new(handler);
|
||||
|
||||
let listen_addr = "127.0.0.1:3000";
|
||||
let listener = TcpListener::bind(listen_addr).await.unwrap();
|
||||
println!("Listening on http://{listen_addr}");
|
||||
|
||||
let local = LocalSet::new();
|
||||
loop {
|
||||
let (stream, peer_addr) = match listener.accept().await {
|
||||
Ok(x) => x,
|
||||
@@ -128,29 +115,14 @@ async fn main() {
|
||||
}
|
||||
};
|
||||
|
||||
let svc = Svc::new(lua.clone(), handler.clone(), peer_addr);
|
||||
local
|
||||
.run_until(async move {
|
||||
let result = ServerConnBuilder::new(LocalExec)
|
||||
.http1()
|
||||
.serve_connection(TokioIo::new(stream), svc)
|
||||
.await;
|
||||
if let Err(err) = result {
|
||||
eprintln!("Error serving connection: {err:?}");
|
||||
}
|
||||
})
|
||||
.await;
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
struct LocalExec;
|
||||
|
||||
impl<F> hyper::rt::Executor<F> for LocalExec
|
||||
where
|
||||
F: Future + 'static, // not requiring `Send`
|
||||
{
|
||||
fn execute(&self, fut: F) {
|
||||
tokio::task::spawn_local(fut);
|
||||
let svc = Svc::new(handler.clone(), peer_addr);
|
||||
tokio::task::spawn(async move {
|
||||
if let Err(err) = http1::Builder::new()
|
||||
.serve_connection(TokioIo::new(stream), svc)
|
||||
.await
|
||||
{
|
||||
eprintln!("Error serving connection: {:?}", err);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,48 +1,42 @@
|
||||
use std::io;
|
||||
use std::net::SocketAddr;
|
||||
use std::rc::Rc;
|
||||
|
||||
use tokio::io::{AsyncReadExt, AsyncWriteExt};
|
||||
use tokio::net::{TcpListener, TcpStream};
|
||||
use tokio::task;
|
||||
|
||||
use mlua::{chunk, Function, Lua, RegistryKey, String as LuaString, UserData, UserDataMethods};
|
||||
use mlua::{chunk, BString, Function, Lua, UserData, UserDataMethods};
|
||||
|
||||
struct LuaTcpStream(TcpStream);
|
||||
|
||||
impl UserData for LuaTcpStream {
|
||||
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
|
||||
methods.add_method("peer_addr", |_, this, ()| {
|
||||
Ok(this.0.peer_addr()?.to_string())
|
||||
});
|
||||
fn add_methods<M: UserDataMethods<Self>>(methods: &mut M) {
|
||||
methods.add_method("peer_addr", |_, this, ()| Ok(this.0.peer_addr()?.to_string()));
|
||||
|
||||
methods.add_async_method_mut("read", |lua, this, size| async move {
|
||||
methods.add_async_method_mut("read", |lua, mut this, size| async move {
|
||||
let mut buf = vec![0; size];
|
||||
let n = this.0.read(&mut buf).await?;
|
||||
buf.truncate(n);
|
||||
lua.create_string(&buf)
|
||||
});
|
||||
|
||||
methods.add_async_method_mut("write", |_, this, data: LuaString| async move {
|
||||
let n = this.0.write(&data.as_bytes()).await?;
|
||||
methods.add_async_method_mut("write", |_, mut this, data: BString| async move {
|
||||
let n = this.0.write(&data).await?;
|
||||
Ok(n)
|
||||
});
|
||||
|
||||
methods.add_async_method_mut("close", |_, this, ()| async move {
|
||||
methods.add_async_method_mut("close", |_, mut this, ()| async move {
|
||||
this.0.shutdown().await?;
|
||||
Ok(())
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async fn run_server(lua: Lua, handler: RegistryKey) -> io::Result<()> {
|
||||
async fn run_server(handler: Function) -> io::Result<()> {
|
||||
let addr: SocketAddr = ([127, 0, 0, 1], 3000).into();
|
||||
let listener = TcpListener::bind(addr).await.expect("cannot bind addr");
|
||||
|
||||
println!("Listening on {}", addr);
|
||||
|
||||
let lua = Rc::new(lua);
|
||||
let handler = Rc::new(handler);
|
||||
loop {
|
||||
let (stream, _) = match listener.accept().await {
|
||||
Ok(res) => res,
|
||||
@@ -50,15 +44,10 @@ async fn run_server(lua: Lua, handler: RegistryKey) -> io::Result<()> {
|
||||
Err(err) => return Err(err),
|
||||
};
|
||||
|
||||
let lua = lua.clone();
|
||||
let handler = handler.clone();
|
||||
task::spawn_local(async move {
|
||||
let handler: Function = lua
|
||||
.registry_value(&handler)
|
||||
.expect("cannot get Lua handler");
|
||||
|
||||
tokio::task::spawn(async move {
|
||||
let stream = LuaTcpStream(stream);
|
||||
if let Err(err) = handler.call_async::<_, ()>(stream).await {
|
||||
if let Err(err) = handler.call_async::<()>(stream).await {
|
||||
eprintln!("{}", err);
|
||||
}
|
||||
});
|
||||
@@ -70,7 +59,7 @@ async fn main() {
|
||||
let lua = Lua::new();
|
||||
|
||||
// Create Lua handler function
|
||||
let handler_fn = lua
|
||||
let handler = lua
|
||||
.load(chunk! {
|
||||
function(stream)
|
||||
local peer_addr = stream:peer_addr()
|
||||
@@ -92,15 +81,7 @@ async fn main() {
|
||||
.eval::<Function>()
|
||||
.expect("cannot create Lua handler");
|
||||
|
||||
// Store it in the Registry
|
||||
let handler = lua
|
||||
.create_registry_value(handler_fn)
|
||||
.expect("cannot store Lua handler");
|
||||
|
||||
task::LocalSet::new()
|
||||
.run_until(run_server(lua, handler))
|
||||
.await
|
||||
.expect("cannot run server")
|
||||
run_server(handler).await.expect("cannot run server")
|
||||
}
|
||||
|
||||
fn is_transient_error(e: &io::Error) -> bool {
|
||||
|
||||
+14
-19
@@ -1,9 +1,7 @@
|
||||
use std::f32;
|
||||
use std::iter::FromIterator;
|
||||
|
||||
use mlua::{
|
||||
chunk, FromLua, Function, Lua, MetaMethod, Result, UserData, UserDataMethods, Value, Variadic,
|
||||
};
|
||||
use mlua::{chunk, FromLua, Function, Lua, MetaMethod, Result, UserData, UserDataMethods, Value, Variadic};
|
||||
|
||||
fn main() -> Result<()> {
|
||||
// You can create a new Lua state with `Lua::new()`. This loads the default Lua std library
|
||||
@@ -19,8 +17,8 @@ fn main() -> Result<()> {
|
||||
globals.set("string_var", "hello")?;
|
||||
globals.set("int_var", 42)?;
|
||||
|
||||
assert_eq!(globals.get::<_, String>("string_var")?, "hello");
|
||||
assert_eq!(globals.get::<_, i64>("int_var")?, 42);
|
||||
assert_eq!(globals.get::<String>("string_var")?, "hello");
|
||||
assert_eq!(globals.get::<i64>("int_var")?, 42);
|
||||
|
||||
// You can load and evaluate Lua code. The returned type of `Lua::load` is a builder
|
||||
// that allows you to change settings before running Lua code. Here, we are using it to set
|
||||
@@ -34,7 +32,7 @@ fn main() -> Result<()> {
|
||||
)
|
||||
.set_name("example code")
|
||||
.exec()?;
|
||||
assert_eq!(globals.get::<_, String>("global")?, "foobar");
|
||||
assert_eq!(globals.get::<String>("global")?, "foobar");
|
||||
|
||||
assert_eq!(lua.load("1 + 1").eval::<i32>()?, 2);
|
||||
assert_eq!(lua.load("false == false").eval::<bool>()?, true);
|
||||
@@ -87,16 +85,16 @@ fn main() -> Result<()> {
|
||||
// You can load Lua functions
|
||||
|
||||
let print: Function = globals.get("print")?;
|
||||
print.call::<_, ()>("hello from rust")?;
|
||||
print.call::<()>("hello from rust")?;
|
||||
|
||||
// This API generally handles variadic using tuples. This is one way to call a function with
|
||||
// multiple parameters:
|
||||
|
||||
print.call::<_, ()>(("hello", "again", "from", "rust"))?;
|
||||
print.call::<()>(("hello", "again", "from", "rust"))?;
|
||||
|
||||
// But, you can also pass variadic arguments with the `Variadic` type.
|
||||
|
||||
print.call::<_, ()>(Variadic::from_iter(
|
||||
print.call::<()>(Variadic::from_iter(
|
||||
["hello", "yet", "again", "from", "rust"].iter().cloned(),
|
||||
))?;
|
||||
|
||||
@@ -154,8 +152,8 @@ fn main() -> Result<()> {
|
||||
struct Vec2(f32, f32);
|
||||
|
||||
// We can implement `FromLua` trait for our `Vec2` to return a copy
|
||||
impl<'lua> FromLua<'lua> for Vec2 {
|
||||
fn from_lua(value: Value<'lua>, _: &'lua Lua) -> Result<Self> {
|
||||
impl FromLua for Vec2 {
|
||||
fn from_lua(value: Value, _: &Lua) -> Result<Self> {
|
||||
match value {
|
||||
Value::UserData(ud) => Ok(*ud.borrow::<Self>()?),
|
||||
_ => unreachable!(),
|
||||
@@ -164,7 +162,7 @@ fn main() -> Result<()> {
|
||||
}
|
||||
|
||||
impl UserData for Vec2 {
|
||||
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
|
||||
fn add_methods<M: UserDataMethods<Self>>(methods: &mut M) {
|
||||
methods.add_method("magnitude", |_, vec, ()| {
|
||||
let mag_squared = vec.0 * vec.0 + vec.1 * vec.1;
|
||||
Ok(mag_squared.sqrt())
|
||||
@@ -179,19 +177,15 @@ fn main() -> Result<()> {
|
||||
let vec2_constructor = lua.create_function(|_, (x, y): (f32, f32)| Ok(Vec2(x, y)))?;
|
||||
globals.set("vec2", vec2_constructor)?;
|
||||
|
||||
assert!(
|
||||
(lua.load("(vec2(1, 2) + vec2(2, 2)):magnitude()")
|
||||
.eval::<f32>()?
|
||||
- 5.0)
|
||||
.abs()
|
||||
< f32::EPSILON
|
||||
);
|
||||
assert!((lua.load("(vec2(1, 2) + vec2(2, 2)):magnitude()").eval::<f32>()? - 5.0).abs() < f32::EPSILON);
|
||||
|
||||
// Normally, Rust types passed to `Lua` must be `'static`, because there is no way to be
|
||||
// sure of their lifetime inside the Lua state. There is, however, a limited way to lift this
|
||||
// requirement. You can call `Lua::scope` to create userdata and callbacks types that only live
|
||||
// for as long as the call to scope, but do not have to be `'static` (and `Send`).
|
||||
|
||||
// TODO: Re-enable this
|
||||
/*
|
||||
{
|
||||
let mut rust_val = 0;
|
||||
|
||||
@@ -213,6 +207,7 @@ fn main() -> Result<()> {
|
||||
|
||||
assert_eq!(rust_val, 42);
|
||||
}
|
||||
*/
|
||||
|
||||
// We were able to run our 'sketchy' function inside the scope just fine. However, if we
|
||||
// try to run our 'sketchy' function outside of the scope, the function we created will have
|
||||
|
||||
@@ -28,9 +28,14 @@ fn main() -> Result<()> {
|
||||
let globals = lua.globals();
|
||||
|
||||
// Create Car struct from a Lua table
|
||||
let car: Car = lua.from_value(lua.load(r#"
|
||||
let car: Car = lua.from_value(
|
||||
lua.load(
|
||||
r#"
|
||||
{active = true, model = "Volkswagen Golf", transmission = "Automatic", engine = {v = 1499, kw = 90}}
|
||||
"#).eval()?)?;
|
||||
"#,
|
||||
)
|
||||
.eval()?,
|
||||
)?;
|
||||
|
||||
// Set it as (serializable) userdata
|
||||
globals.set("null", lua.null())?;
|
||||
|
||||
@@ -7,7 +7,7 @@ struct Rectangle {
|
||||
}
|
||||
|
||||
impl UserData for Rectangle {
|
||||
fn add_fields<'lua, F: mlua::UserDataFields<'lua, Self>>(fields: &mut F) {
|
||||
fn add_fields<F: mlua::UserDataFields<Self>>(fields: &mut F) {
|
||||
fields.add_field_method_get("length", |_, this| Ok(this.length));
|
||||
fields.add_field_method_set("length", |_, this, val| {
|
||||
this.length = val;
|
||||
@@ -20,7 +20,7 @@ impl UserData for Rectangle {
|
||||
});
|
||||
}
|
||||
|
||||
fn add_methods<'lua, M: mlua::UserDataMethods<'lua, Self>>(methods: &mut M) {
|
||||
fn add_methods<M: mlua::UserDataMethods<Self>>(methods: &mut M) {
|
||||
methods.add_method("area", |_, this, ()| Ok(this.length * this.width));
|
||||
methods.add_method("diagonal", |_, this, ()| {
|
||||
Ok((this.length.pow(2) as f64 + this.width.pow(2) as f64).sqrt())
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "mlua-sys"
|
||||
version = "0.6.2"
|
||||
version = "0.6.3"
|
||||
authors = ["Aleksandr Orlenko <zxteam@pm.me>"]
|
||||
rust-version = "1.71"
|
||||
edition = "2021"
|
||||
|
||||
+1
-4
@@ -54,10 +54,7 @@ pub const LUA_TRACEBACK_STACK: c_int = 11;
|
||||
target_arch = "sparc",
|
||||
target_arch = "wasm32",
|
||||
target_arch = "hexagon",
|
||||
all(
|
||||
target_arch = "riscv32",
|
||||
not(any(target_os = "espidf", target_os = "zkvm"))
|
||||
),
|
||||
all(target_arch = "riscv32", not(any(target_os = "espidf", target_os = "zkvm"))),
|
||||
all(target_arch = "xtensa", not(target_os = "espidf")),
|
||||
))]
|
||||
#[doc(hidden)]
|
||||
|
||||
@@ -2,9 +2,8 @@
|
||||
//!
|
||||
//! Based on github.com/keplerproject/lua-compat-5.3
|
||||
|
||||
use std::mem;
|
||||
use std::os::raw::{c_char, c_int, c_void};
|
||||
use std::ptr;
|
||||
use std::{mem, ptr};
|
||||
|
||||
use super::lauxlib::*;
|
||||
use super::lua::*;
|
||||
@@ -328,12 +327,7 @@ pub unsafe fn lua_setuservalue(L: *mut lua_State, idx: c_int) {
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn lua_dump(
|
||||
L: *mut lua_State,
|
||||
writer: lua_Writer,
|
||||
data: *mut c_void,
|
||||
_strip: c_int,
|
||||
) -> c_int {
|
||||
pub unsafe fn lua_dump(L: *mut lua_State, writer: lua_Writer, data: *mut c_void, _strip: c_int) -> c_int {
|
||||
lua_dump_(L, writer, data)
|
||||
}
|
||||
|
||||
@@ -365,12 +359,7 @@ pub unsafe fn lua_pushglobaltable(L: *mut lua_State) {
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn lua_resume(
|
||||
L: *mut lua_State,
|
||||
_from: *mut lua_State,
|
||||
narg: c_int,
|
||||
nres: *mut c_int,
|
||||
) -> c_int {
|
||||
pub unsafe fn lua_resume(L: *mut lua_State, _from: *mut lua_State, narg: c_int, nres: *mut c_int) -> c_int {
|
||||
let ret = lua_resume_(L, narg);
|
||||
if (ret == LUA_OK || ret == LUA_YIELD) && !(nres.is_null()) {
|
||||
*nres = lua_gettop(L);
|
||||
@@ -414,6 +403,25 @@ pub unsafe fn luaL_newmetatable(L: *mut lua_State, tname: *const c_char) -> c_in
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe fn luaL_loadbufferenv(
|
||||
L: *mut lua_State,
|
||||
data: *const c_char,
|
||||
size: usize,
|
||||
name: *const c_char,
|
||||
mode: *const c_char,
|
||||
mut env: c_int,
|
||||
) -> c_int {
|
||||
if env != 0 {
|
||||
env = lua_absindex(L, env);
|
||||
}
|
||||
let status = luaL_loadbufferx(L, data, size, name, mode);
|
||||
if status == LUA_OK && env != 0 {
|
||||
lua_pushvalue(L, env);
|
||||
lua_setfenv(L, -2);
|
||||
}
|
||||
status
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn luaL_loadbufferx(
|
||||
L: *mut lua_State,
|
||||
@@ -446,12 +454,7 @@ pub unsafe fn luaL_len(L: *mut lua_State, idx: c_int) -> lua_Integer {
|
||||
res
|
||||
}
|
||||
|
||||
pub unsafe fn luaL_traceback(
|
||||
L: *mut lua_State,
|
||||
L1: *mut lua_State,
|
||||
msg: *const c_char,
|
||||
mut level: c_int,
|
||||
) {
|
||||
pub unsafe fn luaL_traceback(L: *mut lua_State, L1: *mut lua_State, msg: *const c_char, mut level: c_int) {
|
||||
let mut ar: lua_Debug = mem::zeroed();
|
||||
let top = lua_gettop(L);
|
||||
let numlevels = compat53_countlevels(L1);
|
||||
@@ -543,12 +546,7 @@ pub unsafe fn luaL_getsubtable(L: *mut lua_State, idx: c_int, fname: *const c_ch
|
||||
0
|
||||
}
|
||||
|
||||
pub unsafe fn luaL_requiref(
|
||||
L: *mut lua_State,
|
||||
modname: *const c_char,
|
||||
openf: lua_CFunction,
|
||||
glb: c_int,
|
||||
) {
|
||||
pub unsafe fn luaL_requiref(L: *mut lua_State, modname: *const c_char, openf: lua_CFunction, glb: c_int) {
|
||||
luaL_checkstack(L, 3, cstr!("not enough stack slots available"));
|
||||
luaL_getsubtable(L, LUA_REGISTRYINDEX, cstr!("_LOADED"));
|
||||
if lua_getfield(L, -1, modname) == LUA_TNIL {
|
||||
|
||||
@@ -63,12 +63,7 @@ extern "C-unwind" {
|
||||
pub fn luaL_unref(L: *mut lua_State, t: c_int, r#ref: c_int);
|
||||
|
||||
pub fn luaL_loadfile(L: *mut lua_State, filename: *const c_char) -> c_int;
|
||||
pub fn luaL_loadbuffer(
|
||||
L: *mut lua_State,
|
||||
buff: *const c_char,
|
||||
sz: usize,
|
||||
name: *const c_char,
|
||||
) -> c_int;
|
||||
pub fn luaL_loadbuffer(L: *mut lua_State, buff: *const c_char, sz: usize, name: *const c_char) -> c_int;
|
||||
pub fn luaL_loadstring(L: *mut lua_State, s: *const c_char) -> c_int;
|
||||
|
||||
pub fn luaL_newstate() -> *mut lua_State;
|
||||
|
||||
@@ -379,12 +379,7 @@ extern "C-unwind" {
|
||||
pub fn lua_getupvalue(L: *mut lua_State, funcindex: c_int, n: c_int) -> *const c_char;
|
||||
pub fn lua_setupvalue(L: *mut lua_State, funcindex: c_int, n: c_int) -> *const c_char;
|
||||
|
||||
pub fn lua_sethook(
|
||||
L: *mut lua_State,
|
||||
func: Option<lua_Hook>,
|
||||
mask: c_int,
|
||||
count: c_int,
|
||||
) -> c_int;
|
||||
pub fn lua_sethook(L: *mut lua_State, func: Option<lua_Hook>, mask: c_int, count: c_int) -> c_int;
|
||||
pub fn lua_gethook(L: *mut lua_State) -> Option<lua_Hook>;
|
||||
pub fn lua_gethookmask(L: *mut lua_State) -> c_int;
|
||||
pub fn lua_gethookcount(L: *mut lua_State) -> c_int;
|
||||
|
||||
@@ -157,22 +157,12 @@ pub unsafe fn lua_rawseti(L: *mut lua_State, idx: c_int, n: lua_Integer) {
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn lua_dump(
|
||||
L: *mut lua_State,
|
||||
writer: lua_Writer,
|
||||
data: *mut c_void,
|
||||
_strip: c_int,
|
||||
) -> c_int {
|
||||
pub unsafe fn lua_dump(L: *mut lua_State, writer: lua_Writer, data: *mut c_void, _strip: c_int) -> c_int {
|
||||
lua_dump_(L, writer, data)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn lua_resume(
|
||||
L: *mut lua_State,
|
||||
from: *mut lua_State,
|
||||
narg: c_int,
|
||||
nres: *mut c_int,
|
||||
) -> c_int {
|
||||
pub unsafe fn lua_resume(L: *mut lua_State, from: *mut lua_State, narg: c_int, nres: *mut c_int) -> c_int {
|
||||
let ret = lua_resume_(L, from, narg);
|
||||
if (ret == LUA_OK || ret == LUA_YIELD) && !(nres.is_null()) {
|
||||
*nres = lua_gettop(L);
|
||||
@@ -240,12 +230,7 @@ pub unsafe fn luaL_tolstring(L: *mut lua_State, mut idx: c_int, len: *mut usize)
|
||||
lua_tolstring(L, -1, len)
|
||||
}
|
||||
|
||||
pub unsafe fn luaL_requiref(
|
||||
L: *mut lua_State,
|
||||
modname: *const c_char,
|
||||
openf: lua_CFunction,
|
||||
glb: c_int,
|
||||
) {
|
||||
pub unsafe fn luaL_requiref(L: *mut lua_State, modname: *const c_char, openf: lua_CFunction, glb: c_int) {
|
||||
luaL_checkstack(L, 3, cstr!("not enough stack slots available"));
|
||||
luaL_getsubtable(L, LUA_REGISTRYINDEX, cstr!("_LOADED"));
|
||||
if lua_getfield(L, -1, modname) == LUA_TNIL {
|
||||
@@ -262,3 +247,22 @@ pub unsafe fn luaL_requiref(
|
||||
}
|
||||
lua_replace(L, -2);
|
||||
}
|
||||
|
||||
pub unsafe fn luaL_loadbufferenv(
|
||||
L: *mut lua_State,
|
||||
data: *const c_char,
|
||||
size: usize,
|
||||
name: *const c_char,
|
||||
mode: *const c_char,
|
||||
mut env: c_int,
|
||||
) -> c_int {
|
||||
if env != 0 {
|
||||
env = lua_absindex(L, env);
|
||||
}
|
||||
let status = luaL_loadbufferx(L, data, size, name, mode);
|
||||
if status == LUA_OK && env != 0 {
|
||||
lua_pushvalue(L, env);
|
||||
lua_setupvalue(L, -2, 1);
|
||||
}
|
||||
status
|
||||
}
|
||||
|
||||
@@ -25,12 +25,8 @@ extern "C-unwind" {
|
||||
pub fn luaL_tolstring_(L: *mut lua_State, idx: c_int, len: *mut usize) -> *const c_char;
|
||||
pub fn luaL_argerror(L: *mut lua_State, arg: c_int, extramsg: *const c_char) -> c_int;
|
||||
pub fn luaL_checklstring(L: *mut lua_State, arg: c_int, l: *mut usize) -> *const c_char;
|
||||
pub fn luaL_optlstring(
|
||||
L: *mut lua_State,
|
||||
arg: c_int,
|
||||
def: *const c_char,
|
||||
l: *mut usize,
|
||||
) -> *const c_char;
|
||||
pub fn luaL_optlstring(L: *mut lua_State, arg: c_int, def: *const c_char, l: *mut usize)
|
||||
-> *const c_char;
|
||||
pub fn luaL_checknumber(L: *mut lua_State, arg: c_int) -> lua_Number;
|
||||
pub fn luaL_optnumber(L: *mut lua_State, arg: c_int, def: lua_Number) -> lua_Number;
|
||||
pub fn luaL_checkinteger(L: *mut lua_State, arg: c_int) -> lua_Integer;
|
||||
@@ -71,8 +67,7 @@ extern "C-unwind" {
|
||||
pub fn luaL_ref(L: *mut lua_State, t: c_int) -> c_int;
|
||||
pub fn luaL_unref(L: *mut lua_State, t: c_int, r#ref: c_int);
|
||||
|
||||
pub fn luaL_loadfilex(L: *mut lua_State, filename: *const c_char, mode: *const c_char)
|
||||
-> c_int;
|
||||
pub fn luaL_loadfilex(L: *mut lua_State, filename: *const c_char, mode: *const c_char) -> c_int;
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
@@ -109,12 +104,7 @@ extern "C-unwind" {
|
||||
pub fn luaL_traceback(L: *mut lua_State, L1: *mut lua_State, msg: *const c_char, level: c_int);
|
||||
|
||||
#[link_name = "luaL_requiref"]
|
||||
pub fn luaL_requiref_(
|
||||
L: *mut lua_State,
|
||||
modname: *const c_char,
|
||||
openf: lua_CFunction,
|
||||
glb: c_int,
|
||||
);
|
||||
pub fn luaL_requiref_(L: *mut lua_State, modname: *const c_char, openf: lua_CFunction, glb: c_int);
|
||||
}
|
||||
|
||||
//
|
||||
@@ -173,12 +163,7 @@ pub unsafe fn luaL_getmetatable(L: *mut lua_State, n: *const c_char) {
|
||||
// luaL_opt would be implemented here but it is undocumented, so it's omitted
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn luaL_loadbuffer(
|
||||
L: *mut lua_State,
|
||||
s: *const c_char,
|
||||
sz: usize,
|
||||
n: *const c_char,
|
||||
) -> c_int {
|
||||
pub unsafe fn luaL_loadbuffer(L: *mut lua_State, s: *const c_char, sz: usize, n: *const c_char) -> c_int {
|
||||
luaL_loadbufferx(L, s, sz, n, ptr::null())
|
||||
}
|
||||
|
||||
|
||||
@@ -222,13 +222,7 @@ extern "C-unwind" {
|
||||
//
|
||||
// 'load' and 'call' functions (load and run Lua code)
|
||||
//
|
||||
pub fn lua_callk(
|
||||
L: *mut lua_State,
|
||||
nargs: c_int,
|
||||
nresults: c_int,
|
||||
ctx: c_int,
|
||||
k: Option<lua_CFunction>,
|
||||
);
|
||||
pub fn lua_callk(L: *mut lua_State, nargs: c_int, nresults: c_int, ctx: c_int, k: Option<lua_CFunction>);
|
||||
pub fn lua_pcallk(
|
||||
L: *mut lua_State,
|
||||
nargs: c_int,
|
||||
@@ -266,12 +260,7 @@ extern "C-unwind" {
|
||||
//
|
||||
// Coroutine functions
|
||||
//
|
||||
pub fn lua_yieldk(
|
||||
L: *mut lua_State,
|
||||
nresults: c_int,
|
||||
ctx: c_int,
|
||||
k: Option<lua_CFunction>,
|
||||
) -> c_int;
|
||||
pub fn lua_yieldk(L: *mut lua_State, nresults: c_int, ctx: c_int, k: Option<lua_CFunction>) -> c_int;
|
||||
#[link_name = "lua_resume"]
|
||||
pub fn lua_resume_(L: *mut lua_State, from: *mut lua_State, narg: c_int) -> c_int;
|
||||
pub fn lua_status(L: *mut lua_State) -> c_int;
|
||||
@@ -471,12 +460,7 @@ extern "C-unwind" {
|
||||
pub fn lua_upvalueid(L: *mut lua_State, fidx: c_int, n: c_int) -> *mut c_void;
|
||||
pub fn lua_upvaluejoin(L: *mut lua_State, fidx1: c_int, n1: c_int, fidx2: c_int, n2: c_int);
|
||||
|
||||
pub fn lua_sethook(
|
||||
L: *mut lua_State,
|
||||
func: Option<lua_Hook>,
|
||||
mask: c_int,
|
||||
count: c_int,
|
||||
) -> c_int;
|
||||
pub fn lua_sethook(L: *mut lua_State, func: Option<lua_Hook>, mask: c_int, count: c_int) -> c_int;
|
||||
pub fn lua_gethook(L: *mut lua_State) -> Option<lua_Hook>;
|
||||
pub fn lua_gethookmask(L: *mut lua_State) -> c_int;
|
||||
pub fn lua_gethookcount(L: *mut lua_State) -> c_int;
|
||||
|
||||
@@ -1,19 +1,34 @@
|
||||
//! MLua compatibility layer for Lua 5.3
|
||||
|
||||
use std::os::raw::c_int;
|
||||
use std::os::raw::{c_char, c_int};
|
||||
|
||||
use super::lauxlib::*;
|
||||
use super::lua::*;
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn lua_resume(
|
||||
L: *mut lua_State,
|
||||
from: *mut lua_State,
|
||||
narg: c_int,
|
||||
nres: *mut c_int,
|
||||
) -> c_int {
|
||||
pub unsafe fn lua_resume(L: *mut lua_State, from: *mut lua_State, narg: c_int, nres: *mut c_int) -> c_int {
|
||||
let ret = lua_resume_(L, from, narg);
|
||||
if (ret == LUA_OK || ret == LUA_YIELD) && !(nres.is_null()) {
|
||||
*nres = lua_gettop(L);
|
||||
}
|
||||
ret
|
||||
}
|
||||
|
||||
pub unsafe fn luaL_loadbufferenv(
|
||||
L: *mut lua_State,
|
||||
data: *const c_char,
|
||||
size: usize,
|
||||
name: *const c_char,
|
||||
mode: *const c_char,
|
||||
mut env: c_int,
|
||||
) -> c_int {
|
||||
if env != 0 {
|
||||
env = lua_absindex(L, env);
|
||||
}
|
||||
let status = luaL_loadbufferx(L, data, size, name, mode);
|
||||
if status == LUA_OK && env != 0 {
|
||||
lua_pushvalue(L, env);
|
||||
lua_setupvalue(L, -2, 1);
|
||||
}
|
||||
status
|
||||
}
|
||||
|
||||
@@ -30,12 +30,8 @@ extern "C-unwind" {
|
||||
pub fn luaL_tolstring_(L: *mut lua_State, idx: c_int, len: *mut usize) -> *const c_char;
|
||||
pub fn luaL_argerror(L: *mut lua_State, arg: c_int, extramsg: *const c_char) -> c_int;
|
||||
pub fn luaL_checklstring(L: *mut lua_State, arg: c_int, l: *mut usize) -> *const c_char;
|
||||
pub fn luaL_optlstring(
|
||||
L: *mut lua_State,
|
||||
arg: c_int,
|
||||
def: *const c_char,
|
||||
l: *mut usize,
|
||||
) -> *const c_char;
|
||||
pub fn luaL_optlstring(L: *mut lua_State, arg: c_int, def: *const c_char, l: *mut usize)
|
||||
-> *const c_char;
|
||||
pub fn luaL_checknumber(L: *mut lua_State, arg: c_int) -> lua_Number;
|
||||
pub fn luaL_optnumber(L: *mut lua_State, arg: c_int, def: lua_Number) -> lua_Number;
|
||||
pub fn luaL_checkinteger(L: *mut lua_State, arg: c_int) -> lua_Integer;
|
||||
@@ -73,8 +69,7 @@ extern "C-unwind" {
|
||||
pub fn luaL_ref(L: *mut lua_State, t: c_int) -> c_int;
|
||||
pub fn luaL_unref(L: *mut lua_State, t: c_int, r#ref: c_int);
|
||||
|
||||
pub fn luaL_loadfilex(L: *mut lua_State, filename: *const c_char, mode: *const c_char)
|
||||
-> c_int;
|
||||
pub fn luaL_loadfilex(L: *mut lua_State, filename: *const c_char, mode: *const c_char) -> c_int;
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
@@ -110,12 +105,7 @@ extern "C-unwind" {
|
||||
|
||||
pub fn luaL_traceback(L: *mut lua_State, L1: *mut lua_State, msg: *const c_char, level: c_int);
|
||||
|
||||
pub fn luaL_requiref(
|
||||
L: *mut lua_State,
|
||||
modname: *const c_char,
|
||||
openf: lua_CFunction,
|
||||
glb: c_int,
|
||||
);
|
||||
pub fn luaL_requiref(L: *mut lua_State, modname: *const c_char, openf: lua_CFunction, glb: c_int);
|
||||
}
|
||||
|
||||
//
|
||||
@@ -179,12 +169,7 @@ pub unsafe fn luaL_tolstring(L: *mut lua_State, idx: c_int, len: *mut usize) ->
|
||||
// luaL_opt would be implemented here but it is undocumented, so it's omitted
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn luaL_loadbuffer(
|
||||
L: *mut lua_State,
|
||||
s: *const c_char,
|
||||
sz: usize,
|
||||
n: *const c_char,
|
||||
) -> c_int {
|
||||
pub unsafe fn luaL_loadbuffer(L: *mut lua_State, s: *const c_char, sz: usize, n: *const c_char) -> c_int {
|
||||
luaL_loadbufferx(L, s, sz, n, ptr::null())
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
//! Contains definitions from `lua.h`.
|
||||
|
||||
use std::marker::{PhantomData, PhantomPinned};
|
||||
use std::mem;
|
||||
use std::os::raw::{c_char, c_double, c_int, c_uchar, c_void};
|
||||
use std::ptr;
|
||||
use std::{mem, ptr};
|
||||
|
||||
// Mark for precompiled code (`<esc>Lua`)
|
||||
pub const LUA_SIGNATURE: &[u8] = b"\x1bLua";
|
||||
@@ -251,12 +250,7 @@ extern "C-unwind" {
|
||||
mode: *const c_char,
|
||||
) -> c_int;
|
||||
|
||||
pub fn lua_dump(
|
||||
L: *mut lua_State,
|
||||
writer: lua_Writer,
|
||||
data: *mut c_void,
|
||||
strip: c_int,
|
||||
) -> c_int;
|
||||
pub fn lua_dump(L: *mut lua_State, writer: lua_Writer, data: *mut c_void, strip: c_int) -> c_int;
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
|
||||
@@ -29,12 +29,8 @@ extern "C-unwind" {
|
||||
pub fn luaL_tolstring(L: *mut lua_State, idx: c_int, len: *mut usize) -> *const c_char;
|
||||
pub fn luaL_argerror(L: *mut lua_State, arg: c_int, extramsg: *const c_char) -> c_int;
|
||||
pub fn luaL_checklstring(L: *mut lua_State, arg: c_int, l: *mut usize) -> *const c_char;
|
||||
pub fn luaL_optlstring(
|
||||
L: *mut lua_State,
|
||||
arg: c_int,
|
||||
def: *const c_char,
|
||||
l: *mut usize,
|
||||
) -> *const c_char;
|
||||
pub fn luaL_optlstring(L: *mut lua_State, arg: c_int, def: *const c_char, l: *mut usize)
|
||||
-> *const c_char;
|
||||
pub fn luaL_checknumber(L: *mut lua_State, arg: c_int) -> lua_Number;
|
||||
pub fn luaL_optnumber(L: *mut lua_State, arg: c_int, def: lua_Number) -> lua_Number;
|
||||
pub fn luaL_checkinteger(L: *mut lua_State, arg: c_int) -> lua_Integer;
|
||||
@@ -72,8 +68,7 @@ extern "C-unwind" {
|
||||
pub fn luaL_ref(L: *mut lua_State, t: c_int) -> c_int;
|
||||
pub fn luaL_unref(L: *mut lua_State, t: c_int, r#ref: c_int);
|
||||
|
||||
pub fn luaL_loadfilex(L: *mut lua_State, filename: *const c_char, mode: *const c_char)
|
||||
-> c_int;
|
||||
pub fn luaL_loadfilex(L: *mut lua_State, filename: *const c_char, mode: *const c_char) -> c_int;
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
@@ -111,12 +106,7 @@ extern "C-unwind" {
|
||||
|
||||
pub fn luaL_traceback(L: *mut lua_State, L1: *mut lua_State, msg: *const c_char, level: c_int);
|
||||
|
||||
pub fn luaL_requiref(
|
||||
L: *mut lua_State,
|
||||
modname: *const c_char,
|
||||
openf: lua_CFunction,
|
||||
glb: c_int,
|
||||
);
|
||||
pub fn luaL_requiref(L: *mut lua_State, modname: *const c_char, openf: lua_CFunction, glb: c_int);
|
||||
}
|
||||
|
||||
//
|
||||
@@ -175,15 +165,29 @@ pub unsafe fn luaL_getmetatable(L: *mut lua_State, n: *const c_char) {
|
||||
// luaL_opt would be implemented here but it is undocumented, so it's omitted
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn luaL_loadbuffer(
|
||||
L: *mut lua_State,
|
||||
s: *const c_char,
|
||||
sz: usize,
|
||||
n: *const c_char,
|
||||
) -> c_int {
|
||||
pub unsafe fn luaL_loadbuffer(L: *mut lua_State, s: *const c_char, sz: usize, n: *const c_char) -> c_int {
|
||||
luaL_loadbufferx(L, s, sz, n, ptr::null())
|
||||
}
|
||||
|
||||
pub unsafe fn luaL_loadbufferenv(
|
||||
L: *mut lua_State,
|
||||
data: *const c_char,
|
||||
size: usize,
|
||||
name: *const c_char,
|
||||
mode: *const c_char,
|
||||
mut env: c_int,
|
||||
) -> c_int {
|
||||
if env != 0 {
|
||||
env = lua::lua_absindex(L, env);
|
||||
}
|
||||
let status = luaL_loadbufferx(L, data, size, name, mode);
|
||||
if status == lua::LUA_OK && env != 0 {
|
||||
lua::lua_pushvalue(L, env);
|
||||
lua::lua_setupvalue(L, -2, 1);
|
||||
}
|
||||
status
|
||||
}
|
||||
|
||||
//
|
||||
// TODO: Generic Buffer Manipulation
|
||||
//
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
//! Contains definitions from `lua.h`.
|
||||
|
||||
use std::marker::{PhantomData, PhantomPinned};
|
||||
use std::mem;
|
||||
use std::os::raw::{c_char, c_double, c_int, c_uchar, c_ushort, c_void};
|
||||
use std::ptr;
|
||||
use std::{mem, ptr};
|
||||
|
||||
// Mark for precompiled code (`<esc>Lua`)
|
||||
pub const LUA_SIGNATURE: &[u8] = b"\x1bLua";
|
||||
@@ -101,8 +100,7 @@ pub type lua_Alloc =
|
||||
unsafe extern "C-unwind" fn(ud: *mut c_void, ptr: *mut c_void, osize: usize, nsize: usize) -> *mut c_void;
|
||||
|
||||
/// Type for warning functions
|
||||
pub type lua_WarnFunction =
|
||||
unsafe extern "C-unwind" fn(ud: *mut c_void, msg: *const c_char, tocont: c_int);
|
||||
pub type lua_WarnFunction = unsafe extern "C-unwind" fn(ud: *mut c_void, msg: *const c_char, tocont: c_int);
|
||||
|
||||
#[cfg_attr(all(windows, raw_dylib), link(name = "lua54", kind = "raw-dylib"))]
|
||||
extern "C-unwind" {
|
||||
@@ -266,12 +264,7 @@ extern "C-unwind" {
|
||||
mode: *const c_char,
|
||||
) -> c_int;
|
||||
|
||||
pub fn lua_dump(
|
||||
L: *mut lua_State,
|
||||
writer: lua_Writer,
|
||||
data: *mut c_void,
|
||||
strip: c_int,
|
||||
) -> c_int;
|
||||
pub fn lua_dump(L: *mut lua_State, writer: lua_Writer, data: *mut c_void, strip: c_int) -> c_int;
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
@@ -295,12 +288,7 @@ extern "C-unwind" {
|
||||
ctx: lua_KContext,
|
||||
k: Option<lua_KFunction>,
|
||||
) -> c_int;
|
||||
pub fn lua_resume(
|
||||
L: *mut lua_State,
|
||||
from: *mut lua_State,
|
||||
narg: c_int,
|
||||
nres: *mut c_int,
|
||||
) -> c_int;
|
||||
pub fn lua_resume(L: *mut lua_State, from: *mut lua_State, narg: c_int, nres: *mut c_int) -> c_int;
|
||||
pub fn lua_status(L: *mut lua_State) -> c_int;
|
||||
pub fn lua_isyieldable(L: *mut lua_State) -> c_int;
|
||||
}
|
||||
|
||||
+23
-39
@@ -3,9 +3,8 @@
|
||||
//! Based on github.com/keplerproject/lua-compat-5.3
|
||||
|
||||
use std::ffi::CStr;
|
||||
use std::mem;
|
||||
use std::os::raw::{c_char, c_int, c_void};
|
||||
use std::ptr;
|
||||
use std::{mem, ptr};
|
||||
|
||||
use super::lauxlib::*;
|
||||
use super::lua::*;
|
||||
@@ -53,11 +52,7 @@ unsafe fn compat53_findfield(L: *mut lua_State, objidx: c_int, level: c_int) ->
|
||||
0 // not found
|
||||
}
|
||||
|
||||
unsafe fn compat53_pushglobalfuncname(
|
||||
L: *mut lua_State,
|
||||
level: c_int,
|
||||
ar: *mut lua_Debug,
|
||||
) -> c_int {
|
||||
unsafe fn compat53_pushglobalfuncname(L: *mut lua_State, level: c_int, ar: *mut lua_Debug) -> c_int {
|
||||
let top = lua_gettop(L);
|
||||
// push function
|
||||
lua_getinfo(L, level, cstr!("f"), ar);
|
||||
@@ -281,12 +276,7 @@ pub unsafe fn lua_pushglobaltable(L: *mut lua_State) {
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn lua_resume(
|
||||
L: *mut lua_State,
|
||||
from: *mut lua_State,
|
||||
narg: c_int,
|
||||
nres: *mut c_int,
|
||||
) -> c_int {
|
||||
pub unsafe fn lua_resume(L: *mut lua_State, from: *mut lua_State, narg: c_int, nres: *mut c_int) -> c_int {
|
||||
let ret = lua_resume_(L, from, narg);
|
||||
if (ret == LUA_OK || ret == LUA_YIELD) && !(nres.is_null()) {
|
||||
*nres = lua_gettop(L);
|
||||
@@ -330,12 +320,13 @@ pub unsafe fn luaL_newmetatable(L: *mut lua_State, tname: *const c_char) -> c_in
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe fn luaL_loadbufferx(
|
||||
pub unsafe fn luaL_loadbufferenv(
|
||||
L: *mut lua_State,
|
||||
data: *const c_char,
|
||||
mut size: usize,
|
||||
name: *const c_char,
|
||||
mode: *const c_char,
|
||||
env: c_int,
|
||||
) -> c_int {
|
||||
extern "C" {
|
||||
fn free(p: *mut c_void);
|
||||
@@ -345,35 +336,38 @@ pub unsafe fn luaL_loadbufferx(
|
||||
if !mode.is_null() {
|
||||
let modeb = CStr::from_ptr(mode).to_bytes();
|
||||
if !chunk_is_text && !modeb.contains(&b'b') {
|
||||
lua_pushfstring(
|
||||
L,
|
||||
cstr!("attempt to load a binary chunk (mode is '%s')"),
|
||||
mode,
|
||||
);
|
||||
lua_pushfstring(L, cstr!("attempt to load a binary chunk (mode is '%s')"), mode);
|
||||
return LUA_ERRSYNTAX;
|
||||
} else if chunk_is_text && !modeb.contains(&b't') {
|
||||
lua_pushfstring(
|
||||
L,
|
||||
cstr!("attempt to load a text chunk (mode is '%s')"),
|
||||
mode,
|
||||
);
|
||||
lua_pushfstring(L, cstr!("attempt to load a text chunk (mode is '%s')"), mode);
|
||||
return LUA_ERRSYNTAX;
|
||||
}
|
||||
}
|
||||
|
||||
if chunk_is_text {
|
||||
let data = luau_compile_(data, size, ptr::null_mut(), &mut size);
|
||||
let ok = luau_load(L, name, data, size, 0) == 0;
|
||||
let ok = luau_load(L, name, data, size, env) == 0;
|
||||
free(data as *mut c_void);
|
||||
if !ok {
|
||||
return LUA_ERRSYNTAX;
|
||||
}
|
||||
} else if luau_load(L, name, data, size, 0) != 0 {
|
||||
} else if luau_load(L, name, data, size, env) != 0 {
|
||||
return LUA_ERRSYNTAX;
|
||||
}
|
||||
LUA_OK
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn luaL_loadbufferx(
|
||||
L: *mut lua_State,
|
||||
data: *const c_char,
|
||||
size: usize,
|
||||
name: *const c_char,
|
||||
mode: *const c_char,
|
||||
) -> c_int {
|
||||
luaL_loadbufferenv(L, data, size, name, mode, 0)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn luaL_loadbuffer(
|
||||
L: *mut lua_State,
|
||||
@@ -381,7 +375,7 @@ pub unsafe fn luaL_loadbuffer(
|
||||
size: usize,
|
||||
name: *const c_char,
|
||||
) -> c_int {
|
||||
luaL_loadbufferx(L, data, size, name, ptr::null())
|
||||
luaL_loadbufferenv(L, data, size, name, ptr::null(), 0)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
@@ -397,12 +391,7 @@ pub unsafe fn luaL_len(L: *mut lua_State, idx: c_int) -> lua_Integer {
|
||||
res
|
||||
}
|
||||
|
||||
pub unsafe fn luaL_traceback(
|
||||
L: *mut lua_State,
|
||||
L1: *mut lua_State,
|
||||
msg: *const c_char,
|
||||
mut level: c_int,
|
||||
) {
|
||||
pub unsafe fn luaL_traceback(L: *mut lua_State, L1: *mut lua_State, msg: *const c_char, mut level: c_int) {
|
||||
let mut ar: lua_Debug = mem::zeroed();
|
||||
let top = lua_gettop(L);
|
||||
let numlevels = lua_stackdepth(L);
|
||||
@@ -494,12 +483,7 @@ pub unsafe fn luaL_getsubtable(L: *mut lua_State, idx: c_int, fname: *const c_ch
|
||||
0
|
||||
}
|
||||
|
||||
pub unsafe fn luaL_requiref(
|
||||
L: *mut lua_State,
|
||||
modname: *const c_char,
|
||||
openf: lua_CFunction,
|
||||
glb: c_int,
|
||||
) {
|
||||
pub unsafe fn luaL_requiref(L: *mut lua_State, modname: *const c_char, openf: lua_CFunction, glb: c_int) {
|
||||
luaL_checkstack(L, 3, cstr!("not enough stack slots available"));
|
||||
luaL_getsubtable(L, LUA_REGISTRYINDEX, cstr!("_LOADED"));
|
||||
if lua_getfield(L, -1, modname) == LUA_TNIL {
|
||||
|
||||
@@ -3,9 +3,7 @@
|
||||
use std::os::raw::{c_char, c_float, c_int, c_void};
|
||||
use std::ptr;
|
||||
|
||||
use super::lua::{
|
||||
self, lua_CFunction, lua_Integer, lua_Number, lua_State, lua_Unsigned, LUA_REGISTRYINDEX,
|
||||
};
|
||||
use super::lua::{self, lua_CFunction, lua_Integer, lua_Number, lua_State, lua_Unsigned, LUA_REGISTRYINDEX};
|
||||
|
||||
#[repr(C)]
|
||||
pub struct luaL_Reg {
|
||||
|
||||
@@ -84,12 +84,8 @@ pub type lua_Udestructor = unsafe extern "C-unwind" fn(*mut c_void);
|
||||
pub type lua_Destructor = unsafe extern "C-unwind" fn(L: *mut lua_State, *mut c_void);
|
||||
|
||||
/// Type for memory-allocation functions.
|
||||
pub type lua_Alloc = unsafe extern "C-unwind" fn(
|
||||
ud: *mut c_void,
|
||||
ptr: *mut c_void,
|
||||
osize: usize,
|
||||
nsize: usize,
|
||||
) -> *mut c_void;
|
||||
pub type lua_Alloc =
|
||||
unsafe extern "C-unwind" fn(ud: *mut c_void, ptr: *mut c_void, osize: usize, nsize: usize) -> *mut c_void;
|
||||
|
||||
/// Returns Luau release version (eg. `0.xxx`).
|
||||
pub const fn luau_version() -> Option<&'static str> {
|
||||
@@ -426,12 +422,7 @@ pub unsafe fn lua_pushcclosure(L: *mut lua_State, f: lua_CFunction, nup: c_int)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn lua_pushcclosured(
|
||||
L: *mut lua_State,
|
||||
f: lua_CFunction,
|
||||
debugname: *const c_char,
|
||||
nup: c_int,
|
||||
) {
|
||||
pub unsafe fn lua_pushcclosured(L: *mut lua_State, f: lua_CFunction, debugname: *const c_char, nup: c_int) {
|
||||
lua_pushcclosurek(L, f, debugname, nup, None)
|
||||
}
|
||||
|
||||
@@ -476,12 +467,7 @@ pub type lua_Coverage = unsafe extern "C-unwind" fn(
|
||||
|
||||
extern "C-unwind" {
|
||||
pub fn lua_stackdepth(L: *mut lua_State) -> c_int;
|
||||
pub fn lua_getinfo(
|
||||
L: *mut lua_State,
|
||||
level: c_int,
|
||||
what: *const c_char,
|
||||
ar: *mut lua_Debug,
|
||||
) -> c_int;
|
||||
pub fn lua_getinfo(L: *mut lua_State, level: c_int, what: *const c_char, ar: *mut lua_Debug) -> c_int;
|
||||
pub fn lua_getargument(L: *mut lua_State, level: c_int, n: c_int) -> c_int;
|
||||
pub fn lua_getlocal(L: *mut lua_State, level: c_int, n: c_int) -> *const c_char;
|
||||
pub fn lua_setlocal(L: *mut lua_State, level: c_int, n: c_int) -> *const c_char;
|
||||
@@ -489,19 +475,9 @@ extern "C-unwind" {
|
||||
pub fn lua_setupvalue(L: *mut lua_State, funcindex: c_int, n: c_int) -> *const c_char;
|
||||
|
||||
pub fn lua_singlestep(L: *mut lua_State, enabled: c_int);
|
||||
pub fn lua_breakpoint(
|
||||
L: *mut lua_State,
|
||||
funcindex: c_int,
|
||||
line: c_int,
|
||||
enabled: c_int,
|
||||
) -> c_int;
|
||||
pub fn lua_breakpoint(L: *mut lua_State, funcindex: c_int, line: c_int, enabled: c_int) -> c_int;
|
||||
|
||||
pub fn lua_getcoverage(
|
||||
L: *mut lua_State,
|
||||
funcindex: c_int,
|
||||
context: *mut c_void,
|
||||
callback: lua_Coverage,
|
||||
);
|
||||
pub fn lua_getcoverage(L: *mut lua_State, funcindex: c_int, context: *mut c_void, callback: lua_Coverage);
|
||||
|
||||
pub fn lua_debugtrace(L: *mut lua_State) -> *const c_char;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
#[allow(unused_macros)]
|
||||
macro_rules! cstr {
|
||||
($s:expr) => {
|
||||
concat!($s, "\0") as *const str as *const [::std::os::raw::c_char]
|
||||
as *const ::std::os::raw::c_char
|
||||
concat!($s, "\0") as *const str as *const [::std::os::raw::c_char] as *const ::std::os::raw::c_char
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "mlua_derive"
|
||||
version = "0.9.3"
|
||||
version = "0.10.0-beta.1"
|
||||
authors = ["Aleksandr Orlenko <zxteam@pm.me>"]
|
||||
edition = "2021"
|
||||
description = "Procedural macros for the mlua crate."
|
||||
@@ -19,6 +19,6 @@ quote = "1.0"
|
||||
proc-macro2 = { version = "1.0", features = ["span-locations"] }
|
||||
proc-macro-error = { version = "1.0", optional = true }
|
||||
syn = { version = "2.0", features = ["full"] }
|
||||
itertools = { version = "0.12", optional = true }
|
||||
itertools = { version = "0.13", optional = true }
|
||||
regex = { version = "1.4", optional = true }
|
||||
once_cell = { version = "1.0", optional = true }
|
||||
|
||||
@@ -3,9 +3,7 @@ use quote::quote;
|
||||
use syn::{parse_macro_input, DeriveInput};
|
||||
|
||||
pub fn from_lua(input: TokenStream) -> TokenStream {
|
||||
let DeriveInput {
|
||||
ident, generics, ..
|
||||
} = parse_macro_input!(input as DeriveInput);
|
||||
let DeriveInput { ident, generics, .. } = parse_macro_input!(input as DeriveInput);
|
||||
|
||||
let ident_str = ident.to_string();
|
||||
let (impl_generics, ty_generics, _) = generics.split_for_impl();
|
||||
@@ -15,9 +13,9 @@ pub fn from_lua(input: TokenStream) -> TokenStream {
|
||||
};
|
||||
|
||||
quote! {
|
||||
impl #impl_generics ::mlua::FromLua<'_> for #ident #ty_generics #where_clause {
|
||||
impl #impl_generics ::mlua::FromLua for #ident #ty_generics #where_clause {
|
||||
#[inline]
|
||||
fn from_lua(value: ::mlua::Value<'_>, _: &'_ ::mlua::Lua) -> ::mlua::Result<Self> {
|
||||
fn from_lua(value: ::mlua::Value, _: &::mlua::Lua) -> ::mlua::Result<Self> {
|
||||
match value {
|
||||
::mlua::Value::UserData(ud) => Ok(ud.borrow::<Self>()?.clone()),
|
||||
_ => Err(::mlua::Error::FromLuaConversionError {
|
||||
|
||||
+11
-13
@@ -64,9 +64,10 @@ pub fn lua_module(attr: TokenStream, item: TokenStream) -> TokenStream {
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C-unwind" fn #ext_entrypoint_name(state: *mut mlua::lua_State) -> ::std::os::raw::c_int {
|
||||
let lua = mlua::Lua::init_from_ptr(state);
|
||||
#skip_memory_check
|
||||
lua.entrypoint1(state, #func_name)
|
||||
mlua::Lua::entrypoint1(state, move |lua| {
|
||||
#skip_memory_check
|
||||
#func_name(lua)
|
||||
})
|
||||
}
|
||||
};
|
||||
|
||||
@@ -99,15 +100,14 @@ pub fn chunk(input: TokenStream) -> TokenStream {
|
||||
use ::std::borrow::Cow;
|
||||
use ::std::cell::Cell;
|
||||
use ::std::io::Result as IoResult;
|
||||
use ::std::marker::PhantomData;
|
||||
|
||||
struct InnerChunk<'lua, F: FnOnce(&'lua Lua) -> Result<Table<'lua>>>(Cell<Option<F>>, PhantomData<&'lua ()>);
|
||||
struct InnerChunk<F: FnOnce(&Lua) -> Result<Table>>(Cell<Option<F>>);
|
||||
|
||||
impl<'lua, F> AsChunk<'lua, 'static> for InnerChunk<'lua, F>
|
||||
impl<F> AsChunk<'static> for InnerChunk<F>
|
||||
where
|
||||
F: FnOnce(&'lua Lua) -> Result<Table<'lua>>,
|
||||
F: FnOnce(&Lua) -> Result<Table>,
|
||||
{
|
||||
fn environment(&self, lua: &'lua Lua) -> Result<Option<Table<'lua>>> {
|
||||
fn environment(&self, lua: &Lua) -> Result<Option<Table>> {
|
||||
if #caps_len > 0 {
|
||||
if let Some(make_env) = self.0.take() {
|
||||
return make_env(lua).map(Some);
|
||||
@@ -125,9 +125,7 @@ pub fn chunk(input: TokenStream) -> TokenStream {
|
||||
}
|
||||
}
|
||||
|
||||
fn annotate<'a, F: FnOnce(&'a Lua) -> Result<Table<'a>>>(f: F) -> F { f }
|
||||
|
||||
let make_env = annotate(move |lua: &Lua| -> Result<Table> {
|
||||
let make_env = move |lua: &Lua| -> Result<Table> {
|
||||
let globals = lua.globals();
|
||||
let env = lua.create_table()?;
|
||||
let meta = lua.create_table()?;
|
||||
@@ -139,9 +137,9 @@ pub fn chunk(input: TokenStream) -> TokenStream {
|
||||
|
||||
env.set_metatable(Some(meta));
|
||||
Ok(env)
|
||||
});
|
||||
};
|
||||
|
||||
InnerChunk(Cell::new(Some(make_env)), PhantomData)
|
||||
InnerChunk(Cell::new(Some(make_env)))
|
||||
}};
|
||||
|
||||
wrapped_code.into()
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
use std::{
|
||||
cmp::{Eq, PartialEq},
|
||||
fmt::{self, Display, Formatter},
|
||||
vec::IntoIter,
|
||||
};
|
||||
use std::cmp::{Eq, PartialEq};
|
||||
use std::fmt::{self, Display, Formatter};
|
||||
use std::vec::IntoIter;
|
||||
|
||||
use itertools::Itertools;
|
||||
use once_cell::sync::Lazy;
|
||||
@@ -47,10 +45,7 @@ fn span_pos(span: &Span) -> (Pos, Pos) {
|
||||
return fallback_span_pos(span);
|
||||
}
|
||||
|
||||
(
|
||||
Pos::new(start.line, start.column),
|
||||
Pos::new(end.line, end.column),
|
||||
)
|
||||
(Pos::new(start.line, start.column), Pos::new(end.line, end.column))
|
||||
}
|
||||
|
||||
fn parse_pos(span: &Span) -> Option<(usize, usize)> {
|
||||
@@ -79,9 +74,7 @@ fn parse_pos(span: &Span) -> Option<(usize, usize)> {
|
||||
fn fallback_span_pos(span: &Span) -> (Pos, Pos) {
|
||||
let (start, end) = match parse_pos(span) {
|
||||
Some(v) => v,
|
||||
None => proc_macro_error::abort_call_site!(
|
||||
"Cannot retrieve span information; please use nightly"
|
||||
),
|
||||
None => proc_macro_error::abort_call_site!("Cannot retrieve span information; please use nightly"),
|
||||
};
|
||||
(Pos::new(1, start), Pos::new(1, end))
|
||||
}
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
imports_granularity = "Module"
|
||||
max_width = 110
|
||||
comment_width = 100
|
||||
wrap_comments = true
|
||||
+64
-51
@@ -5,17 +5,17 @@ use std::io::Result as IoResult;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::string::String as StdString;
|
||||
|
||||
use crate::error::{Error, ErrorContext, Result};
|
||||
use crate::error::{Error, Result};
|
||||
use crate::function::Function;
|
||||
use crate::lua::Lua;
|
||||
use crate::state::{Lua, WeakLua};
|
||||
use crate::table::Table;
|
||||
use crate::value::{FromLuaMulti, IntoLua, IntoLuaMulti};
|
||||
use crate::value::{FromLuaMulti, IntoLuaMulti};
|
||||
|
||||
/// Trait for types [loadable by Lua] and convertible to a [`Chunk`]
|
||||
///
|
||||
/// [loadable by Lua]: https://www.lua.org/manual/5.4/manual.html#3.3.2
|
||||
/// [`Chunk`]: crate::Chunk
|
||||
pub trait AsChunk<'lua, 'a> {
|
||||
pub trait AsChunk<'a> {
|
||||
/// Returns optional chunk name
|
||||
fn name(&self) -> Option<StdString> {
|
||||
None
|
||||
@@ -24,7 +24,7 @@ pub trait AsChunk<'lua, 'a> {
|
||||
/// Returns optional chunk [environment]
|
||||
///
|
||||
/// [environment]: https://www.lua.org/manual/5.4/manual.html#2.2
|
||||
fn environment(&self, lua: &'lua Lua) -> Result<Option<Table<'lua>>> {
|
||||
fn environment(&self, lua: &Lua) -> Result<Option<Table>> {
|
||||
let _lua = lua; // suppress warning
|
||||
Ok(None)
|
||||
}
|
||||
@@ -38,43 +38,43 @@ pub trait AsChunk<'lua, 'a> {
|
||||
fn source(self) -> IoResult<Cow<'a, [u8]>>;
|
||||
}
|
||||
|
||||
impl<'a> AsChunk<'_, 'a> for &'a str {
|
||||
impl<'a> AsChunk<'a> for &'a str {
|
||||
fn source(self) -> IoResult<Cow<'a, [u8]>> {
|
||||
Ok(Cow::Borrowed(self.as_ref()))
|
||||
}
|
||||
}
|
||||
|
||||
impl AsChunk<'_, 'static> for StdString {
|
||||
impl AsChunk<'static> for StdString {
|
||||
fn source(self) -> IoResult<Cow<'static, [u8]>> {
|
||||
Ok(Cow::Owned(self.into_bytes()))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> AsChunk<'_, 'a> for &'a StdString {
|
||||
impl<'a> AsChunk<'a> for &'a StdString {
|
||||
fn source(self) -> IoResult<Cow<'a, [u8]>> {
|
||||
Ok(Cow::Borrowed(self.as_bytes()))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> AsChunk<'_, 'a> for &'a [u8] {
|
||||
impl<'a> AsChunk<'a> for &'a [u8] {
|
||||
fn source(self) -> IoResult<Cow<'a, [u8]>> {
|
||||
Ok(Cow::Borrowed(self))
|
||||
}
|
||||
}
|
||||
|
||||
impl AsChunk<'_, 'static> for Vec<u8> {
|
||||
impl AsChunk<'static> for Vec<u8> {
|
||||
fn source(self) -> IoResult<Cow<'static, [u8]>> {
|
||||
Ok(Cow::Owned(self))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> AsChunk<'_, 'a> for &'a Vec<u8> {
|
||||
impl<'a> AsChunk<'a> for &'a Vec<u8> {
|
||||
fn source(self) -> IoResult<Cow<'a, [u8]>> {
|
||||
Ok(Cow::Borrowed(self.as_ref()))
|
||||
}
|
||||
}
|
||||
|
||||
impl AsChunk<'_, 'static> for &Path {
|
||||
impl AsChunk<'static> for &Path {
|
||||
fn name(&self) -> Option<StdString> {
|
||||
Some(format!("@{}", self.display()))
|
||||
}
|
||||
@@ -84,7 +84,7 @@ impl AsChunk<'_, 'static> for &Path {
|
||||
}
|
||||
}
|
||||
|
||||
impl AsChunk<'_, 'static> for PathBuf {
|
||||
impl AsChunk<'static> for PathBuf {
|
||||
fn name(&self) -> Option<StdString> {
|
||||
Some(format!("@{}", self.display()))
|
||||
}
|
||||
@@ -98,10 +98,10 @@ impl AsChunk<'_, 'static> for PathBuf {
|
||||
///
|
||||
/// [`Lua::load`]: crate::Lua::load
|
||||
#[must_use = "`Chunk`s do nothing unless one of `exec`, `eval`, `call`, or `into_function` are called on them"]
|
||||
pub struct Chunk<'lua, 'a> {
|
||||
pub(crate) lua: &'lua Lua,
|
||||
pub struct Chunk<'a> {
|
||||
pub(crate) lua: WeakLua,
|
||||
pub(crate) name: StdString,
|
||||
pub(crate) env: Result<Option<Table<'lua>>>,
|
||||
pub(crate) env: Result<Option<Table>>,
|
||||
pub(crate) mode: Option<ChunkMode>,
|
||||
pub(crate) source: IoResult<Cow<'a, [u8]>>,
|
||||
#[cfg(feature = "luau")]
|
||||
@@ -134,7 +134,7 @@ pub struct Compiler {
|
||||
#[cfg(any(feature = "luau", doc))]
|
||||
impl Default for Compiler {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
const { Self::new() }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -240,7 +240,9 @@ impl Compiler {
|
||||
}
|
||||
|
||||
/// Compiles the `source` into bytecode.
|
||||
pub fn compile(&self, source: impl AsRef<[u8]>) -> Vec<u8> {
|
||||
///
|
||||
/// Returns `Error::SyntaxError` if the source code is invalid.
|
||||
pub fn compile(&self, source: impl AsRef<[u8]>) -> Result<Vec<u8>> {
|
||||
use std::os::raw::c_int;
|
||||
use std::ptr;
|
||||
|
||||
@@ -274,7 +276,7 @@ impl Compiler {
|
||||
vec2cstring_ptr!(mutable_globals, mutable_globals_ptr);
|
||||
vec2cstring_ptr!(userdata_types, userdata_types_ptr);
|
||||
|
||||
unsafe {
|
||||
let bytecode = unsafe {
|
||||
let mut options = ffi::lua_CompileOptions::default();
|
||||
options.optimizationLevel = self.optimization_level as c_int;
|
||||
options.debugLevel = self.debug_level as c_int;
|
||||
@@ -286,11 +288,23 @@ impl Compiler {
|
||||
options.mutableGlobals = mutable_globals_ptr;
|
||||
options.userdataTypes = userdata_types_ptr;
|
||||
ffi::luau_compile(source.as_ref(), options)
|
||||
};
|
||||
|
||||
if bytecode.first() == Some(&0) {
|
||||
// The rest of the bytecode is the error message starting with `:`
|
||||
// See https://github.com/luau-lang/luau/blob/0.640/Compiler/src/Compiler.cpp#L4336
|
||||
let message = String::from_utf8_lossy(&bytecode[2..]).to_string();
|
||||
return Err(Error::SyntaxError {
|
||||
incomplete_input: message.ends_with("<eof>"),
|
||||
message,
|
||||
});
|
||||
}
|
||||
|
||||
Ok(bytecode)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua, 'a> Chunk<'lua, 'a> {
|
||||
impl<'a> Chunk<'a> {
|
||||
/// Sets the name of this chunk, which results in more informative error traces.
|
||||
pub fn set_name(mut self, name: impl Into<String>) -> Self {
|
||||
self.name = name.into();
|
||||
@@ -299,8 +313,8 @@ impl<'lua, 'a> Chunk<'lua, 'a> {
|
||||
|
||||
/// Sets the environment of the loaded chunk to the given value.
|
||||
///
|
||||
/// In Lua >=5.2 main chunks always have exactly one upvalue, and this upvalue is used as the `_ENV`
|
||||
/// variable inside the chunk. By default this value is set to the global environment.
|
||||
/// In Lua >=5.2 main chunks always have exactly one upvalue, and this upvalue is used as the
|
||||
/// `_ENV` variable inside the chunk. By default this value is set to the global environment.
|
||||
///
|
||||
/// Calling this method changes the `_ENV` upvalue to the value provided, and variables inside
|
||||
/// the chunk will refer to the given environment rather than the global one.
|
||||
@@ -308,11 +322,8 @@ impl<'lua, 'a> Chunk<'lua, 'a> {
|
||||
/// All global variables (including the standard library!) are looked up in `_ENV`, so it may be
|
||||
/// necessary to populate the environment in order for scripts using custom environments to be
|
||||
/// useful.
|
||||
pub fn set_environment<V: IntoLua<'lua>>(mut self, env: V) -> Self {
|
||||
self.env = env
|
||||
.into_lua(self.lua)
|
||||
.and_then(|val| self.lua.unpack(val))
|
||||
.context("bad environment value");
|
||||
pub fn set_environment(mut self, env: Table) -> Self {
|
||||
self.env = Ok(Some(env));
|
||||
self
|
||||
}
|
||||
|
||||
@@ -341,8 +352,7 @@ impl<'lua, 'a> Chunk<'lua, 'a> {
|
||||
///
|
||||
/// This is equivalent to calling the chunk function with no arguments and no return values.
|
||||
pub fn exec(self) -> Result<()> {
|
||||
self.call::<_, ()>(())?;
|
||||
Ok(())
|
||||
self.call(())
|
||||
}
|
||||
|
||||
/// Asynchronously execute this chunk of code.
|
||||
@@ -363,7 +373,7 @@ impl<'lua, 'a> Chunk<'lua, 'a> {
|
||||
/// If the chunk can be parsed as an expression, this loads and executes the chunk and returns
|
||||
/// the value that it evaluates to. Otherwise, the chunk is interpreted as a block as normal,
|
||||
/// and this is equivalent to calling `exec`.
|
||||
pub fn eval<R: FromLuaMulti<'lua>>(self) -> Result<R> {
|
||||
pub fn eval<R: FromLuaMulti>(self) -> Result<R> {
|
||||
// Bytecode is always interpreted as a statement.
|
||||
// For source code, first try interpreting the lua as an expression by adding
|
||||
// "return", then as a statement. This is the same thing the
|
||||
@@ -388,7 +398,7 @@ impl<'lua, 'a> Chunk<'lua, 'a> {
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
|
||||
pub async fn eval_async<R>(self) -> Result<R>
|
||||
where
|
||||
R: FromLuaMulti<'lua> + 'lua,
|
||||
R: FromLuaMulti,
|
||||
{
|
||||
if self.detect_mode() == ChunkMode::Binary {
|
||||
self.call_async(()).await
|
||||
@@ -402,7 +412,7 @@ impl<'lua, 'a> Chunk<'lua, 'a> {
|
||||
/// Load the chunk function and call it with the given arguments.
|
||||
///
|
||||
/// This is equivalent to `into_function` and calling the resulting function.
|
||||
pub fn call<A: IntoLuaMulti<'lua>, R: FromLuaMulti<'lua>>(self, args: A) -> Result<R> {
|
||||
pub fn call<R: FromLuaMulti>(self, args: impl IntoLuaMulti) -> Result<R> {
|
||||
self.into_function()?.call(args)
|
||||
}
|
||||
|
||||
@@ -415,10 +425,9 @@ impl<'lua, 'a> Chunk<'lua, 'a> {
|
||||
/// [`call`]: #method.call
|
||||
#[cfg(feature = "async")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
|
||||
pub async fn call_async<A, R>(self, args: A) -> Result<R>
|
||||
pub async fn call_async<R>(self, args: impl IntoLuaMulti) -> Result<R>
|
||||
where
|
||||
A: IntoLuaMulti<'lua>,
|
||||
R: FromLuaMulti<'lua> + 'lua,
|
||||
R: FromLuaMulti,
|
||||
{
|
||||
self.into_function()?.call_async(args).await
|
||||
}
|
||||
@@ -427,7 +436,7 @@ impl<'lua, 'a> Chunk<'lua, 'a> {
|
||||
///
|
||||
/// This simply compiles the chunk without actually executing it.
|
||||
#[cfg_attr(not(feature = "luau"), allow(unused_mut))]
|
||||
pub fn into_function(mut self) -> Result<Function<'lua>> {
|
||||
pub fn into_function(mut self) -> Result<Function> {
|
||||
#[cfg(feature = "luau")]
|
||||
if self.compiler.is_some() {
|
||||
// We don't need to compile source if no compiler set
|
||||
@@ -436,26 +445,23 @@ impl<'lua, 'a> Chunk<'lua, 'a> {
|
||||
|
||||
let name = Self::convert_name(self.name)?;
|
||||
self.lua
|
||||
.load_chunk(Some(&name), self.env?, self.mode, self.source?.as_ref())
|
||||
.lock()
|
||||
.load_chunk(Some(&name), self.env?.as_ref(), self.mode, self.source?.as_ref())
|
||||
}
|
||||
|
||||
/// Compiles the chunk and changes mode to binary.
|
||||
///
|
||||
/// It does nothing if the chunk is already binary.
|
||||
/// It does nothing if the chunk is already binary or invalid.
|
||||
fn compile(&mut self) {
|
||||
if let Ok(ref source) = self.source {
|
||||
if self.detect_mode() == ChunkMode::Text {
|
||||
#[cfg(feature = "luau")]
|
||||
{
|
||||
let data = self
|
||||
.compiler
|
||||
.get_or_insert_with(Default::default)
|
||||
.compile(source);
|
||||
if let Ok(data) = self.compiler.get_or_insert_with(Default::default).compile(source) {
|
||||
self.source = Ok(Cow::Owned(data));
|
||||
self.mode = Some(ChunkMode::Binary);
|
||||
}
|
||||
#[cfg(not(feature = "luau"))]
|
||||
if let Ok(func) = self.lua.load_chunk(None, None, None, source.as_ref()) {
|
||||
if let Ok(func) = self.lua.lock().load_chunk(None, None, None, source.as_ref()) {
|
||||
let data = func.dump(false);
|
||||
self.source = Ok(Cow::Owned(data));
|
||||
self.mode = Some(ChunkMode::Binary);
|
||||
@@ -474,7 +480,8 @@ impl<'lua, 'a> Chunk<'lua, 'a> {
|
||||
let mut text_source = None;
|
||||
if let Ok(ref source) = self.source {
|
||||
if self.detect_mode() == ChunkMode::Text {
|
||||
if let Some(cache) = self.lua.app_data_ref::<ChunksCache>() {
|
||||
let lua = self.lua.lock();
|
||||
if let Some(cache) = lua.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);
|
||||
@@ -490,13 +497,14 @@ impl<'lua, 'a> Chunk<'lua, 'a> {
|
||||
self.compile();
|
||||
if let Ok(ref binary_source) = self.source {
|
||||
if self.detect_mode() == ChunkMode::Binary {
|
||||
if let Some(mut cache) = self.lua.app_data_mut::<ChunksCache>() {
|
||||
let lua = self.lua.lock();
|
||||
if let Some(mut cache) = lua.app_data_mut::<ChunksCache>() {
|
||||
cache.0.insert(text_source, binary_source.as_ref().to_vec());
|
||||
} else {
|
||||
let mut cache = ChunksCache(HashMap::new());
|
||||
cache.0.insert(text_source, binary_source.as_ref().to_vec());
|
||||
let _ = self.lua.try_set_app_data(cache);
|
||||
}
|
||||
let _ = lua.try_set_app_data(cache);
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -504,7 +512,7 @@ impl<'lua, 'a> Chunk<'lua, 'a> {
|
||||
self
|
||||
}
|
||||
|
||||
fn to_expression(&self) -> Result<Function<'lua>> {
|
||||
fn to_expression(&self) -> Result<Function> {
|
||||
// We assume that mode is Text
|
||||
let source = self.source.as_ref();
|
||||
let source = source.map_err(Error::runtime)?;
|
||||
@@ -515,11 +523,16 @@ impl<'lua, 'a> Chunk<'lua, 'a> {
|
||||
.compiler
|
||||
.as_ref()
|
||||
.map(|c| c.compile(&source))
|
||||
.transpose()?
|
||||
.unwrap_or(source);
|
||||
|
||||
let name = Self::convert_name(self.name.clone())?;
|
||||
self.lua
|
||||
.load_chunk(Some(&name), self.env.clone()?, None, &source)
|
||||
let env = match &self.env {
|
||||
Ok(Some(env)) => Some(env),
|
||||
Ok(None) => None,
|
||||
Err(err) => return Err(err.clone()),
|
||||
};
|
||||
self.lua.lock().load_chunk(Some(&name), env, None, &source)
|
||||
}
|
||||
|
||||
fn detect_mode(&self) -> ChunkMode {
|
||||
|
||||
+241
-392
File diff suppressed because it is too large
Load Diff
+7
-6
@@ -98,17 +98,17 @@ pub enum Error {
|
||||
/// A string containing more detailed error information.
|
||||
message: Option<StdString>,
|
||||
},
|
||||
/// [`Thread::resume`] was called on an inactive coroutine.
|
||||
/// [`Thread::resume`] was called on an unresumable coroutine.
|
||||
///
|
||||
/// A coroutine is inactive if its main function has returned or if an error has occurred inside
|
||||
/// the coroutine. Already running coroutines are also marked as inactive (unresumable).
|
||||
/// A coroutine is unresumable if its main function has returned or if an error has occurred
|
||||
/// inside the coroutine. Already running coroutines are also marked as unresumable.
|
||||
///
|
||||
/// [`Thread::status`] can be used to check if the coroutine can be resumed without causing this
|
||||
/// error.
|
||||
///
|
||||
/// [`Thread::resume`]: crate::Thread::resume
|
||||
/// [`Thread::status`]: crate::Thread::status
|
||||
CoroutineInactive,
|
||||
CoroutineUnresumable,
|
||||
/// An [`AnyUserData`] is not the expected type in a borrow.
|
||||
///
|
||||
/// This error can only happen when manually using [`AnyUserData`], or when implementing
|
||||
@@ -259,7 +259,7 @@ impl fmt::Display for Error {
|
||||
Some(ref message) => write!(fmt, " ({message})"),
|
||||
}
|
||||
}
|
||||
Error::CoroutineInactive => write!(fmt, "cannot resume inactive coroutine"),
|
||||
Error::CoroutineUnresumable => write!(fmt, "coroutine is non-resumable"),
|
||||
Error::UserDataTypeMismatch => write!(fmt, "userdata is not expected type"),
|
||||
Error::UserDataDestructed => write!(fmt, "userdata has been destructed"),
|
||||
Error::UserDataBorrowError => write!(fmt, "error borrowing userdata"),
|
||||
@@ -324,7 +324,8 @@ impl StdError for Error {
|
||||
// An error type with a source error should either return that error via source or
|
||||
// include that source's error message in its own Display output, but never both.
|
||||
// https://blog.rust-lang.org/inside-rust/2021/07/01/What-the-error-handling-project-group-is-working-towards.html
|
||||
// Given that we include source to fmt::Display implementation for `CallbackError`, this call returns nothing.
|
||||
// Given that we include source to fmt::Display implementation for `CallbackError`, this call
|
||||
// returns nothing.
|
||||
Error::CallbackError { .. } => None,
|
||||
Error::ExternalError(ref err) => err.source(),
|
||||
Error::WithContext { ref cause, .. } => match cause.as_ref() {
|
||||
|
||||
+83
-140
@@ -1,49 +1,25 @@
|
||||
use std::cell::RefCell;
|
||||
use std::mem;
|
||||
use std::os::raw::{c_int, c_void};
|
||||
use std::ptr;
|
||||
use std::slice;
|
||||
use std::{mem, ptr, slice};
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
use crate::lua::Lua;
|
||||
use crate::state::Lua;
|
||||
use crate::table::Table;
|
||||
use crate::types::{Callback, LuaRef, MaybeSend};
|
||||
use crate::types::{Callback, MaybeSend, ValueRef};
|
||||
use crate::util::{
|
||||
assert_stack, check_stack, linenumber_to_usize, pop_error, ptr_to_lossy_str, ptr_to_str,
|
||||
StackGuard,
|
||||
assert_stack, check_stack, linenumber_to_usize, pop_error, ptr_to_lossy_str, ptr_to_str, StackGuard,
|
||||
};
|
||||
use crate::value::{FromLuaMulti, IntoLua, IntoLuaMulti, Value};
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
use {
|
||||
crate::types::AsyncCallback,
|
||||
futures_util::future::{self, Future},
|
||||
std::future::{self, Future},
|
||||
};
|
||||
|
||||
/// Handle to an internal Lua function.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Function<'lua>(pub(crate) LuaRef<'lua>);
|
||||
|
||||
/// Owned handle to an internal Lua function.
|
||||
///
|
||||
/// The owned handle holds a *strong* reference to the current Lua instance.
|
||||
/// Be warned, if you place it into a Lua type (eg. [`UserData`] or a Rust callback), it is *very easy*
|
||||
/// to accidentally cause reference cycles that would prevent destroying Lua instance.
|
||||
///
|
||||
/// [`UserData`]: crate::UserData
|
||||
#[cfg(feature = "unstable")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "unstable")))]
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct OwnedFunction(pub(crate) crate::types::LuaOwnedRef);
|
||||
|
||||
#[cfg(feature = "unstable")]
|
||||
impl OwnedFunction {
|
||||
/// Get borrowed handle to the underlying Lua function.
|
||||
#[cfg_attr(feature = "send", allow(unused))]
|
||||
pub const fn to_ref(&self) -> Function {
|
||||
Function(self.0.to_ref())
|
||||
}
|
||||
}
|
||||
pub struct Function(pub(crate) ValueRef);
|
||||
|
||||
/// Contains information about a function.
|
||||
///
|
||||
@@ -58,7 +34,8 @@ pub struct FunctionInfo {
|
||||
///
|
||||
/// Always `None` for Luau.
|
||||
pub name_what: Option<&'static str>,
|
||||
/// A string `Lua` if the function is a Lua function, `C` if it is a C function, `main` if it is the main part of a chunk.
|
||||
/// A string `Lua` if the function is a Lua function, `C` if it is a C function, `main` if it is
|
||||
/// the main part of a chunk.
|
||||
pub what: &'static str,
|
||||
/// Source of the chunk that created the function.
|
||||
pub source: Option<String>,
|
||||
@@ -81,7 +58,7 @@ pub struct CoverageInfo {
|
||||
pub hits: Vec<i32>,
|
||||
}
|
||||
|
||||
impl<'lua> Function<'lua> {
|
||||
impl Function {
|
||||
/// Calls the function, passing `args` as function arguments.
|
||||
///
|
||||
/// The function's return values are converted to the generic type `R`.
|
||||
@@ -98,7 +75,7 @@ impl<'lua> Function<'lua> {
|
||||
///
|
||||
/// let tostring: Function = globals.get("tostring")?;
|
||||
///
|
||||
/// assert_eq!(tostring.call::<_, String>(123)?, "123");
|
||||
/// assert_eq!(tostring.call::<String>(123)?, "123");
|
||||
///
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
@@ -117,13 +94,13 @@ impl<'lua> Function<'lua> {
|
||||
/// end
|
||||
/// "#).eval()?;
|
||||
///
|
||||
/// assert_eq!(sum.call::<_, u32>((3, 4))?, 3 + 4);
|
||||
/// assert_eq!(sum.call::<u32>((3, 4))?, 3 + 4);
|
||||
///
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn call<A: IntoLuaMulti<'lua>, R: FromLuaMulti<'lua>>(&self, args: A) -> Result<R> {
|
||||
let lua = self.0.lua;
|
||||
pub fn call<R: FromLuaMulti>(&self, args: impl IntoLuaMulti) -> Result<R> {
|
||||
let lua = self.0.lua.lock();
|
||||
let state = lua.state();
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(state);
|
||||
@@ -134,7 +111,7 @@ impl<'lua> Function<'lua> {
|
||||
let stack_start = ffi::lua_gettop(state);
|
||||
// Push function and the arguments
|
||||
lua.push_ref(&self.0);
|
||||
let nargs = args.push_into_stack_multi(lua)?;
|
||||
let nargs = args.push_into_stack_multi(&lua)?;
|
||||
// Call the function
|
||||
let ret = ffi::lua_pcall(state, nargs, ffi::LUA_MULTRET, stack_start);
|
||||
if ret != ffi::LUA_OK {
|
||||
@@ -142,7 +119,7 @@ impl<'lua> Function<'lua> {
|
||||
}
|
||||
// Get the results
|
||||
let nresults = ffi::lua_gettop(state) - stack_start;
|
||||
R::from_stack_multi(nresults, lua)
|
||||
R::from_stack_multi(nresults, &lua)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -176,17 +153,18 @@ impl<'lua> Function<'lua> {
|
||||
/// [`AsyncThread`]: crate::AsyncThread
|
||||
#[cfg(feature = "async")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
|
||||
pub fn call_async<A, R>(&self, args: A) -> impl Future<Output = Result<R>> + 'lua
|
||||
pub fn call_async<R>(&self, args: impl IntoLuaMulti) -> impl Future<Output = Result<R>>
|
||||
where
|
||||
A: IntoLuaMulti<'lua>,
|
||||
R: FromLuaMulti<'lua> + 'lua,
|
||||
R: FromLuaMulti,
|
||||
{
|
||||
let lua = self.0.lua;
|
||||
let thread_res = lua.create_recycled_thread(self).map(|th| {
|
||||
let mut th = th.into_async(args);
|
||||
th.set_recyclable(true);
|
||||
th
|
||||
});
|
||||
let lua = self.0.lua.lock();
|
||||
let thread_res = unsafe {
|
||||
lua.create_recycled_thread(self).map(|th| {
|
||||
let mut th = th.into_async(args);
|
||||
th.set_recyclable(true);
|
||||
th
|
||||
})
|
||||
};
|
||||
async move { thread_res?.await }
|
||||
}
|
||||
|
||||
@@ -209,15 +187,15 @@ impl<'lua> Function<'lua> {
|
||||
/// "#).eval()?;
|
||||
///
|
||||
/// let bound_a = sum.bind(1)?;
|
||||
/// assert_eq!(bound_a.call::<_, u32>(2)?, 1 + 2);
|
||||
/// assert_eq!(bound_a.call::<u32>(2)?, 1 + 2);
|
||||
///
|
||||
/// let bound_a_and_b = sum.bind(13)?.bind(57)?;
|
||||
/// assert_eq!(bound_a_and_b.call::<_, u32>(())?, 13 + 57);
|
||||
/// assert_eq!(bound_a_and_b.call::<u32>(())?, 13 + 57);
|
||||
///
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn bind<A: IntoLuaMulti<'lua>>(&self, args: A) -> Result<Function<'lua>> {
|
||||
pub fn bind(&self, args: impl IntoLuaMulti) -> Result<Function> {
|
||||
unsafe extern "C-unwind" fn args_wrapper_impl(state: *mut ffi::lua_State) -> c_int {
|
||||
let nargs = ffi::lua_gettop(state);
|
||||
let nbinds = ffi::lua_tointeger(state, ffi::lua_upvalueindex(1)) as c_int;
|
||||
@@ -233,10 +211,10 @@ impl<'lua> Function<'lua> {
|
||||
nargs + nbinds
|
||||
}
|
||||
|
||||
let lua = self.0.lua;
|
||||
let lua = self.0.lua.lock();
|
||||
let state = lua.state();
|
||||
|
||||
let args = args.into_lua_multi(lua)?;
|
||||
let args = args.into_lua_multi(lua.lua())?;
|
||||
let nargs = args.len() as c_int;
|
||||
|
||||
if nargs == 0 {
|
||||
@@ -252,7 +230,7 @@ impl<'lua> Function<'lua> {
|
||||
check_stack(state, nargs + 3)?;
|
||||
|
||||
ffi::lua_pushinteger(state, nargs as ffi::lua_Integer);
|
||||
for arg in args {
|
||||
for arg in &args {
|
||||
lua.push_value(arg)?;
|
||||
}
|
||||
protect_lua!(state, nargs + 1, 1, fn(state) {
|
||||
@@ -262,6 +240,7 @@ impl<'lua> Function<'lua> {
|
||||
Function(lua.pop_ref())
|
||||
};
|
||||
|
||||
let lua = lua.lua();
|
||||
lua.load(
|
||||
r#"
|
||||
local func, args_wrapper = ...
|
||||
@@ -272,7 +251,7 @@ impl<'lua> Function<'lua> {
|
||||
)
|
||||
.try_cache()
|
||||
.set_name("__mlua_bind")
|
||||
.call((self.clone(), args_wrapper))
|
||||
.call((self, args_wrapper))
|
||||
}
|
||||
|
||||
/// Returns the environment of the Lua function.
|
||||
@@ -281,7 +260,7 @@ impl<'lua> Function<'lua> {
|
||||
///
|
||||
/// This function always returns `None` for Rust/C functions.
|
||||
pub fn environment(&self) -> Option<Table> {
|
||||
let lua = self.0.lua;
|
||||
let lua = self.0.lua.lock();
|
||||
let state = lua.state();
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(state);
|
||||
@@ -318,7 +297,7 @@ impl<'lua> Function<'lua> {
|
||||
///
|
||||
/// This function does nothing for Rust/C functions.
|
||||
pub fn set_environment(&self, env: Table) -> Result<bool> {
|
||||
let lua = self.0.lua;
|
||||
let lua = self.0.lua.lock();
|
||||
let state = lua.state();
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(state);
|
||||
@@ -342,6 +321,7 @@ impl<'lua> Function<'lua> {
|
||||
ffi::lua_pop(state, 1);
|
||||
// Create an anonymous function with the new environment
|
||||
let f_with_env = lua
|
||||
.lua()
|
||||
.load("return _ENV")
|
||||
.set_environment(env)
|
||||
.try_cache()
|
||||
@@ -364,7 +344,7 @@ impl<'lua> Function<'lua> {
|
||||
///
|
||||
/// [`lua_getinfo`]: https://www.lua.org/manual/5.4/manual.html#lua_getinfo
|
||||
pub fn info(&self) -> FunctionInfo {
|
||||
let lua = self.0.lua;
|
||||
let lua = self.0.lua.lock();
|
||||
let state = lua.state();
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(state);
|
||||
@@ -425,7 +405,7 @@ impl<'lua> Function<'lua> {
|
||||
0
|
||||
}
|
||||
|
||||
let lua = self.0.lua;
|
||||
let lua = self.0.lua.lock();
|
||||
let state = lua.state();
|
||||
let mut data: Vec<u8> = Vec::new();
|
||||
unsafe {
|
||||
@@ -443,8 +423,8 @@ impl<'lua> Function<'lua> {
|
||||
|
||||
/// Retrieves recorded coverage information about this Lua function including inner calls.
|
||||
///
|
||||
/// This function takes a callback as an argument and calls it providing [`CoverageInfo`] snapshot
|
||||
/// per each executed inner function.
|
||||
/// This function takes a callback as an argument and calls it providing [`CoverageInfo`]
|
||||
/// snapshot per each executed inner function.
|
||||
///
|
||||
/// Recording of coverage information is controlled by [`Compiler::set_coverage_level`] option.
|
||||
///
|
||||
@@ -482,7 +462,7 @@ impl<'lua> Function<'lua> {
|
||||
});
|
||||
}
|
||||
|
||||
let lua = self.0.lua;
|
||||
let lua = self.0.lua.lock();
|
||||
let state = lua.state();
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(state);
|
||||
@@ -514,134 +494,97 @@ impl<'lua> Function<'lua> {
|
||||
#[cfg(feature = "luau")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
|
||||
pub fn deep_clone(&self) -> Self {
|
||||
let ref_thread = self.0.lua.ref_thread();
|
||||
let lua = self.0.lua.lock();
|
||||
let ref_thread = lua.ref_thread();
|
||||
unsafe {
|
||||
if ffi::lua_iscfunction(ref_thread, self.0.index) != 0 {
|
||||
return self.clone();
|
||||
}
|
||||
|
||||
ffi::lua_clonefunction(ref_thread, self.0.index);
|
||||
Function(self.0.lua.pop_ref_thread())
|
||||
Function(lua.pop_ref_thread())
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert this handle to owned version.
|
||||
#[cfg(all(feature = "unstable", any(not(feature = "send"), doc)))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all(feature = "unstable", not(feature = "send")))))]
|
||||
#[inline]
|
||||
pub fn into_owned(self) -> OwnedFunction {
|
||||
OwnedFunction(self.0.into_owned())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> PartialEq for Function<'lua> {
|
||||
impl PartialEq for Function {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.0 == other.0
|
||||
}
|
||||
}
|
||||
|
||||
// Additional shortcuts
|
||||
#[cfg(feature = "unstable")]
|
||||
impl OwnedFunction {
|
||||
/// Calls the function, passing `args` as function arguments.
|
||||
///
|
||||
/// This is a shortcut for [`Function::call()`].
|
||||
#[inline]
|
||||
pub fn call<'lua, A, R>(&'lua self, args: A) -> Result<R>
|
||||
where
|
||||
A: IntoLuaMulti<'lua>,
|
||||
R: FromLuaMulti<'lua>,
|
||||
{
|
||||
self.to_ref().call(args)
|
||||
}
|
||||
|
||||
/// Returns a future that, when polled, calls `self`, passing `args` as function arguments,
|
||||
/// and drives the execution.
|
||||
///
|
||||
/// This is a shortcut for [`Function::call_async()`].
|
||||
#[cfg(feature = "async")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
|
||||
#[inline]
|
||||
pub async fn call_async<'lua, A, R>(&'lua self, args: A) -> Result<R>
|
||||
where
|
||||
A: IntoLuaMulti<'lua>,
|
||||
R: FromLuaMulti<'lua> + 'lua,
|
||||
{
|
||||
self.to_ref().call_async(args).await
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct WrappedFunction<'lua>(pub(crate) Callback<'lua, 'static>);
|
||||
pub(crate) struct WrappedFunction(pub(crate) Callback);
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
pub(crate) struct WrappedAsyncFunction<'lua>(pub(crate) AsyncCallback<'lua, 'static>);
|
||||
pub(crate) struct WrappedAsyncFunction(pub(crate) AsyncCallback);
|
||||
|
||||
impl<'lua> Function<'lua> {
|
||||
/// Wraps a Rust function or closure, returning an opaque type that implements [`IntoLua`] trait.
|
||||
impl Function {
|
||||
/// Wraps a Rust function or closure, returning an opaque type that implements [`IntoLua`]
|
||||
/// trait.
|
||||
#[inline]
|
||||
pub fn wrap<A, R, F>(func: F) -> impl IntoLua<'lua>
|
||||
pub fn wrap<A, R, F>(func: F) -> impl IntoLua
|
||||
where
|
||||
A: FromLuaMulti<'lua>,
|
||||
R: IntoLuaMulti<'lua>,
|
||||
F: Fn(&'lua Lua, A) -> Result<R> + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
R: IntoLuaMulti,
|
||||
F: Fn(&Lua, A) -> Result<R> + MaybeSend + 'static,
|
||||
{
|
||||
WrappedFunction(Box::new(move |lua, nargs| unsafe {
|
||||
let args = A::from_stack_args(nargs, 1, None, lua)?;
|
||||
func(lua, args)?.push_into_stack_multi(lua)
|
||||
func(lua.lua(), args)?.push_into_stack_multi(lua)
|
||||
}))
|
||||
}
|
||||
|
||||
/// Wraps a Rust mutable closure, returning an opaque type that implements [`IntoLua`] trait.
|
||||
#[inline]
|
||||
pub fn wrap_mut<A, R, F>(func: F) -> impl IntoLua<'lua>
|
||||
pub fn wrap_mut<A, R, F>(func: F) -> impl IntoLua
|
||||
where
|
||||
A: FromLuaMulti<'lua>,
|
||||
R: IntoLuaMulti<'lua>,
|
||||
F: FnMut(&'lua Lua, A) -> Result<R> + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
R: IntoLuaMulti,
|
||||
F: FnMut(&Lua, A) -> Result<R> + MaybeSend + 'static,
|
||||
{
|
||||
let func = RefCell::new(func);
|
||||
WrappedFunction(Box::new(move |lua, nargs| unsafe {
|
||||
let mut func = func
|
||||
.try_borrow_mut()
|
||||
.map_err(|_| Error::RecursiveMutCallback)?;
|
||||
let mut func = func.try_borrow_mut().map_err(|_| Error::RecursiveMutCallback)?;
|
||||
let args = A::from_stack_args(nargs, 1, None, lua)?;
|
||||
func(lua, args)?.push_into_stack_multi(lua)
|
||||
func(lua.lua(), args)?.push_into_stack_multi(lua)
|
||||
}))
|
||||
}
|
||||
|
||||
/// Wraps a Rust async function or closure, returning an opaque type that implements [`IntoLua`] trait.
|
||||
/// Wraps a Rust async function or closure, returning an opaque type that implements [`IntoLua`]
|
||||
/// trait.
|
||||
#[cfg(feature = "async")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
|
||||
pub fn wrap_async<A, R, F, FR>(func: F) -> impl IntoLua<'lua>
|
||||
pub fn wrap_async<A, R, F, FR>(func: F) -> impl IntoLua
|
||||
where
|
||||
A: FromLuaMulti<'lua>,
|
||||
R: IntoLuaMulti<'lua>,
|
||||
F: Fn(&'lua Lua, A) -> FR + MaybeSend + 'static,
|
||||
FR: Future<Output = Result<R>> + 'lua,
|
||||
A: FromLuaMulti,
|
||||
R: IntoLuaMulti,
|
||||
F: Fn(Lua, A) -> FR + MaybeSend + 'static,
|
||||
FR: Future<Output = Result<R>> + MaybeSend + 'static,
|
||||
{
|
||||
WrappedAsyncFunction(Box::new(move |lua, args| unsafe {
|
||||
let args = match A::from_lua_args(args, 1, None, lua) {
|
||||
WrappedAsyncFunction(Box::new(move |rawlua, nargs| unsafe {
|
||||
let args = match A::from_stack_args(nargs, 1, None, rawlua) {
|
||||
Ok(args) => args,
|
||||
Err(e) => return Box::pin(future::err(e)),
|
||||
Err(e) => return Box::pin(future::ready(Err(e))),
|
||||
};
|
||||
let fut = func(lua, args);
|
||||
Box::pin(async move { fut.await?.push_into_stack_multi(lua) })
|
||||
let lua = rawlua.lua().clone();
|
||||
let fut = func(lua.clone(), args);
|
||||
Box::pin(async move { fut.await?.push_into_stack_multi(lua.raw_lua()) })
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> IntoLua<'lua> for WrappedFunction<'lua> {
|
||||
impl IntoLua for WrappedFunction {
|
||||
#[inline]
|
||||
fn into_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
|
||||
lua.create_callback(self.0).map(Value::Function)
|
||||
fn into_lua(self, lua: &Lua) -> Result<Value> {
|
||||
lua.lock().create_callback(self.0).map(Value::Function)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
impl<'lua> IntoLua<'lua> for WrappedAsyncFunction<'lua> {
|
||||
impl IntoLua for WrappedAsyncFunction {
|
||||
#[inline]
|
||||
fn into_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
|
||||
lua.create_async_callback(self.0).map(Value::Function)
|
||||
fn into_lua(self, lua: &Lua) -> Result<Value> {
|
||||
lua.lock().create_async_callback(self.0).map(Value::Function)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -649,8 +592,8 @@ impl<'lua> IntoLua<'lua> for WrappedAsyncFunction<'lua> {
|
||||
mod assertions {
|
||||
use super::*;
|
||||
|
||||
#[cfg(not(feature = "send"))]
|
||||
static_assertions::assert_not_impl_any!(Function: Send);
|
||||
|
||||
#[cfg(all(feature = "unstable", not(feature = "send")))]
|
||||
static_assertions::assert_not_impl_any!(OwnedFunction: Send);
|
||||
#[cfg(feature = "send")]
|
||||
static_assertions::assert_impl_all!(Function: Send, Sync);
|
||||
}
|
||||
|
||||
+33
-21
@@ -1,12 +1,14 @@
|
||||
use std::borrow::Cow;
|
||||
use std::cell::UnsafeCell;
|
||||
use std::ops::Deref;
|
||||
#[cfg(not(feature = "luau"))]
|
||||
use std::ops::{BitOr, BitOrAssign};
|
||||
use std::os::raw::c_int;
|
||||
|
||||
use ffi::lua_Debug;
|
||||
|
||||
use crate::lua::Lua;
|
||||
use crate::state::RawLua;
|
||||
use crate::types::ReentrantMutexGuard;
|
||||
use crate::util::{linenumber_to_usize, ptr_to_lossy_str, ptr_to_str};
|
||||
|
||||
/// Contains information about currently executing Lua code.
|
||||
@@ -18,25 +20,44 @@ use crate::util::{linenumber_to_usize, ptr_to_lossy_str, ptr_to_str};
|
||||
///
|
||||
/// [lua_doc]: https://www.lua.org/manual/5.4/manual.html#lua_Debug
|
||||
/// [`Lua::set_hook`]: crate::Lua::set_hook
|
||||
pub struct Debug<'lua> {
|
||||
lua: &'lua Lua,
|
||||
pub struct Debug<'a> {
|
||||
lua: EitherLua<'a>,
|
||||
ar: ActivationRecord,
|
||||
#[cfg(feature = "luau")]
|
||||
level: c_int,
|
||||
}
|
||||
|
||||
impl<'lua> Debug<'lua> {
|
||||
enum EitherLua<'a> {
|
||||
Owned(ReentrantMutexGuard<'a, RawLua>),
|
||||
#[cfg(not(feature = "luau"))]
|
||||
pub(crate) fn new(lua: &'lua Lua, ar: *mut lua_Debug) -> Self {
|
||||
Borrowed(&'a RawLua),
|
||||
}
|
||||
|
||||
impl Deref for EitherLua<'_> {
|
||||
type Target = RawLua;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
match self {
|
||||
EitherLua::Owned(guard) => guard,
|
||||
#[cfg(not(feature = "luau"))]
|
||||
EitherLua::Borrowed(lua) => lua,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Debug<'a> {
|
||||
// We assume the lock is held when this function is called.
|
||||
#[cfg(not(feature = "luau"))]
|
||||
pub(crate) fn new(lua: &'a RawLua, ar: *mut lua_Debug) -> Self {
|
||||
Debug {
|
||||
lua,
|
||||
lua: EitherLua::Borrowed(lua),
|
||||
ar: ActivationRecord::Borrowed(ar),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn new_owned(lua: &'lua Lua, _level: c_int, ar: lua_Debug) -> Self {
|
||||
pub(crate) fn new_owned(guard: ReentrantMutexGuard<'a, RawLua>, _level: c_int, ar: lua_Debug) -> Self {
|
||||
Debug {
|
||||
lua,
|
||||
lua: EitherLua::Owned(guard),
|
||||
ar: ActivationRecord::Owned(UnsafeCell::new(ar)),
|
||||
#[cfg(feature = "luau")]
|
||||
level: _level,
|
||||
@@ -234,7 +255,8 @@ pub struct DebugSource<'a> {
|
||||
pub line_defined: Option<usize>,
|
||||
/// The line number where the definition of the function ends (not set by Luau).
|
||||
pub last_line_defined: Option<usize>,
|
||||
/// A string `Lua` if the function is a Lua function, `C` if it is a C function, `main` if it is the main part of a chunk.
|
||||
/// A string `Lua` if the function is a Lua function, `C` if it is a C function, `main` if it is
|
||||
/// the main part of a chunk.
|
||||
pub what: &'static str,
|
||||
}
|
||||
|
||||
@@ -242,20 +264,10 @@ pub struct DebugSource<'a> {
|
||||
pub struct DebugStack {
|
||||
pub num_ups: i32,
|
||||
/// Requires `feature = "lua54/lua53/lua52/luau"`
|
||||
#[cfg(any(
|
||||
feature = "lua54",
|
||||
feature = "lua53",
|
||||
feature = "lua52",
|
||||
feature = "luau"
|
||||
))]
|
||||
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52", feature = "luau"))]
|
||||
pub num_params: i32,
|
||||
/// Requires `feature = "lua54/lua53/lua52/luau"`
|
||||
#[cfg(any(
|
||||
feature = "lua54",
|
||||
feature = "lua53",
|
||||
feature = "lua52",
|
||||
feature = "luau"
|
||||
))]
|
||||
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52", feature = "luau"))]
|
||||
pub is_vararg: bool,
|
||||
}
|
||||
|
||||
|
||||
+27
-36
@@ -27,9 +27,9 @@
|
||||
//!
|
||||
//! # Serde support
|
||||
//!
|
||||
//! The [`LuaSerdeExt`] trait implemented for [`Lua`] allows conversion from Rust types to Lua values
|
||||
//! and vice versa using serde. Any user defined data type that implements [`serde::Serialize`] or
|
||||
//! [`serde::Deserialize`] can be converted.
|
||||
//! The [`LuaSerdeExt`] trait implemented for [`Lua`] allows conversion from Rust types to Lua
|
||||
//! values and vice versa using serde. Any user defined data type that implements
|
||||
//! [`serde::Serialize`] or [`serde::Deserialize`] can be converted.
|
||||
//! For convenience, additional functionality to handle `NULL` values and arrays is provided.
|
||||
//!
|
||||
//! The [`Value`] enum implements [`serde::Serialize`] trait to support serializing Lua values
|
||||
@@ -40,14 +40,14 @@
|
||||
//! # Async/await support
|
||||
//!
|
||||
//! The [`create_async_function`] allows creating non-blocking functions that returns [`Future`].
|
||||
//! Lua code with async capabilities can be executed by [`call_async`] family of functions or polling
|
||||
//! [`AsyncThread`] using any runtime (eg. Tokio).
|
||||
//! Lua code with async capabilities can be executed by [`call_async`] family of functions or
|
||||
//! polling [`AsyncThread`] using any runtime (eg. Tokio).
|
||||
//!
|
||||
//! Requires `feature = "async"`.
|
||||
//!
|
||||
//! # `Send` requirement
|
||||
//! By default `mlua` is `!Send`. This can be changed by enabling `feature = "send"` that adds `Send` requirement
|
||||
//! to [`Function`]s and [`UserData`].
|
||||
//! By default `mlua` is `!Send`. This can be changed by enabling `feature = "send"` that adds
|
||||
//! `Send` requirement to [`Function`]s and [`UserData`].
|
||||
//!
|
||||
//! [Lua programming language]: https://www.lua.org/
|
||||
//! [`Lua`]: crate::Lua
|
||||
@@ -73,7 +73,6 @@
|
||||
|
||||
// Deny warnings inside doc tests / examples. When this isn't present, rustdoc doesn't show *any*
|
||||
// warnings at all.
|
||||
#![doc(test(attr(warn(warnings))))] // FIXME: Remove this when rust-lang/rust#123748 is fixed
|
||||
#![cfg_attr(docsrs, feature(doc_cfg))]
|
||||
|
||||
#[macro_use]
|
||||
@@ -84,45 +83,44 @@ mod conversion;
|
||||
mod error;
|
||||
mod function;
|
||||
mod hook;
|
||||
mod lua;
|
||||
#[cfg(feature = "luau")]
|
||||
mod luau;
|
||||
mod memory;
|
||||
mod multi;
|
||||
mod scope;
|
||||
// mod scope;
|
||||
mod state;
|
||||
mod stdlib;
|
||||
mod string;
|
||||
mod table;
|
||||
mod thread;
|
||||
mod traits;
|
||||
mod types;
|
||||
mod userdata;
|
||||
mod userdata_ext;
|
||||
mod userdata_impl;
|
||||
mod util;
|
||||
mod value;
|
||||
|
||||
pub mod prelude;
|
||||
|
||||
pub use bstr::BString;
|
||||
pub use ffi::{self, lua_CFunction, lua_State};
|
||||
|
||||
pub use crate::chunk::{AsChunk, Chunk, ChunkMode};
|
||||
pub use crate::error::{Error, ErrorContext, ExternalError, ExternalResult, Result};
|
||||
pub use crate::function::{Function, FunctionInfo};
|
||||
pub use crate::hook::{Debug, DebugEvent, DebugNames, DebugSource, DebugStack};
|
||||
pub use crate::lua::{GCMode, Lua, LuaOptions};
|
||||
pub use crate::multi::Variadic;
|
||||
pub use crate::scope::Scope;
|
||||
pub use crate::state::{GCMode, Lua, LuaOptions};
|
||||
// pub use crate::scope::Scope;
|
||||
pub use crate::stdlib::StdLib;
|
||||
pub use crate::string::String;
|
||||
pub use crate::table::{Table, TableExt, TablePairs, TableSequence};
|
||||
pub use crate::string::{BorrowedBytes, BorrowedStr, String};
|
||||
pub use crate::table::{Table, TablePairs, TableSequence};
|
||||
pub use crate::thread::{Thread, ThreadStatus};
|
||||
pub use crate::types::{AppDataRef, AppDataRefMut, Integer, LightUserData, Number, RegistryKey};
|
||||
pub use crate::traits::ObjectLike;
|
||||
pub use crate::types::{AppDataRef, AppDataRefMut, Integer, LightUserData, MaybeSend, Number, RegistryKey};
|
||||
pub use crate::userdata::{
|
||||
AnyUserData, MetaMethod, UserData, UserDataFields, UserDataMetatable, UserDataMethods,
|
||||
UserDataRef, UserDataRefMut,
|
||||
AnyUserData, MetaMethod, UserData, UserDataFields, UserDataMetatable, UserDataMethods, UserDataRef,
|
||||
UserDataRefMut, UserDataRegistry,
|
||||
};
|
||||
pub use crate::userdata_ext::AnyUserDataExt;
|
||||
pub use crate::userdata_impl::UserDataRegistry;
|
||||
pub use crate::value::{FromLua, FromLuaMulti, IntoLua, IntoLuaMulti, MultiValue, Nil, Value};
|
||||
|
||||
#[cfg(not(feature = "luau"))]
|
||||
@@ -141,9 +139,7 @@ pub use crate::thread::AsyncThread;
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
#[doc(inline)]
|
||||
pub use crate::serde::{
|
||||
de::Options as DeserializeOptions, ser::Options as SerializeOptions, LuaSerdeExt,
|
||||
};
|
||||
pub use crate::serde::{de::Options as DeserializeOptions, ser::Options as SerializeOptions, LuaSerdeExt};
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "serialize")))]
|
||||
@@ -154,13 +150,6 @@ pub mod serde;
|
||||
#[macro_use]
|
||||
extern crate mlua_derive;
|
||||
|
||||
// Unstable features
|
||||
#[cfg(feature = "unstable")]
|
||||
pub use crate::{
|
||||
function::OwnedFunction, string::OwnedString, table::OwnedTable, thread::OwnedThread,
|
||||
userdata::OwnedAnyUserData,
|
||||
};
|
||||
|
||||
/// Create a type that implements [`AsChunk`] and can capture Rust variables.
|
||||
///
|
||||
/// This macro allows to write Lua code directly in Rust code.
|
||||
@@ -201,8 +190,8 @@ pub use crate::{
|
||||
///
|
||||
/// Other minor limitations:
|
||||
///
|
||||
/// - Certain escape codes in string literals don't work.
|
||||
/// (Specifically: `\a`, `\b`, `\f`, `\v`, `\123` (octal escape codes), `\u`, and `\U`).
|
||||
/// - Certain escape codes in string literals don't work. (Specifically: `\a`, `\b`, `\f`, `\v`,
|
||||
/// `\123` (octal escape codes), `\u`, and `\U`).
|
||||
///
|
||||
/// These are accepted: : `\\`, `\n`, `\t`, `\r`, `\xAB` (hex escape codes), and `\0`.
|
||||
///
|
||||
@@ -266,11 +255,13 @@ pub use mlua_derive::FromLua;
|
||||
/// ...
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
#[cfg(any(feature = "module", docsrs))]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "module")))]
|
||||
pub use mlua_derive::lua_module;
|
||||
|
||||
#[cfg(all(feature = "module", feature = "send"))]
|
||||
compile_error!("`send` feature is not supported in module mode");
|
||||
|
||||
pub(crate) mod private {
|
||||
use super::*;
|
||||
|
||||
@@ -279,6 +270,6 @@ pub(crate) mod private {
|
||||
impl Sealed for Error {}
|
||||
impl<T> Sealed for std::result::Result<T, Error> {}
|
||||
impl Sealed for Lua {}
|
||||
impl Sealed for Table<'_> {}
|
||||
impl Sealed for AnyUserData<'_> {}
|
||||
impl Sealed for Table {}
|
||||
impl Sealed for AnyUserData {}
|
||||
}
|
||||
|
||||
-3667
File diff suppressed because it is too large
Load Diff
+2
-5
@@ -2,7 +2,7 @@ use std::ffi::CStr;
|
||||
use std::os::raw::{c_float, c_int};
|
||||
|
||||
use crate::error::Result;
|
||||
use crate::lua::Lua;
|
||||
use crate::state::Lua;
|
||||
|
||||
// Since Luau has some missing standard functions, we re-implement them here
|
||||
|
||||
@@ -10,10 +10,7 @@ impl Lua {
|
||||
pub(crate) unsafe fn configure_luau(&self) -> Result<()> {
|
||||
let globals = self.globals();
|
||||
|
||||
globals.raw_set(
|
||||
"collectgarbage",
|
||||
self.create_c_function(lua_collectgarbage)?,
|
||||
)?;
|
||||
globals.raw_set("collectgarbage", self.create_c_function(lua_collectgarbage)?)?;
|
||||
globals.raw_set("vector", self.create_c_function(lua_vector)?)?;
|
||||
|
||||
// Set `_VERSION` global to include version number
|
||||
|
||||
+25
-24
@@ -7,9 +7,8 @@ use std::{env, fs};
|
||||
|
||||
use crate::chunk::ChunkMode;
|
||||
use crate::error::Result;
|
||||
use crate::lua::Lua;
|
||||
use crate::state::Lua;
|
||||
use crate::table::Table;
|
||||
use crate::types::RegistryKey;
|
||||
use crate::value::{IntoLua, Value};
|
||||
|
||||
#[cfg(unix)]
|
||||
@@ -27,9 +26,6 @@ const TARGET_MLUA_LUAU_ABI_VERSION: u32 = 1;
|
||||
#[used]
|
||||
pub static MLUA_LUAU_ABI_VERSION: u32 = TARGET_MLUA_LUAU_ABI_VERSION;
|
||||
|
||||
// We keep reference to the `package` table in registry under this key
|
||||
struct PackageKey(RegistryKey);
|
||||
|
||||
// We keep reference to the loaded dylibs in application data
|
||||
#[cfg(unix)]
|
||||
struct LoadedDylibs(FxHashMap<PathBuf, Library>);
|
||||
@@ -51,9 +47,8 @@ impl std::ops::DerefMut for LoadedDylibs {
|
||||
}
|
||||
|
||||
pub(crate) fn register_package_module(lua: &Lua) -> Result<()> {
|
||||
// Create the package table and store it in app_data for later use (bypassing globals lookup)
|
||||
// Create the package table
|
||||
let package = lua.create_table()?;
|
||||
lua.set_app_data(PackageKey(lua.create_registry_value(package.clone())?));
|
||||
|
||||
// Set `package.path`
|
||||
let mut search_path = env::var("LUAU_PATH")
|
||||
@@ -81,13 +76,19 @@ pub(crate) fn register_package_module(lua: &Lua) -> Result<()> {
|
||||
}
|
||||
|
||||
// Set `package.loaded` (table with a list of loaded modules)
|
||||
let loaded = lua.create_table()?;
|
||||
package.raw_set("loaded", loaded.clone())?;
|
||||
lua.set_named_registry_value("_LOADED", loaded)?;
|
||||
let loaded = if let Ok(Some(loaded)) = lua.named_registry_value::<Option<Table>>("_LOADED") {
|
||||
package.raw_set("loaded", &loaded)?;
|
||||
loaded
|
||||
} else {
|
||||
let loaded = lua.create_table()?;
|
||||
package.raw_set("loaded", &loaded)?;
|
||||
lua.set_named_registry_value("_LOADED", &loaded)?;
|
||||
loaded
|
||||
};
|
||||
|
||||
// Set `package.loaders`
|
||||
let loaders = lua.create_sequence_from([lua.create_function(lua_loader)?])?;
|
||||
package.raw_set("loaders", loaders.clone())?;
|
||||
package.raw_set("loaders", &loaders)?;
|
||||
#[cfg(unix)]
|
||||
{
|
||||
loaders.push(lua.create_function(dylib_loader)?)?;
|
||||
@@ -97,7 +98,8 @@ pub(crate) fn register_package_module(lua: &Lua) -> Result<()> {
|
||||
|
||||
// Register the module and `require` function in globals
|
||||
let globals = lua.globals();
|
||||
globals.raw_set("package", package)?;
|
||||
globals.raw_set("package", &package)?;
|
||||
loaded.raw_set("package", package)?;
|
||||
globals.raw_set("require", unsafe { lua.create_c_function(lua_require)? })?;
|
||||
|
||||
Ok(())
|
||||
@@ -191,17 +193,17 @@ fn package_searchpath(name: &str, search_path: &str, try_prefix: bool) -> Option
|
||||
/// Tries to load a lua (text) file
|
||||
fn lua_loader(lua: &Lua, modname: StdString) -> Result<Value> {
|
||||
let package = {
|
||||
let key = lua.app_data_ref::<PackageKey>().unwrap();
|
||||
lua.registry_value::<Table>(&key.0)
|
||||
let loaded = lua.named_registry_value::<Table>("_LOADED")?;
|
||||
loaded.raw_get::<Table>("package")
|
||||
}?;
|
||||
let search_path = package.get::<_, StdString>("path").unwrap_or_default();
|
||||
let search_path = package.get::<StdString>("path").unwrap_or_default();
|
||||
|
||||
if let Some(file_path) = package_searchpath(&modname, &search_path, false) {
|
||||
match fs::read(&file_path) {
|
||||
Ok(buf) => {
|
||||
return lua
|
||||
.load(&buf)
|
||||
.set_name(&format!("={}", file_path.display()))
|
||||
.set_name(format!("={}", file_path.display()))
|
||||
.set_mode(ChunkMode::Text)
|
||||
.into_function()
|
||||
.map(Value::Function);
|
||||
@@ -219,20 +221,19 @@ fn lua_loader(lua: &Lua, modname: StdString) -> Result<Value> {
|
||||
#[cfg(unix)]
|
||||
fn dylib_loader(lua: &Lua, modname: StdString) -> Result<Value> {
|
||||
let package = {
|
||||
let key = lua.app_data_ref::<PackageKey>().unwrap();
|
||||
lua.registry_value::<Table>(&key.0)
|
||||
let loaded = lua.named_registry_value::<Table>("_LOADED")?;
|
||||
loaded.raw_get::<Table>("package")
|
||||
}?;
|
||||
let search_cpath = package.get::<_, StdString>("cpath").unwrap_or_default();
|
||||
let search_cpath = package.get::<StdString>("cpath").unwrap_or_default();
|
||||
|
||||
let find_symbol = |lib: &Library| unsafe {
|
||||
if let Ok(entry) = lib.get::<ffi::lua_CFunction>(format!("luaopen_{modname}\0").as_bytes())
|
||||
{
|
||||
if let Ok(entry) = lib.get::<ffi::lua_CFunction>(format!("luaopen_{modname}\0").as_bytes()) {
|
||||
return lua.create_c_function(*entry).map(Value::Function);
|
||||
}
|
||||
// Try all in one mode
|
||||
if let Ok(entry) = lib.get::<ffi::lua_CFunction>(
|
||||
format!("luaopen_{}\0", modname.replace('.', "_")).as_bytes(),
|
||||
) {
|
||||
if let Ok(entry) =
|
||||
lib.get::<ffi::lua_CFunction>(format!("luaopen_{}\0", modname.replace('.', "_")).as_bytes())
|
||||
{
|
||||
return lua.create_c_function(*entry).map(Value::Function);
|
||||
}
|
||||
"cannot find module entrypoint".into_lua(lua)
|
||||
|
||||
+1
-2
@@ -10,8 +10,7 @@ macro_rules! bug_msg {
|
||||
|
||||
macro_rules! cstr {
|
||||
($s:expr) => {
|
||||
concat!($s, "\0") as *const str as *const [::std::os::raw::c_char]
|
||||
as *const ::std::os::raw::c_char
|
||||
concat!($s, "\0") as *const str as *const [::std::os::raw::c_char] as *const ::std::os::raw::c_char
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
+60
-93
@@ -1,135 +1,104 @@
|
||||
use std::iter::FromIterator;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use std::os::raw::c_int;
|
||||
use std::result::Result as StdResult;
|
||||
|
||||
use crate::error::Result;
|
||||
use crate::lua::Lua;
|
||||
use crate::state::{Lua, RawLua};
|
||||
use crate::util::check_stack;
|
||||
use crate::value::{FromLua, FromLuaMulti, IntoLua, IntoLuaMulti, MultiValue, Nil};
|
||||
|
||||
/// Result is convertible to `MultiValue` following the common Lua idiom of returning the result
|
||||
/// on success, or in the case of an error, returning `nil` and an error message.
|
||||
impl<'lua, T: IntoLua<'lua>, E: IntoLua<'lua>> IntoLuaMulti<'lua> for StdResult<T, E> {
|
||||
impl<T: IntoLua, E: IntoLua> IntoLuaMulti for StdResult<T, E> {
|
||||
#[inline]
|
||||
fn into_lua_multi(self, lua: &'lua Lua) -> Result<MultiValue<'lua>> {
|
||||
let mut result = MultiValue::with_lua_and_capacity(lua, 2);
|
||||
fn into_lua_multi(self, lua: &Lua) -> Result<MultiValue> {
|
||||
match self {
|
||||
Ok(v) => result.push_front(v.into_lua(lua)?),
|
||||
Err(e) => {
|
||||
result.push_front(e.into_lua(lua)?);
|
||||
result.push_front(Nil);
|
||||
}
|
||||
Ok(val) => (val,).into_lua_multi(lua),
|
||||
Err(err) => (Nil, err).into_lua_multi(lua),
|
||||
}
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn push_into_stack_multi(self, lua: &'lua Lua) -> Result<c_int> {
|
||||
unsafe fn push_into_stack_multi(self, lua: &RawLua) -> Result<c_int> {
|
||||
match self {
|
||||
Ok(v) => v.push_into_stack(lua).map(|_| 1),
|
||||
Err(e) => {
|
||||
let state = lua.state();
|
||||
check_stack(state, 3)?;
|
||||
ffi::lua_pushnil(state);
|
||||
e.push_into_stack(lua)?;
|
||||
Ok(2)
|
||||
}
|
||||
Ok(val) => (val,).push_into_stack_multi(lua),
|
||||
Err(err) => (Nil, err).push_into_stack_multi(lua),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua, E: IntoLua<'lua>> IntoLuaMulti<'lua> for StdResult<(), E> {
|
||||
impl<E: IntoLua> IntoLuaMulti for StdResult<(), E> {
|
||||
#[inline]
|
||||
fn into_lua_multi(self, lua: &'lua Lua) -> Result<MultiValue<'lua>> {
|
||||
fn into_lua_multi(self, lua: &Lua) -> Result<MultiValue> {
|
||||
match self {
|
||||
Ok(_) => return Ok(MultiValue::new()),
|
||||
Err(e) => {
|
||||
let mut result = MultiValue::with_lua_and_capacity(lua, 2);
|
||||
result.push_front(e.into_lua(lua)?);
|
||||
result.push_front(Nil);
|
||||
Ok(result)
|
||||
}
|
||||
Ok(_) => const { Ok(MultiValue::new()) },
|
||||
Err(err) => (Nil, err).into_lua_multi(lua),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn push_into_stack_multi(self, lua: &'lua Lua) -> Result<c_int> {
|
||||
unsafe fn push_into_stack_multi(self, lua: &RawLua) -> Result<c_int> {
|
||||
match self {
|
||||
Ok(_) => Ok(0),
|
||||
Err(e) => {
|
||||
let state = lua.state();
|
||||
check_stack(state, 3)?;
|
||||
ffi::lua_pushnil(state);
|
||||
e.push_into_stack(lua)?;
|
||||
Ok(2)
|
||||
}
|
||||
Err(err) => (Nil, err).push_into_stack_multi(lua),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua, T: IntoLua<'lua>> IntoLuaMulti<'lua> for T {
|
||||
impl<T: IntoLua> IntoLuaMulti for T {
|
||||
#[inline]
|
||||
fn into_lua_multi(self, lua: &'lua Lua) -> Result<MultiValue<'lua>> {
|
||||
let mut v = MultiValue::with_lua_and_capacity(lua, 1);
|
||||
v.push_front(self.into_lua(lua)?);
|
||||
fn into_lua_multi(self, lua: &Lua) -> Result<MultiValue> {
|
||||
let mut v = MultiValue::with_capacity(1);
|
||||
v.push_back(self.into_lua(lua)?);
|
||||
Ok(v)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn push_into_stack_multi(self, lua: &'lua Lua) -> Result<c_int> {
|
||||
unsafe fn push_into_stack_multi(self, lua: &RawLua) -> Result<c_int> {
|
||||
self.push_into_stack(lua)?;
|
||||
Ok(1)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua, T: FromLua<'lua>> FromLuaMulti<'lua> for T {
|
||||
impl<T: FromLua> FromLuaMulti for T {
|
||||
#[inline]
|
||||
fn from_lua_multi(mut values: MultiValue<'lua>, lua: &'lua Lua) -> Result<Self> {
|
||||
fn from_lua_multi(mut values: MultiValue, lua: &Lua) -> Result<Self> {
|
||||
T::from_lua(values.pop_front().unwrap_or(Nil), lua)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn from_lua_args(
|
||||
mut args: MultiValue<'lua>,
|
||||
i: usize,
|
||||
to: Option<&str>,
|
||||
lua: &'lua Lua,
|
||||
) -> Result<Self> {
|
||||
fn from_lua_args(mut args: MultiValue, i: usize, to: Option<&str>, lua: &Lua) -> Result<Self> {
|
||||
T::from_lua_arg(args.pop_front().unwrap_or(Nil), i, to, lua)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn from_stack_multi(nvals: c_int, lua: &'lua Lua) -> Result<Self> {
|
||||
unsafe fn from_stack_multi(nvals: c_int, lua: &RawLua) -> Result<Self> {
|
||||
if nvals == 0 {
|
||||
return T::from_lua(Nil, lua);
|
||||
return T::from_lua(Nil, lua.lua());
|
||||
}
|
||||
T::from_stack(-nvals, lua)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn from_stack_args(
|
||||
nargs: c_int,
|
||||
i: usize,
|
||||
to: Option<&str>,
|
||||
lua: &'lua Lua,
|
||||
) -> Result<Self> {
|
||||
unsafe fn from_stack_args(nargs: c_int, i: usize, to: Option<&str>, lua: &RawLua) -> Result<Self> {
|
||||
if nargs == 0 {
|
||||
return T::from_lua_arg(Nil, i, to, lua);
|
||||
return T::from_lua_arg(Nil, i, to, lua.lua());
|
||||
}
|
||||
T::from_stack_arg(-nargs, i, to, lua)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> IntoLuaMulti<'lua> for MultiValue<'lua> {
|
||||
impl IntoLuaMulti for MultiValue {
|
||||
#[inline]
|
||||
fn into_lua_multi(self, _: &'lua Lua) -> Result<MultiValue<'lua>> {
|
||||
fn into_lua_multi(self, _: &Lua) -> Result<MultiValue> {
|
||||
Ok(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> FromLuaMulti<'lua> for MultiValue<'lua> {
|
||||
impl FromLuaMulti for MultiValue {
|
||||
#[inline]
|
||||
fn from_lua_multi(values: MultiValue<'lua>, _: &'lua Lua) -> Result<Self> {
|
||||
fn from_lua_multi(values: MultiValue, _: &Lua) -> Result<Self> {
|
||||
Ok(values)
|
||||
}
|
||||
}
|
||||
@@ -172,7 +141,7 @@ impl<T> Variadic<T> {
|
||||
|
||||
impl<T> Default for Variadic<T> {
|
||||
fn default() -> Variadic<T> {
|
||||
Variadic::new()
|
||||
const { Variadic::new() }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -205,21 +174,19 @@ impl<T> DerefMut for Variadic<T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua, T: IntoLua<'lua>> IntoLuaMulti<'lua> for Variadic<T> {
|
||||
impl<T: IntoLua> IntoLuaMulti for Variadic<T> {
|
||||
#[inline]
|
||||
fn into_lua_multi(self, lua: &'lua Lua) -> Result<MultiValue<'lua>> {
|
||||
let mut values = MultiValue::with_lua_and_capacity(lua, self.0.len());
|
||||
values.refill(self.0.into_iter().map(|e| e.into_lua(lua)))?;
|
||||
Ok(values)
|
||||
fn into_lua_multi(self, lua: &Lua) -> Result<MultiValue> {
|
||||
MultiValue::from_lua_iter(lua, self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua, T: FromLua<'lua>> FromLuaMulti<'lua> for Variadic<T> {
|
||||
impl<T: FromLua> FromLuaMulti for Variadic<T> {
|
||||
#[inline]
|
||||
fn from_lua_multi(mut values: MultiValue<'lua>, lua: &'lua Lua) -> Result<Self> {
|
||||
fn from_lua_multi(mut values: MultiValue, lua: &Lua) -> Result<Self> {
|
||||
values
|
||||
.drain_all()
|
||||
.map(|e| T::from_lua(e, lua))
|
||||
.drain(..)
|
||||
.map(|val| T::from_lua(val, lua))
|
||||
.collect::<Result<Vec<T>>>()
|
||||
.map(Variadic)
|
||||
}
|
||||
@@ -227,26 +194,26 @@ impl<'lua, T: FromLua<'lua>> FromLuaMulti<'lua> for Variadic<T> {
|
||||
|
||||
macro_rules! impl_tuple {
|
||||
() => (
|
||||
impl<'lua> IntoLuaMulti<'lua> for () {
|
||||
impl IntoLuaMulti for () {
|
||||
#[inline]
|
||||
fn into_lua_multi(self, lua: &'lua Lua) -> Result<MultiValue<'lua>> {
|
||||
Ok(MultiValue::with_lua_and_capacity(lua, 0))
|
||||
fn into_lua_multi(self, _: &Lua) -> Result<MultiValue> {
|
||||
const { Ok(MultiValue::new()) }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn push_into_stack_multi(self, _lua: &'lua Lua) -> Result<c_int> {
|
||||
unsafe fn push_into_stack_multi(self, _lua: &RawLua) -> Result<c_int> {
|
||||
Ok(0)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> FromLuaMulti<'lua> for () {
|
||||
impl FromLuaMulti for () {
|
||||
#[inline]
|
||||
fn from_lua_multi(_values: MultiValue<'lua>, _lua: &'lua Lua) -> Result<Self> {
|
||||
fn from_lua_multi(_values: MultiValue, _lua: &Lua) -> Result<Self> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn from_stack_multi(nvals: c_int, lua: &'lua Lua) -> Result<Self> {
|
||||
unsafe fn from_stack_multi(nvals: c_int, lua: &RawLua) -> Result<Self> {
|
||||
if nvals > 0 {
|
||||
ffi::lua_pop(lua.state(), nvals);
|
||||
}
|
||||
@@ -256,13 +223,13 @@ macro_rules! impl_tuple {
|
||||
);
|
||||
|
||||
($last:ident $($name:ident)*) => (
|
||||
impl<'lua, $($name,)* $last> IntoLuaMulti<'lua> for ($($name,)* $last,)
|
||||
where $($name: IntoLua<'lua>,)*
|
||||
$last: IntoLuaMulti<'lua>
|
||||
impl<$($name,)* $last> IntoLuaMulti for ($($name,)* $last,)
|
||||
where $($name: IntoLua,)*
|
||||
$last: IntoLuaMulti
|
||||
{
|
||||
#[allow(unused_mut, non_snake_case)]
|
||||
#[inline]
|
||||
fn into_lua_multi(self, lua: &'lua Lua) -> Result<MultiValue<'lua>> {
|
||||
fn into_lua_multi(self, lua: &Lua) -> Result<MultiValue> {
|
||||
let ($($name,)* $last,) = self;
|
||||
|
||||
let mut results = $last.into_lua_multi(lua)?;
|
||||
@@ -272,7 +239,7 @@ macro_rules! impl_tuple {
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
#[inline]
|
||||
unsafe fn push_into_stack_multi(self, lua: &'lua Lua) -> Result<c_int> {
|
||||
unsafe fn push_into_stack_multi(self, lua: &RawLua) -> Result<c_int> {
|
||||
let ($($name,)* $last,) = self;
|
||||
let mut nresults = 0;
|
||||
$(
|
||||
@@ -288,13 +255,13 @@ macro_rules! impl_tuple {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua, $($name,)* $last> FromLuaMulti<'lua> for ($($name,)* $last,)
|
||||
where $($name: FromLua<'lua>,)*
|
||||
$last: FromLuaMulti<'lua>
|
||||
impl<$($name,)* $last> FromLuaMulti for ($($name,)* $last,)
|
||||
where $($name: FromLua,)*
|
||||
$last: FromLuaMulti
|
||||
{
|
||||
#[allow(unused_mut, non_snake_case)]
|
||||
#[inline]
|
||||
fn from_lua_multi(mut values: MultiValue<'lua>, lua: &'lua Lua) -> Result<Self> {
|
||||
fn from_lua_multi(mut values: MultiValue, lua: &Lua) -> Result<Self> {
|
||||
$(let $name = FromLua::from_lua(values.pop_front().unwrap_or(Nil), lua)?;)*
|
||||
let $last = FromLuaMulti::from_lua_multi(values, lua)?;
|
||||
Ok(($($name,)* $last,))
|
||||
@@ -302,7 +269,7 @@ macro_rules! impl_tuple {
|
||||
|
||||
#[allow(unused_mut, non_snake_case)]
|
||||
#[inline]
|
||||
fn from_lua_args(mut args: MultiValue<'lua>, mut i: usize, to: Option<&str>, lua: &'lua Lua) -> Result<Self> {
|
||||
fn from_lua_args(mut args: MultiValue, mut i: usize, to: Option<&str>, lua: &Lua) -> Result<Self> {
|
||||
$(
|
||||
let $name = FromLua::from_lua_arg(args.pop_front().unwrap_or(Nil), i, to, lua)?;
|
||||
i += 1;
|
||||
@@ -313,13 +280,13 @@ macro_rules! impl_tuple {
|
||||
|
||||
#[allow(unused_mut, non_snake_case)]
|
||||
#[inline]
|
||||
unsafe fn from_stack_multi(mut nvals: c_int, lua: &'lua Lua) -> Result<Self> {
|
||||
unsafe fn from_stack_multi(mut nvals: c_int, lua: &RawLua) -> Result<Self> {
|
||||
$(
|
||||
let $name = if nvals > 0 {
|
||||
nvals -= 1;
|
||||
FromLua::from_stack(-(nvals + 1), lua)
|
||||
} else {
|
||||
FromLua::from_lua(Nil, lua)
|
||||
FromLua::from_lua(Nil, lua.lua())
|
||||
}?;
|
||||
)*
|
||||
let $last = FromLuaMulti::from_stack_multi(nvals, lua)?;
|
||||
@@ -328,13 +295,13 @@ macro_rules! impl_tuple {
|
||||
|
||||
#[allow(unused_mut, non_snake_case)]
|
||||
#[inline]
|
||||
unsafe fn from_stack_args(mut nargs: c_int, mut i: usize, to: Option<&str>, lua: &'lua Lua) -> Result<Self> {
|
||||
unsafe fn from_stack_args(mut nargs: c_int, mut i: usize, to: Option<&str>, lua: &RawLua) -> Result<Self> {
|
||||
$(
|
||||
let $name = if nargs > 0 {
|
||||
nargs -= 1;
|
||||
FromLua::from_stack_arg(-(nargs + 1), i, to, lua)
|
||||
} else {
|
||||
FromLua::from_lua_arg(Nil, i, to, lua)
|
||||
FromLua::from_lua_arg(Nil, i, to, lua.lua())
|
||||
}?;
|
||||
i += 1;
|
||||
)*
|
||||
|
||||
+12
-22
@@ -2,19 +2,17 @@
|
||||
|
||||
#[doc(no_inline)]
|
||||
pub use crate::{
|
||||
AnyUserData as LuaAnyUserData, AnyUserDataExt as LuaAnyUserDataExt, Chunk as LuaChunk,
|
||||
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, LuaOptions, MetaMethod as LuaMetaMethod,
|
||||
MultiValue as LuaMultiValue, Nil as LuaNil, Number as LuaNumber, RegistryKey as LuaRegistryKey,
|
||||
Result as LuaResult, StdLib as LuaStdLib, String as LuaString, Table as LuaTable,
|
||||
TableExt as LuaTableExt, 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,
|
||||
AnyUserData as LuaAnyUserData, Chunk as LuaChunk, 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, 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,
|
||||
};
|
||||
|
||||
#[cfg(not(feature = "luau"))]
|
||||
@@ -32,13 +30,5 @@ pub use crate::AsyncThread as LuaAsyncThread;
|
||||
#[cfg(feature = "serialize")]
|
||||
#[doc(no_inline)]
|
||||
pub use crate::{
|
||||
DeserializeOptions as LuaDeserializeOptions, LuaSerdeExt,
|
||||
SerializeOptions as LuaSerializeOptions,
|
||||
};
|
||||
|
||||
#[cfg(feature = "unstable")]
|
||||
#[doc(no_inline)]
|
||||
pub use crate::{
|
||||
OwnedAnyUserData as LuaOwnedAnyUserData, OwnedFunction as LuaOwnedFunction,
|
||||
OwnedString as LuaOwnedString, OwnedTable as LuaOwnedTable, OwnedThread as LuaOwnedThread,
|
||||
DeserializeOptions as LuaDeserializeOptions, LuaSerdeExt, SerializeOptions as LuaSerializeOptions,
|
||||
};
|
||||
|
||||
+24
-24
@@ -10,7 +10,7 @@ use serde::Serialize;
|
||||
use crate::error::{Error, Result};
|
||||
use crate::function::Function;
|
||||
use crate::lua::Lua;
|
||||
use crate::types::{Callback, CallbackUpvalue, LuaRef, MaybeSend, SubtypeId};
|
||||
use crate::types::{Callback, CallbackUpvalue, MaybeSend, SubtypeId, ValueRef};
|
||||
use crate::userdata::{
|
||||
AnyUserData, MetaMethod, UserData, UserDataCell, UserDataFields, UserDataMethods,
|
||||
};
|
||||
@@ -38,11 +38,11 @@ where
|
||||
'lua: 'scope,
|
||||
{
|
||||
lua: &'lua Lua,
|
||||
destructors: RefCell<Vec<(LuaRef<'lua>, DestructorCallback<'lua>)>>,
|
||||
destructors: RefCell<Vec<(ValueRef<'lua>, DestructorCallback<'lua>)>>,
|
||||
_scope_invariant: PhantomData<Cell<&'scope ()>>,
|
||||
}
|
||||
|
||||
type DestructorCallback<'lua> = Box<dyn Fn(LuaRef<'lua>) -> Vec<Box<dyn Any>> + 'lua>;
|
||||
type DestructorCallback<'lua> = Box<dyn Fn(ValueRef<'lua>) -> Vec<Box<dyn Any>> + 'lua>;
|
||||
|
||||
impl<'lua, 'scope> Scope<'lua, 'scope> {
|
||||
pub(crate) fn new(lua: &'lua Lua) -> Scope<'lua, 'scope> {
|
||||
@@ -63,7 +63,7 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
|
||||
pub fn create_function<'callback, A, R, F>(&'callback self, func: F) -> Result<Function<'lua>>
|
||||
where
|
||||
A: FromLuaMulti<'callback>,
|
||||
R: IntoLuaMulti<'callback>,
|
||||
R: IntoLuaMulti,
|
||||
F: Fn(&'callback Lua, A) -> Result<R> + 'scope,
|
||||
{
|
||||
// Safe, because 'scope must outlive 'callback (due to Self containing 'scope), however the
|
||||
@@ -97,7 +97,7 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
|
||||
) -> Result<Function<'lua>>
|
||||
where
|
||||
A: FromLuaMulti<'callback>,
|
||||
R: IntoLuaMulti<'callback>,
|
||||
R: IntoLuaMulti,
|
||||
F: FnMut(&'callback Lua, A) -> Result<R> + 'scope,
|
||||
{
|
||||
let func = RefCell::new(func);
|
||||
@@ -656,7 +656,7 @@ impl<'lua, T> NonStaticUserDataRegistry<'lua, T> {
|
||||
impl<'lua, T> UserDataFields<'lua, T> for NonStaticUserDataRegistry<'lua, T> {
|
||||
fn add_field<V>(&mut self, name: impl AsRef<str>, value: V)
|
||||
where
|
||||
V: IntoLua<'lua> + Clone + 'static,
|
||||
V: IntoLua + Clone + 'static,
|
||||
{
|
||||
let name = name.as_ref().to_string();
|
||||
self.fields.push((
|
||||
@@ -670,7 +670,7 @@ impl<'lua, T> UserDataFields<'lua, T> for NonStaticUserDataRegistry<'lua, T> {
|
||||
fn add_field_method_get<M, R>(&mut self, name: impl AsRef<str>, method: M)
|
||||
where
|
||||
M: Fn(&'lua Lua, &T) -> Result<R> + MaybeSend + 'static,
|
||||
R: IntoLua<'lua>,
|
||||
R: IntoLua,
|
||||
{
|
||||
let method = NonStaticMethod::Method(Box::new(move |lua, ud, _| unsafe {
|
||||
method(lua, ud)?.push_into_stack_multi(lua)
|
||||
@@ -694,7 +694,7 @@ impl<'lua, T> UserDataFields<'lua, T> for NonStaticUserDataRegistry<'lua, T> {
|
||||
fn add_field_function_get<F, R>(&mut self, name: impl AsRef<str>, function: F)
|
||||
where
|
||||
F: Fn(&'lua Lua, AnyUserData<'lua>) -> Result<R> + MaybeSend + 'static,
|
||||
R: IntoLua<'lua>,
|
||||
R: IntoLua,
|
||||
{
|
||||
let func_name = format!("{}.{}", short_type_name::<T>(), name.as_ref());
|
||||
let func = NonStaticMethod::Function(Box::new(move |lua, nargs| unsafe {
|
||||
@@ -719,7 +719,7 @@ impl<'lua, T> UserDataFields<'lua, T> for NonStaticUserDataRegistry<'lua, T> {
|
||||
|
||||
fn add_meta_field<V>(&mut self, name: impl AsRef<str>, value: V)
|
||||
where
|
||||
V: IntoLua<'lua> + Clone + 'static,
|
||||
V: IntoLua + Clone + 'static,
|
||||
{
|
||||
let name = name.as_ref().to_string();
|
||||
let name2 = name.clone();
|
||||
@@ -735,7 +735,7 @@ impl<'lua, T> UserDataFields<'lua, T> for NonStaticUserDataRegistry<'lua, T> {
|
||||
fn add_meta_field_with<F, R>(&mut self, name: impl AsRef<str>, f: F)
|
||||
where
|
||||
F: Fn(&'lua Lua) -> Result<R> + MaybeSend + 'static,
|
||||
R: IntoLua<'lua>,
|
||||
R: IntoLua,
|
||||
{
|
||||
let name = name.as_ref().to_string();
|
||||
let name2 = name.clone();
|
||||
@@ -754,7 +754,7 @@ impl<'lua, T> UserDataMethods<'lua, T> for NonStaticUserDataRegistry<'lua, T> {
|
||||
where
|
||||
M: Fn(&'lua Lua, &T, A) -> Result<R> + MaybeSend + 'static,
|
||||
A: FromLuaMulti<'lua>,
|
||||
R: IntoLuaMulti<'lua>,
|
||||
R: IntoLuaMulti,
|
||||
{
|
||||
let func_name = format!("{}.{}", short_type_name::<T>(), name.as_ref());
|
||||
let method = NonStaticMethod::Method(Box::new(move |lua, ud, nargs| unsafe {
|
||||
@@ -768,7 +768,7 @@ impl<'lua, T> UserDataMethods<'lua, T> for NonStaticUserDataRegistry<'lua, T> {
|
||||
where
|
||||
M: FnMut(&'lua Lua, &mut T, A) -> Result<R> + MaybeSend + 'static,
|
||||
A: FromLuaMulti<'lua>,
|
||||
R: IntoLuaMulti<'lua>,
|
||||
R: IntoLuaMulti,
|
||||
{
|
||||
let func_name = format!("{}.{}", short_type_name::<T>(), name.as_ref());
|
||||
let method = NonStaticMethod::MethodMut(Box::new(move |lua, ud, nargs| unsafe {
|
||||
@@ -786,7 +786,7 @@ impl<'lua, T> UserDataMethods<'lua, T> for NonStaticUserDataRegistry<'lua, T> {
|
||||
M: Fn(&'lua Lua, &'s T, A) -> MR + MaybeSend + 'static,
|
||||
A: FromLuaMulti<'lua>,
|
||||
MR: Future<Output = Result<R>> + 's,
|
||||
R: IntoLuaMulti<'lua>,
|
||||
R: IntoLuaMulti,
|
||||
{
|
||||
// The panic should never happen as async non-static code wouldn't compile
|
||||
// Non-static lifetime must be bounded to 'lua lifetime
|
||||
@@ -801,7 +801,7 @@ impl<'lua, T> UserDataMethods<'lua, T> for NonStaticUserDataRegistry<'lua, T> {
|
||||
M: Fn(&'lua Lua, &'s mut T, A) -> MR + MaybeSend + 'static,
|
||||
A: FromLuaMulti<'lua>,
|
||||
MR: Future<Output = Result<R>> + 's,
|
||||
R: IntoLuaMulti<'lua>,
|
||||
R: IntoLuaMulti,
|
||||
{
|
||||
// The panic should never happen as async non-static code wouldn't compile
|
||||
// Non-static lifetime must be bounded to 'lua lifetime
|
||||
@@ -812,7 +812,7 @@ impl<'lua, T> UserDataMethods<'lua, T> for NonStaticUserDataRegistry<'lua, T> {
|
||||
where
|
||||
F: Fn(&'lua Lua, A) -> Result<R> + MaybeSend + 'static,
|
||||
A: FromLuaMulti<'lua>,
|
||||
R: IntoLuaMulti<'lua>,
|
||||
R: IntoLuaMulti,
|
||||
{
|
||||
let func_name = format!("{}.{}", short_type_name::<T>(), name.as_ref());
|
||||
let func = NonStaticMethod::Function(Box::new(move |lua, nargs| unsafe {
|
||||
@@ -826,7 +826,7 @@ impl<'lua, T> UserDataMethods<'lua, T> for NonStaticUserDataRegistry<'lua, T> {
|
||||
where
|
||||
F: FnMut(&'lua Lua, A) -> Result<R> + MaybeSend + 'static,
|
||||
A: FromLuaMulti<'lua>,
|
||||
R: IntoLuaMulti<'lua>,
|
||||
R: IntoLuaMulti,
|
||||
{
|
||||
let func_name = format!("{}.{}", short_type_name::<T>(), name.as_ref());
|
||||
let func = NonStaticMethod::FunctionMut(Box::new(move |lua, nargs| unsafe {
|
||||
@@ -842,7 +842,7 @@ impl<'lua, T> UserDataMethods<'lua, T> for NonStaticUserDataRegistry<'lua, T> {
|
||||
F: Fn(&'lua Lua, A) -> FR + MaybeSend + 'static,
|
||||
A: FromLuaMulti<'lua>,
|
||||
FR: Future<Output = Result<R>> + 'lua,
|
||||
R: IntoLuaMulti<'lua>,
|
||||
R: IntoLuaMulti,
|
||||
{
|
||||
// The panic should never happen as async non-static code wouldn't compile
|
||||
// Non-static lifetime must be bounded to 'lua lifetime
|
||||
@@ -853,7 +853,7 @@ impl<'lua, T> UserDataMethods<'lua, T> for NonStaticUserDataRegistry<'lua, T> {
|
||||
where
|
||||
M: Fn(&'lua Lua, &T, A) -> Result<R> + MaybeSend + 'static,
|
||||
A: FromLuaMulti<'lua>,
|
||||
R: IntoLuaMulti<'lua>,
|
||||
R: IntoLuaMulti,
|
||||
{
|
||||
let func_name = format!("{}.{}", short_type_name::<T>(), name.as_ref());
|
||||
let method = NonStaticMethod::Method(Box::new(move |lua, ud, nargs| unsafe {
|
||||
@@ -867,7 +867,7 @@ impl<'lua, T> UserDataMethods<'lua, T> for NonStaticUserDataRegistry<'lua, T> {
|
||||
where
|
||||
M: FnMut(&'lua Lua, &mut T, A) -> Result<R> + MaybeSend + 'static,
|
||||
A: FromLuaMulti<'lua>,
|
||||
R: IntoLuaMulti<'lua>,
|
||||
R: IntoLuaMulti,
|
||||
{
|
||||
let func_name = format!("{}.{}", short_type_name::<T>(), name.as_ref());
|
||||
let method = NonStaticMethod::MethodMut(Box::new(move |lua, ud, nargs| unsafe {
|
||||
@@ -885,7 +885,7 @@ impl<'lua, T> UserDataMethods<'lua, T> for NonStaticUserDataRegistry<'lua, T> {
|
||||
M: Fn(&'lua Lua, &'s T, A) -> MR + MaybeSend + 'static,
|
||||
A: FromLuaMulti<'lua>,
|
||||
MR: Future<Output = Result<R>> + 's,
|
||||
R: IntoLuaMulti<'lua>,
|
||||
R: IntoLuaMulti,
|
||||
{
|
||||
// The panic should never happen as async non-static code wouldn't compile
|
||||
// Non-static lifetime must be bounded to 'lua lifetime
|
||||
@@ -900,7 +900,7 @@ impl<'lua, T> UserDataMethods<'lua, T> for NonStaticUserDataRegistry<'lua, T> {
|
||||
M: Fn(&'lua Lua, &'s mut T, A) -> MR + MaybeSend + 'static,
|
||||
A: FromLuaMulti<'lua>,
|
||||
MR: Future<Output = Result<R>> + 's,
|
||||
R: IntoLuaMulti<'lua>,
|
||||
R: IntoLuaMulti,
|
||||
{
|
||||
// The panic should never happen as async non-static code wouldn't compile
|
||||
// Non-static lifetime must be bounded to 'lua lifetime
|
||||
@@ -911,7 +911,7 @@ impl<'lua, T> UserDataMethods<'lua, T> for NonStaticUserDataRegistry<'lua, T> {
|
||||
where
|
||||
F: Fn(&'lua Lua, A) -> Result<R> + MaybeSend + 'static,
|
||||
A: FromLuaMulti<'lua>,
|
||||
R: IntoLuaMulti<'lua>,
|
||||
R: IntoLuaMulti,
|
||||
{
|
||||
let func_name = format!("{}.{}", short_type_name::<T>(), name.as_ref());
|
||||
let func = NonStaticMethod::Function(Box::new(move |lua, nargs| unsafe {
|
||||
@@ -925,7 +925,7 @@ impl<'lua, T> UserDataMethods<'lua, T> for NonStaticUserDataRegistry<'lua, T> {
|
||||
where
|
||||
F: FnMut(&'lua Lua, A) -> Result<R> + MaybeSend + 'static,
|
||||
A: FromLuaMulti<'lua>,
|
||||
R: IntoLuaMulti<'lua>,
|
||||
R: IntoLuaMulti,
|
||||
{
|
||||
let func_name = format!("{}.{}", short_type_name::<T>(), name.as_ref());
|
||||
let func = NonStaticMethod::FunctionMut(Box::new(move |lua, nargs| unsafe {
|
||||
@@ -941,7 +941,7 @@ impl<'lua, T> UserDataMethods<'lua, T> for NonStaticUserDataRegistry<'lua, T> {
|
||||
F: Fn(&'lua Lua, A) -> FR + MaybeSend + 'static,
|
||||
A: FromLuaMulti<'lua>,
|
||||
FR: Future<Output = Result<R>> + 'lua,
|
||||
R: IntoLuaMulti<'lua>,
|
||||
R: IntoLuaMulti,
|
||||
{
|
||||
// The panic should never happen as async non-static code wouldn't compile
|
||||
// Non-static lifetime must be bounded to 'lua lifetime
|
||||
|
||||
+75
-67
@@ -14,8 +14,8 @@ use crate::value::Value;
|
||||
|
||||
/// A struct for deserializing Lua values into Rust values.
|
||||
#[derive(Debug)]
|
||||
pub struct Deserializer<'lua> {
|
||||
value: Value<'lua>,
|
||||
pub struct Deserializer {
|
||||
value: Value,
|
||||
options: Options,
|
||||
visited: Rc<RefCell<FxHashSet<*const c_void>>>,
|
||||
}
|
||||
@@ -51,7 +51,7 @@ pub struct Options {
|
||||
|
||||
impl Default for Options {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
const { Self::new() }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -93,14 +93,14 @@ impl Options {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> Deserializer<'lua> {
|
||||
impl Deserializer {
|
||||
/// Creates a new Lua Deserializer for the `Value`.
|
||||
pub fn new(value: Value<'lua>) -> Self {
|
||||
pub fn new(value: Value) -> Self {
|
||||
Self::new_with_options(value, Options::default())
|
||||
}
|
||||
|
||||
/// Creates a new Lua Deserializer for the `Value` with custom options.
|
||||
pub fn new_with_options(value: Value<'lua>, options: Options) -> Self {
|
||||
pub fn new_with_options(value: Value, options: Options) -> Self {
|
||||
Deserializer {
|
||||
value,
|
||||
options,
|
||||
@@ -108,11 +108,7 @@ impl<'lua> Deserializer<'lua> {
|
||||
}
|
||||
}
|
||||
|
||||
fn from_parts(
|
||||
value: Value<'lua>,
|
||||
options: Options,
|
||||
visited: Rc<RefCell<FxHashSet<*const c_void>>>,
|
||||
) -> Self {
|
||||
fn from_parts(value: Value, options: Options, visited: Rc<RefCell<FxHashSet<*const c_void>>>) -> Self {
|
||||
Deserializer {
|
||||
value,
|
||||
options,
|
||||
@@ -121,7 +117,7 @@ impl<'lua> Deserializer<'lua> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua, 'de> serde::Deserializer<'de> for Deserializer<'lua> {
|
||||
impl<'de> serde::Deserializer<'de> for Deserializer {
|
||||
type Error = Error;
|
||||
|
||||
#[inline]
|
||||
@@ -139,8 +135,8 @@ impl<'lua, 'de> serde::Deserializer<'de> for Deserializer<'lua> {
|
||||
#[cfg(feature = "luau")]
|
||||
Value::Vector(_) => self.deserialize_seq(visitor),
|
||||
Value::String(s) => match s.to_str() {
|
||||
Ok(s) => visitor.visit_str(s),
|
||||
Err(_) => visitor.visit_bytes(s.as_bytes()),
|
||||
Ok(s) => visitor.visit_str(&s),
|
||||
Err(_) => visitor.visit_bytes(&s.as_bytes()),
|
||||
},
|
||||
Value::Table(ref t) if t.raw_len() > 0 || t.is_array() => self.deserialize_seq(visitor),
|
||||
Value::Table(_) => self.deserialize_map(visitor),
|
||||
@@ -150,8 +146,9 @@ impl<'lua, 'de> serde::Deserializer<'de> for Deserializer<'lua> {
|
||||
}
|
||||
#[cfg(feature = "luau")]
|
||||
Value::UserData(ud) if ud.1 == crate::types::SubtypeId::Buffer => unsafe {
|
||||
let lua = ud.0.lua.lock();
|
||||
let mut size = 0usize;
|
||||
let buf = ffi::lua_tobuffer(ud.0.lua.ref_thread(), ud.0.index, &mut size);
|
||||
let buf = ffi::lua_tobuffer(lua.ref_thread(), ud.0.index, &mut size);
|
||||
mlua_assert!(!buf.is_null(), "invalid Luau buffer");
|
||||
let buf = std::slice::from_raw_parts(buf as *const u8, size);
|
||||
visitor.visit_bytes(buf)
|
||||
@@ -266,10 +263,7 @@ impl<'lua, 'de> serde::Deserializer<'de> for Deserializer<'lua> {
|
||||
if deserializer.seq.count() == 0 {
|
||||
Ok(seq)
|
||||
} else {
|
||||
Err(de::Error::invalid_length(
|
||||
len,
|
||||
&"fewer elements in the table",
|
||||
))
|
||||
Err(de::Error::invalid_length(len, &"fewer elements in the table"))
|
||||
}
|
||||
}
|
||||
Value::UserData(ud) if ud.is_serializable() => {
|
||||
@@ -291,12 +285,7 @@ impl<'lua, 'de> serde::Deserializer<'de> for Deserializer<'lua> {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn deserialize_tuple_struct<V>(
|
||||
self,
|
||||
_name: &'static str,
|
||||
_len: usize,
|
||||
visitor: V,
|
||||
) -> Result<V::Value>
|
||||
fn deserialize_tuple_struct<V>(self, _name: &'static str, _len: usize, visitor: V) -> Result<V::Value>
|
||||
where
|
||||
V: de::Visitor<'de>,
|
||||
{
|
||||
@@ -313,7 +302,7 @@ impl<'lua, 'de> serde::Deserializer<'de> for Deserializer<'lua> {
|
||||
let _guard = RecursionGuard::new(&t, &self.visited);
|
||||
|
||||
let mut deserializer = MapDeserializer {
|
||||
pairs: MapPairs::new(t, self.options.sort_keys)?,
|
||||
pairs: MapPairs::new(&t, self.options.sort_keys)?,
|
||||
value: None,
|
||||
options: self.options,
|
||||
visited: self.visited,
|
||||
@@ -394,13 +383,13 @@ impl<'lua, 'de> serde::Deserializer<'de> for Deserializer<'lua> {
|
||||
}
|
||||
}
|
||||
|
||||
struct SeqDeserializer<'lua> {
|
||||
seq: TableSequence<'lua, Value<'lua>>,
|
||||
struct SeqDeserializer<'a> {
|
||||
seq: TableSequence<'a, Value>,
|
||||
options: Options,
|
||||
visited: Rc<RefCell<FxHashSet<*const c_void>>>,
|
||||
}
|
||||
|
||||
impl<'lua, 'de> de::SeqAccess<'de> for SeqDeserializer<'lua> {
|
||||
impl<'de> de::SeqAccess<'de> for SeqDeserializer<'_> {
|
||||
type Error = Error;
|
||||
|
||||
fn next_element_seed<T>(&mut self, seed: T) -> Result<Option<T::Value>>
|
||||
@@ -453,8 +442,7 @@ impl<'de> de::SeqAccess<'de> for VecDeserializer {
|
||||
Some(&n) => {
|
||||
self.next += 1;
|
||||
let visited = Rc::clone(&self.visited);
|
||||
let deserializer =
|
||||
Deserializer::from_parts(Value::Number(n as _), self.options, visited);
|
||||
let deserializer = Deserializer::from_parts(Value::Number(n as _), self.options, visited);
|
||||
seed.deserialize(deserializer).map(Some)
|
||||
}
|
||||
None => Ok(None),
|
||||
@@ -466,13 +454,13 @@ impl<'de> de::SeqAccess<'de> for VecDeserializer {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) enum MapPairs<'lua> {
|
||||
Iter(TablePairs<'lua, Value<'lua>, Value<'lua>>),
|
||||
Vec(Vec<(Value<'lua>, Value<'lua>)>),
|
||||
pub(crate) enum MapPairs<'a> {
|
||||
Iter(TablePairs<'a, Value, Value>),
|
||||
Vec(Vec<(Value, Value)>),
|
||||
}
|
||||
|
||||
impl<'lua> MapPairs<'lua> {
|
||||
pub(crate) fn new(t: Table<'lua>, sort_keys: bool) -> Result<Self> {
|
||||
impl<'a> MapPairs<'a> {
|
||||
pub(crate) fn new(t: &'a Table, sort_keys: bool) -> Result<Self> {
|
||||
if sort_keys {
|
||||
let mut pairs = t.pairs::<Value, Value>().collect::<Result<Vec<_>>>()?;
|
||||
pairs.sort_by(|(a, _), (b, _)| b.cmp(a)); // reverse order as we pop values from the end
|
||||
@@ -497,8 +485,8 @@ impl<'lua> MapPairs<'lua> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> Iterator for MapPairs<'lua> {
|
||||
type Item = Result<(Value<'lua>, Value<'lua>)>;
|
||||
impl Iterator for MapPairs<'_> {
|
||||
type Item = Result<(Value, Value)>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
match self {
|
||||
@@ -508,21 +496,16 @@ impl<'lua> Iterator for MapPairs<'lua> {
|
||||
}
|
||||
}
|
||||
|
||||
struct MapDeserializer<'lua> {
|
||||
pairs: MapPairs<'lua>,
|
||||
value: Option<Value<'lua>>,
|
||||
struct MapDeserializer<'a> {
|
||||
pairs: MapPairs<'a>,
|
||||
value: Option<Value>,
|
||||
options: Options,
|
||||
visited: Rc<RefCell<FxHashSet<*const c_void>>>,
|
||||
processed: usize,
|
||||
}
|
||||
|
||||
impl<'lua, 'de> de::MapAccess<'de> for MapDeserializer<'lua> {
|
||||
type Error = Error;
|
||||
|
||||
fn next_key_seed<T>(&mut self, seed: T) -> Result<Option<T::Value>>
|
||||
where
|
||||
T: de::DeserializeSeed<'de>,
|
||||
{
|
||||
impl<'a> MapDeserializer<'a> {
|
||||
fn next_key_deserializer(&mut self) -> Result<Option<Deserializer>> {
|
||||
loop {
|
||||
match self.pairs.next() {
|
||||
Some(item) => {
|
||||
@@ -538,23 +521,45 @@ impl<'lua, 'de> de::MapAccess<'de> for MapDeserializer<'lua> {
|
||||
self.value = Some(value);
|
||||
let visited = Rc::clone(&self.visited);
|
||||
let key_de = Deserializer::from_parts(key, self.options, visited);
|
||||
return seed.deserialize(key_de).map(Some);
|
||||
return Ok(Some(key_de));
|
||||
}
|
||||
None => return Ok(None),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn next_value_deserializer(&mut self) -> Result<Deserializer> {
|
||||
match self.value.take() {
|
||||
Some(value) => {
|
||||
let visited = Rc::clone(&self.visited);
|
||||
Ok(Deserializer::from_parts(value, self.options, visited))
|
||||
}
|
||||
None => Err(de::Error::custom("value is missing")),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> de::MapAccess<'de> for MapDeserializer<'_> {
|
||||
type Error = Error;
|
||||
|
||||
fn next_key_seed<T>(&mut self, seed: T) -> Result<Option<T::Value>>
|
||||
where
|
||||
T: de::DeserializeSeed<'de>,
|
||||
{
|
||||
match self.next_key_deserializer() {
|
||||
Ok(Some(key_de)) => seed.deserialize(key_de).map(Some),
|
||||
Ok(None) => Ok(None),
|
||||
Err(error) => Err(error),
|
||||
}
|
||||
}
|
||||
|
||||
fn next_value_seed<T>(&mut self, seed: T) -> Result<T::Value>
|
||||
where
|
||||
T: de::DeserializeSeed<'de>,
|
||||
{
|
||||
match self.value.take() {
|
||||
Some(value) => {
|
||||
let visited = Rc::clone(&self.visited);
|
||||
seed.deserialize(Deserializer::from_parts(value, self.options, visited))
|
||||
}
|
||||
None => Err(de::Error::custom("value is missing")),
|
||||
match self.next_value_deserializer() {
|
||||
Ok(value_de) => seed.deserialize(value_de),
|
||||
Err(error) => Err(error),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -566,16 +571,16 @@ impl<'lua, 'de> de::MapAccess<'de> for MapDeserializer<'lua> {
|
||||
}
|
||||
}
|
||||
|
||||
struct EnumDeserializer<'lua> {
|
||||
struct EnumDeserializer {
|
||||
variant: StdString,
|
||||
value: Option<Value<'lua>>,
|
||||
value: Option<Value>,
|
||||
options: Options,
|
||||
visited: Rc<RefCell<FxHashSet<*const c_void>>>,
|
||||
}
|
||||
|
||||
impl<'lua, 'de> de::EnumAccess<'de> for EnumDeserializer<'lua> {
|
||||
impl<'de> de::EnumAccess<'de> for EnumDeserializer {
|
||||
type Error = Error;
|
||||
type Variant = VariantDeserializer<'lua>;
|
||||
type Variant = VariantDeserializer;
|
||||
|
||||
fn variant_seed<T>(self, seed: T) -> Result<(T::Value, Self::Variant)>
|
||||
where
|
||||
@@ -591,13 +596,13 @@ impl<'lua, 'de> de::EnumAccess<'de> for EnumDeserializer<'lua> {
|
||||
}
|
||||
}
|
||||
|
||||
struct VariantDeserializer<'lua> {
|
||||
value: Option<Value<'lua>>,
|
||||
struct VariantDeserializer {
|
||||
value: Option<Value>,
|
||||
options: Options,
|
||||
visited: Rc<RefCell<FxHashSet<*const c_void>>>,
|
||||
}
|
||||
|
||||
impl<'lua, 'de> de::VariantAccess<'de> for VariantDeserializer<'lua> {
|
||||
impl<'de> de::VariantAccess<'de> for VariantDeserializer {
|
||||
type Error = Error;
|
||||
|
||||
fn unit_variant(self) -> Result<()> {
|
||||
@@ -615,9 +620,7 @@ impl<'lua, 'de> de::VariantAccess<'de> for VariantDeserializer<'lua> {
|
||||
T: de::DeserializeSeed<'de>,
|
||||
{
|
||||
match self.value {
|
||||
Some(value) => {
|
||||
seed.deserialize(Deserializer::from_parts(value, self.options, self.visited))
|
||||
}
|
||||
Some(value) => seed.deserialize(Deserializer::from_parts(value, self.options, self.visited)),
|
||||
None => Err(de::Error::invalid_type(
|
||||
de::Unexpected::UnitVariant,
|
||||
&"newtype variant",
|
||||
@@ -716,6 +719,11 @@ fn serde_userdata<V>(
|
||||
ud: AnyUserData,
|
||||
f: impl FnOnce(serde_value::Value) -> std::result::Result<V, serde_value::DeserializerError>,
|
||||
) -> Result<V> {
|
||||
let value = serde_value::to_value(ud).map_err(|err| Error::SerializeError(err.to_string()))?;
|
||||
f(value).map_err(|err| Error::DeserializeError(err.to_string()))
|
||||
match serde_value::to_value(ud) {
|
||||
Ok(value) => match f(value) {
|
||||
Ok(r) => Ok(r),
|
||||
Err(error) => Err(Error::DeserializeError(error.to_string())),
|
||||
},
|
||||
Err(error) => Err(Error::SerializeError(error.to_string())),
|
||||
}
|
||||
}
|
||||
|
||||
+11
-10
@@ -2,11 +2,12 @@
|
||||
|
||||
use std::os::raw::c_void;
|
||||
|
||||
use serde::{de::DeserializeOwned, ser::Serialize};
|
||||
use serde::de::DeserializeOwned;
|
||||
use serde::ser::Serialize;
|
||||
|
||||
use crate::error::Result;
|
||||
use crate::lua::Lua;
|
||||
use crate::private::Sealed;
|
||||
use crate::state::Lua;
|
||||
use crate::table::Table;
|
||||
use crate::util::check_stack;
|
||||
use crate::value::Value;
|
||||
@@ -99,7 +100,7 @@ pub trait LuaSerdeExt: Sealed {
|
||||
/// "#).exec()
|
||||
/// }
|
||||
/// ```
|
||||
fn to_value<'lua, T: Serialize + ?Sized>(&'lua self, t: &T) -> Result<Value<'lua>>;
|
||||
fn to_value<T: Serialize + ?Sized>(&self, t: &T) -> Result<Value>;
|
||||
|
||||
/// Converts `T` into a [`Value`] instance with options.
|
||||
///
|
||||
@@ -124,7 +125,7 @@ pub trait LuaSerdeExt: Sealed {
|
||||
/// "#).exec()
|
||||
/// }
|
||||
/// ```
|
||||
fn to_value_with<'lua, T>(&'lua self, t: &T, options: ser::Options) -> Result<Value<'lua>>
|
||||
fn to_value_with<T>(&self, t: &T, options: ser::Options) -> Result<Value>
|
||||
where
|
||||
T: Serialize + ?Sized;
|
||||
|
||||
@@ -189,8 +190,7 @@ pub trait LuaSerdeExt: Sealed {
|
||||
/// }
|
||||
/// ```
|
||||
#[allow(clippy::wrong_self_convention)]
|
||||
fn from_value_with<T: DeserializeOwned>(&self, value: Value, options: de::Options)
|
||||
-> Result<T>;
|
||||
fn from_value_with<T: DeserializeOwned>(&self, value: Value, options: de::Options) -> Result<T>;
|
||||
}
|
||||
|
||||
impl LuaSerdeExt for Lua {
|
||||
@@ -199,20 +199,21 @@ impl LuaSerdeExt for Lua {
|
||||
}
|
||||
|
||||
fn array_metatable(&self) -> Table {
|
||||
let lua = self.lock();
|
||||
unsafe {
|
||||
push_array_metatable(self.ref_thread());
|
||||
Table(self.pop_ref_thread())
|
||||
push_array_metatable(lua.ref_thread());
|
||||
Table(lua.pop_ref_thread())
|
||||
}
|
||||
}
|
||||
|
||||
fn to_value<'lua, T>(&'lua self, t: &T) -> Result<Value<'lua>>
|
||||
fn to_value<T>(&self, t: &T) -> Result<Value>
|
||||
where
|
||||
T: Serialize + ?Sized,
|
||||
{
|
||||
t.serialize(ser::Serializer::new(self))
|
||||
}
|
||||
|
||||
fn to_value_with<'lua, T>(&'lua self, t: &T, options: ser::Options) -> Result<Value<'lua>>
|
||||
fn to_value_with<T>(&self, t: &T, options: ser::Options) -> Result<Value>
|
||||
where
|
||||
T: Serialize + ?Sized,
|
||||
{
|
||||
|
||||
+83
-90
@@ -2,14 +2,14 @@ use serde::{ser, Serialize};
|
||||
|
||||
use super::LuaSerdeExt;
|
||||
use crate::error::{Error, Result};
|
||||
use crate::lua::Lua;
|
||||
use crate::state::Lua;
|
||||
use crate::table::Table;
|
||||
use crate::value::{IntoLua, Value};
|
||||
|
||||
/// A struct for serializing Rust values into Lua values.
|
||||
#[derive(Debug)]
|
||||
pub struct Serializer<'lua> {
|
||||
lua: &'lua Lua,
|
||||
pub struct Serializer<'a> {
|
||||
lua: &'a Lua,
|
||||
options: Options,
|
||||
}
|
||||
|
||||
@@ -52,7 +52,7 @@ pub struct Options {
|
||||
|
||||
impl Default for Options {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
const { Self::new() }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -96,8 +96,8 @@ impl Options {
|
||||
|
||||
/// Sets [`detect_serde_json_arbitrary_precision`] option.
|
||||
///
|
||||
/// This option is used to serialize `serde_json::Number` with arbitrary precision to a Lua number.
|
||||
/// Otherwise it will be serialized as an object (what serde does).
|
||||
/// This option is used to serialize `serde_json::Number` with arbitrary precision to a Lua
|
||||
/// number. Otherwise it will be serialized as an object (what serde does).
|
||||
///
|
||||
/// This option is disabled by default.
|
||||
///
|
||||
@@ -109,14 +109,14 @@ impl Options {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> Serializer<'lua> {
|
||||
impl<'a> Serializer<'a> {
|
||||
/// Creates a new Lua Serializer with default options.
|
||||
pub fn new(lua: &'lua Lua) -> Self {
|
||||
pub fn new(lua: &'a Lua) -> Self {
|
||||
Self::new_with_options(lua, Options::default())
|
||||
}
|
||||
|
||||
/// Creates a new Lua Serializer with custom options.
|
||||
pub fn new_with_options(lua: &'lua Lua, options: Options) -> Self {
|
||||
pub fn new_with_options(lua: &'a Lua, options: Options) -> Self {
|
||||
Serializer { lua, options }
|
||||
}
|
||||
}
|
||||
@@ -124,28 +124,28 @@ impl<'lua> Serializer<'lua> {
|
||||
macro_rules! lua_serialize_number {
|
||||
($name:ident, $t:ty) => {
|
||||
#[inline]
|
||||
fn $name(self, value: $t) -> Result<Value<'lua>> {
|
||||
fn $name(self, value: $t) -> Result<Value> {
|
||||
value.into_lua(self.lua)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl<'lua> ser::Serializer for Serializer<'lua> {
|
||||
type Ok = Value<'lua>;
|
||||
impl<'a> ser::Serializer for Serializer<'a> {
|
||||
type Ok = Value;
|
||||
type Error = Error;
|
||||
|
||||
// Associated types for keeping track of additional state while serializing
|
||||
// compound data structures like sequences and maps.
|
||||
type SerializeSeq = SerializeSeq<'lua>;
|
||||
type SerializeTuple = SerializeSeq<'lua>;
|
||||
type SerializeTupleStruct = SerializeSeq<'lua>;
|
||||
type SerializeTupleVariant = SerializeTupleVariant<'lua>;
|
||||
type SerializeMap = SerializeMap<'lua>;
|
||||
type SerializeStruct = SerializeStruct<'lua>;
|
||||
type SerializeStructVariant = SerializeStructVariant<'lua>;
|
||||
type SerializeSeq = SerializeSeq<'a>;
|
||||
type SerializeTuple = SerializeSeq<'a>;
|
||||
type SerializeTupleStruct = SerializeSeq<'a>;
|
||||
type SerializeTupleVariant = SerializeTupleVariant<'a>;
|
||||
type SerializeMap = SerializeMap<'a>;
|
||||
type SerializeStruct = SerializeStruct<'a>;
|
||||
type SerializeStructVariant = SerializeStructVariant<'a>;
|
||||
|
||||
#[inline]
|
||||
fn serialize_bool(self, value: bool) -> Result<Value<'lua>> {
|
||||
fn serialize_bool(self, value: bool) -> Result<Value> {
|
||||
Ok(Value::Boolean(value))
|
||||
}
|
||||
|
||||
@@ -164,22 +164,22 @@ impl<'lua> ser::Serializer for Serializer<'lua> {
|
||||
lua_serialize_number!(serialize_f64, f64);
|
||||
|
||||
#[inline]
|
||||
fn serialize_char(self, value: char) -> Result<Value<'lua>> {
|
||||
fn serialize_char(self, value: char) -> Result<Value> {
|
||||
self.serialize_str(&value.to_string())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn serialize_str(self, value: &str) -> Result<Value<'lua>> {
|
||||
fn serialize_str(self, value: &str) -> Result<Value> {
|
||||
self.lua.create_string(value).map(Value::String)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn serialize_bytes(self, value: &[u8]) -> Result<Value<'lua>> {
|
||||
fn serialize_bytes(self, value: &[u8]) -> Result<Value> {
|
||||
self.lua.create_string(value).map(Value::String)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn serialize_none(self) -> Result<Value<'lua>> {
|
||||
fn serialize_none(self) -> Result<Value> {
|
||||
if self.options.serialize_none_to_null {
|
||||
Ok(self.lua.null())
|
||||
} else {
|
||||
@@ -188,7 +188,7 @@ impl<'lua> ser::Serializer for Serializer<'lua> {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn serialize_some<T>(self, value: &T) -> Result<Value<'lua>>
|
||||
fn serialize_some<T>(self, value: &T) -> Result<Value>
|
||||
where
|
||||
T: Serialize + ?Sized,
|
||||
{
|
||||
@@ -196,7 +196,7 @@ impl<'lua> ser::Serializer for Serializer<'lua> {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn serialize_unit(self) -> Result<Value<'lua>> {
|
||||
fn serialize_unit(self) -> Result<Value> {
|
||||
if self.options.serialize_unit_to_null {
|
||||
Ok(self.lua.null())
|
||||
} else {
|
||||
@@ -205,7 +205,7 @@ impl<'lua> ser::Serializer for Serializer<'lua> {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn serialize_unit_struct(self, _name: &'static str) -> Result<Value<'lua>> {
|
||||
fn serialize_unit_struct(self, _name: &'static str) -> Result<Value> {
|
||||
if self.options.serialize_unit_to_null {
|
||||
Ok(self.lua.null())
|
||||
} else {
|
||||
@@ -219,12 +219,12 @@ impl<'lua> ser::Serializer for Serializer<'lua> {
|
||||
_name: &'static str,
|
||||
_variant_index: u32,
|
||||
variant: &'static str,
|
||||
) -> Result<Value<'lua>> {
|
||||
) -> Result<Value> {
|
||||
self.serialize_str(variant)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn serialize_newtype_struct<T>(self, _name: &'static str, value: &T) -> Result<Value<'lua>>
|
||||
fn serialize_newtype_struct<T>(self, _name: &'static str, value: &T) -> Result<Value>
|
||||
where
|
||||
T: Serialize + ?Sized,
|
||||
{
|
||||
@@ -238,7 +238,7 @@ impl<'lua> ser::Serializer for Serializer<'lua> {
|
||||
_variant_index: u32,
|
||||
variant: &'static str,
|
||||
value: &T,
|
||||
) -> Result<Value<'lua>>
|
||||
) -> Result<Value>
|
||||
where
|
||||
T: Serialize + ?Sized,
|
||||
{
|
||||
@@ -255,7 +255,7 @@ impl<'lua> ser::Serializer for Serializer<'lua> {
|
||||
if self.options.set_array_metatable {
|
||||
table.set_metatable(Some(self.lua.array_metatable()));
|
||||
}
|
||||
Ok(SerializeSeq::new(table, self.options))
|
||||
Ok(SerializeSeq::new(self.lua, table, self.options))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@@ -264,11 +264,7 @@ impl<'lua> ser::Serializer for Serializer<'lua> {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn serialize_tuple_struct(
|
||||
self,
|
||||
name: &'static str,
|
||||
len: usize,
|
||||
) -> Result<Self::SerializeTupleStruct> {
|
||||
fn serialize_tuple_struct(self, name: &'static str, len: usize) -> Result<Self::SerializeTupleStruct> {
|
||||
#[cfg(feature = "luau")]
|
||||
if name == "Vector" && len == crate::types::Vector::SIZE {
|
||||
return Ok(SerializeSeq::new_vector(self.lua, self.options));
|
||||
@@ -286,6 +282,7 @@ impl<'lua> ser::Serializer for Serializer<'lua> {
|
||||
_len: usize,
|
||||
) -> Result<Self::SerializeTupleVariant> {
|
||||
Ok(SerializeTupleVariant {
|
||||
lua: self.lua,
|
||||
variant,
|
||||
table: self.lua.create_table()?,
|
||||
options: self.options,
|
||||
@@ -295,6 +292,7 @@ impl<'lua> ser::Serializer for Serializer<'lua> {
|
||||
#[inline]
|
||||
fn serialize_map(self, len: Option<usize>) -> Result<Self::SerializeMap> {
|
||||
Ok(SerializeMap {
|
||||
lua: self.lua,
|
||||
key: None,
|
||||
table: self.lua.create_table_with_capacity(0, len.unwrap_or(0))?,
|
||||
options: self.options,
|
||||
@@ -330,6 +328,7 @@ impl<'lua> ser::Serializer for Serializer<'lua> {
|
||||
len: usize,
|
||||
) -> Result<Self::SerializeStructVariant> {
|
||||
Ok(SerializeStructVariant {
|
||||
lua: self.lua,
|
||||
variant,
|
||||
table: self.lua.create_table_with_capacity(0, len)?,
|
||||
options: self.options,
|
||||
@@ -338,19 +337,19 @@ impl<'lua> ser::Serializer for Serializer<'lua> {
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub struct SerializeSeq<'lua> {
|
||||
lua: &'lua Lua,
|
||||
pub struct SerializeSeq<'a> {
|
||||
lua: &'a Lua,
|
||||
#[cfg(feature = "luau")]
|
||||
vector: Option<crate::types::Vector>,
|
||||
table: Option<Table<'lua>>,
|
||||
table: Option<Table>,
|
||||
next: usize,
|
||||
options: Options,
|
||||
}
|
||||
|
||||
impl<'lua> SerializeSeq<'lua> {
|
||||
const fn new(table: Table<'lua>, options: Options) -> Self {
|
||||
impl<'a> SerializeSeq<'a> {
|
||||
fn new(lua: &'a Lua, table: Table, options: Options) -> Self {
|
||||
Self {
|
||||
lua: table.0.lua,
|
||||
lua,
|
||||
#[cfg(feature = "luau")]
|
||||
vector: None,
|
||||
table: Some(table),
|
||||
@@ -360,7 +359,7 @@ impl<'lua> SerializeSeq<'lua> {
|
||||
}
|
||||
|
||||
#[cfg(feature = "luau")]
|
||||
const fn new_vector(lua: &'lua Lua, options: Options) -> Self {
|
||||
const fn new_vector(lua: &'a Lua, options: Options) -> Self {
|
||||
Self {
|
||||
lua,
|
||||
vector: Some(crate::types::Vector::zero()),
|
||||
@@ -371,8 +370,8 @@ impl<'lua> SerializeSeq<'lua> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> ser::SerializeSeq for SerializeSeq<'lua> {
|
||||
type Ok = Value<'lua>;
|
||||
impl ser::SerializeSeq for SerializeSeq<'_> {
|
||||
type Ok = Value;
|
||||
type Error = Error;
|
||||
|
||||
fn serialize_element<T>(&mut self, value: &T) -> Result<()>
|
||||
@@ -386,13 +385,13 @@ impl<'lua> ser::SerializeSeq for SerializeSeq<'lua> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn end(self) -> Result<Value<'lua>> {
|
||||
fn end(self) -> Result<Value> {
|
||||
Ok(Value::Table(self.table.unwrap()))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> ser::SerializeTuple for SerializeSeq<'lua> {
|
||||
type Ok = Value<'lua>;
|
||||
impl ser::SerializeTuple for SerializeSeq<'_> {
|
||||
type Ok = Value;
|
||||
type Error = Error;
|
||||
|
||||
fn serialize_element<T>(&mut self, value: &T) -> Result<()>
|
||||
@@ -402,13 +401,13 @@ impl<'lua> ser::SerializeTuple for SerializeSeq<'lua> {
|
||||
ser::SerializeSeq::serialize_element(self, value)
|
||||
}
|
||||
|
||||
fn end(self) -> Result<Value<'lua>> {
|
||||
fn end(self) -> Result<Value> {
|
||||
ser::SerializeSeq::end(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> ser::SerializeTupleStruct for SerializeSeq<'lua> {
|
||||
type Ok = Value<'lua>;
|
||||
impl ser::SerializeTupleStruct for SerializeSeq<'_> {
|
||||
type Ok = Value;
|
||||
type Error = Error;
|
||||
|
||||
fn serialize_field<T>(&mut self, value: &T) -> Result<()>
|
||||
@@ -426,7 +425,7 @@ impl<'lua> ser::SerializeTupleStruct for SerializeSeq<'lua> {
|
||||
ser::SerializeSeq::serialize_element(self, value)
|
||||
}
|
||||
|
||||
fn end(self) -> Result<Value<'lua>> {
|
||||
fn end(self) -> Result<Value> {
|
||||
#[cfg(feature = "luau")]
|
||||
if let Some(vector) = self.vector {
|
||||
return Ok(Value::Vector(vector));
|
||||
@@ -436,49 +435,48 @@ impl<'lua> ser::SerializeTupleStruct for SerializeSeq<'lua> {
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub struct SerializeTupleVariant<'lua> {
|
||||
pub struct SerializeTupleVariant<'a> {
|
||||
lua: &'a Lua,
|
||||
variant: &'static str,
|
||||
table: Table<'lua>,
|
||||
table: Table,
|
||||
options: Options,
|
||||
}
|
||||
|
||||
impl<'lua> ser::SerializeTupleVariant for SerializeTupleVariant<'lua> {
|
||||
type Ok = Value<'lua>;
|
||||
impl ser::SerializeTupleVariant for SerializeTupleVariant<'_> {
|
||||
type Ok = Value;
|
||||
type Error = Error;
|
||||
|
||||
fn serialize_field<T>(&mut self, value: &T) -> Result<()>
|
||||
where
|
||||
T: Serialize + ?Sized,
|
||||
{
|
||||
let lua = self.table.0.lua;
|
||||
self.table.raw_push(lua.to_value_with(value, self.options)?)
|
||||
self.table.raw_push(self.lua.to_value_with(value, self.options)?)
|
||||
}
|
||||
|
||||
fn end(self) -> Result<Value<'lua>> {
|
||||
let lua = self.table.0.lua;
|
||||
let table = lua.create_table()?;
|
||||
fn end(self) -> Result<Value> {
|
||||
let table = self.lua.create_table()?;
|
||||
table.raw_set(self.variant, self.table)?;
|
||||
Ok(Value::Table(table))
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub struct SerializeMap<'lua> {
|
||||
table: Table<'lua>,
|
||||
key: Option<Value<'lua>>,
|
||||
pub struct SerializeMap<'a> {
|
||||
lua: &'a Lua,
|
||||
table: Table,
|
||||
key: Option<Value>,
|
||||
options: Options,
|
||||
}
|
||||
|
||||
impl<'lua> ser::SerializeMap for SerializeMap<'lua> {
|
||||
type Ok = Value<'lua>;
|
||||
impl ser::SerializeMap for SerializeMap<'_> {
|
||||
type Ok = Value;
|
||||
type Error = Error;
|
||||
|
||||
fn serialize_key<T>(&mut self, key: &T) -> Result<()>
|
||||
where
|
||||
T: Serialize + ?Sized,
|
||||
{
|
||||
let lua = self.table.0.lua;
|
||||
self.key = Some(lua.to_value_with(key, self.options)?);
|
||||
self.key = Some(self.lua.to_value_with(key, self.options)?);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -486,29 +484,25 @@ impl<'lua> ser::SerializeMap for SerializeMap<'lua> {
|
||||
where
|
||||
T: Serialize + ?Sized,
|
||||
{
|
||||
let lua = self.table.0.lua;
|
||||
let key = mlua_expect!(
|
||||
self.key.take(),
|
||||
"serialize_value called before serialize_key"
|
||||
);
|
||||
let value = lua.to_value_with(value, self.options)?;
|
||||
let key = mlua_expect!(self.key.take(), "serialize_value called before serialize_key");
|
||||
let value = self.lua.to_value_with(value, self.options)?;
|
||||
self.table.raw_set(key, value)
|
||||
}
|
||||
|
||||
fn end(self) -> Result<Value<'lua>> {
|
||||
fn end(self) -> Result<Value> {
|
||||
Ok(Value::Table(self.table))
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub struct SerializeStruct<'lua> {
|
||||
lua: &'lua Lua,
|
||||
inner: Option<Value<'lua>>,
|
||||
pub struct SerializeStruct<'a> {
|
||||
lua: &'a Lua,
|
||||
inner: Option<Value>,
|
||||
options: Options,
|
||||
}
|
||||
|
||||
impl<'lua> ser::SerializeStruct for SerializeStruct<'lua> {
|
||||
type Ok = Value<'lua>;
|
||||
impl ser::SerializeStruct for SerializeStruct<'_> {
|
||||
type Ok = Value;
|
||||
type Error = Error;
|
||||
|
||||
fn serialize_field<T>(&mut self, key: &'static str, value: &T) -> Result<()>
|
||||
@@ -529,7 +523,7 @@ impl<'lua> ser::SerializeStruct for SerializeStruct<'lua> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn end(self) -> Result<Value<'lua>> {
|
||||
fn end(self) -> Result<Value> {
|
||||
match self.inner {
|
||||
Some(table @ Value::Table(_)) => Ok(table),
|
||||
Some(value) if self.options.detect_serde_json_arbitrary_precision => {
|
||||
@@ -551,29 +545,28 @@ impl<'lua> ser::SerializeStruct for SerializeStruct<'lua> {
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub struct SerializeStructVariant<'lua> {
|
||||
pub struct SerializeStructVariant<'a> {
|
||||
lua: &'a Lua,
|
||||
variant: &'static str,
|
||||
table: Table<'lua>,
|
||||
table: Table,
|
||||
options: Options,
|
||||
}
|
||||
|
||||
impl<'lua> ser::SerializeStructVariant for SerializeStructVariant<'lua> {
|
||||
type Ok = Value<'lua>;
|
||||
impl ser::SerializeStructVariant for SerializeStructVariant<'_> {
|
||||
type Ok = Value;
|
||||
type Error = Error;
|
||||
|
||||
fn serialize_field<T>(&mut self, key: &'static str, value: &T) -> Result<()>
|
||||
where
|
||||
T: Serialize + ?Sized,
|
||||
{
|
||||
let lua = self.table.0.lua;
|
||||
self.table
|
||||
.raw_set(key, lua.to_value_with(value, self.options)?)?;
|
||||
.raw_set(key, self.lua.to_value_with(value, self.options)?)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn end(self) -> Result<Value<'lua>> {
|
||||
let lua = self.table.0.lua;
|
||||
let table = lua.create_table_with_capacity(0, 1)?;
|
||||
fn end(self) -> Result<Value> {
|
||||
let table = self.lua.create_table_with_capacity(0, 1)?;
|
||||
table.raw_set(self.variant, self.table)?;
|
||||
Ok(Value::Table(table))
|
||||
}
|
||||
|
||||
+1927
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,246 @@
|
||||
use std::any::TypeId;
|
||||
use std::cell::UnsafeCell;
|
||||
use std::mem::MaybeUninit;
|
||||
use std::os::raw::{c_int, c_void};
|
||||
use std::ptr;
|
||||
use std::rc::Rc;
|
||||
use std::sync::Arc;
|
||||
|
||||
use parking_lot::Mutex;
|
||||
use rustc_hash::FxHashMap;
|
||||
|
||||
use crate::error::Result;
|
||||
use crate::state::RawLua;
|
||||
use crate::stdlib::StdLib;
|
||||
use crate::types::{AppData, ReentrantMutex, XRc};
|
||||
use crate::util::{get_internal_metatable, push_internal_userdata, TypeKey, WrappedFailure};
|
||||
|
||||
#[cfg(any(feature = "luau", doc))]
|
||||
use crate::chunk::Compiler;
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
use {futures_util::task::noop_waker_ref, std::ptr::NonNull, std::task::Waker};
|
||||
|
||||
use super::{Lua, WeakLua};
|
||||
|
||||
// Unique key to store `ExtraData` in the registry
|
||||
static EXTRA_REGISTRY_KEY: u8 = 0;
|
||||
|
||||
const WRAPPED_FAILURE_POOL_SIZE: usize = 64;
|
||||
const REF_STACK_RESERVE: c_int = 1;
|
||||
|
||||
/// Data associated with the Lua state.
|
||||
pub(crate) struct ExtraData {
|
||||
pub(super) lua: MaybeUninit<Lua>,
|
||||
pub(super) weak: MaybeUninit<WeakLua>,
|
||||
pub(super) owned: bool,
|
||||
|
||||
pub(super) registered_userdata: FxHashMap<TypeId, c_int>,
|
||||
pub(super) registered_userdata_mt: FxHashMap<*const c_void, Option<TypeId>>,
|
||||
pub(super) last_checked_userdata_mt: (*const c_void, Option<TypeId>),
|
||||
|
||||
// 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)
|
||||
pub(super) app_data: AppData,
|
||||
|
||||
pub(super) safe: bool,
|
||||
pub(super) libs: StdLib,
|
||||
// Used in module mode
|
||||
pub(super) skip_memory_check: bool,
|
||||
|
||||
// Auxiliary thread to store references
|
||||
pub(super) ref_thread: *mut ffi::lua_State,
|
||||
pub(super) ref_stack_size: c_int,
|
||||
pub(super) ref_stack_top: c_int,
|
||||
pub(super) ref_free: Vec<c_int>,
|
||||
|
||||
// Pool of `WrappedFailure` enums in the ref thread (as userdata)
|
||||
pub(super) wrapped_failure_pool: Vec<c_int>,
|
||||
// Pool of `Thread`s (coroutines) for async execution
|
||||
#[cfg(feature = "async")]
|
||||
pub(super) thread_pool: Vec<c_int>,
|
||||
|
||||
// Address of `WrappedFailure` metatable
|
||||
pub(super) wrapped_failure_mt_ptr: *const c_void,
|
||||
|
||||
// Waker for polling futures
|
||||
#[cfg(feature = "async")]
|
||||
pub(super) waker: NonNull<Waker>,
|
||||
|
||||
#[cfg(not(feature = "luau"))]
|
||||
pub(super) hook_callback: Option<crate::types::HookCallback>,
|
||||
#[cfg(not(feature = "luau"))]
|
||||
pub(super) hook_thread: *mut ffi::lua_State,
|
||||
#[cfg(feature = "lua54")]
|
||||
pub(super) warn_callback: Option<crate::types::WarnCallback>,
|
||||
#[cfg(feature = "luau")]
|
||||
pub(super) interrupt_callback: Option<crate::types::InterruptCallback>,
|
||||
|
||||
#[cfg(feature = "luau")]
|
||||
pub(super) sandboxed: bool,
|
||||
#[cfg(feature = "luau")]
|
||||
pub(super) compiler: Option<Compiler>,
|
||||
#[cfg(feature = "luau-jit")]
|
||||
pub(super) enable_jit: bool,
|
||||
}
|
||||
|
||||
impl Drop for ExtraData {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
if !self.owned {
|
||||
self.lua.assume_init_drop();
|
||||
}
|
||||
|
||||
self.weak.assume_init_drop();
|
||||
}
|
||||
*self.registry_unref_list.lock() = None;
|
||||
}
|
||||
}
|
||||
|
||||
static EXTRA_TYPE_KEY: u8 = 0;
|
||||
|
||||
impl TypeKey for XRc<UnsafeCell<ExtraData>> {
|
||||
#[inline(always)]
|
||||
fn type_key() -> *const c_void {
|
||||
&EXTRA_TYPE_KEY as *const u8 as *const c_void
|
||||
}
|
||||
}
|
||||
|
||||
impl ExtraData {
|
||||
// Index of `error_traceback` function in auxiliary thread stack
|
||||
#[cfg(any(feature = "lua51", feature = "luajit", feature = "luau"))]
|
||||
pub(super) const ERROR_TRACEBACK_IDX: c_int = 1;
|
||||
|
||||
pub(super) unsafe fn init(state: *mut ffi::lua_State, owned: bool) -> XRc<UnsafeCell<Self>> {
|
||||
// Create ref stack thread and place it in the registry to prevent it
|
||||
// from being garbage collected.
|
||||
let ref_thread = mlua_expect!(
|
||||
protect_lua!(state, 0, 0, |state| {
|
||||
let thread = ffi::lua_newthread(state);
|
||||
ffi::luaL_ref(state, ffi::LUA_REGISTRYINDEX);
|
||||
thread
|
||||
}),
|
||||
"Error while creating ref thread",
|
||||
);
|
||||
|
||||
let wrapped_failure_mt_ptr = {
|
||||
get_internal_metatable::<WrappedFailure>(state);
|
||||
let ptr = ffi::lua_topointer(state, -1);
|
||||
ffi::lua_pop(state, 1);
|
||||
ptr
|
||||
};
|
||||
|
||||
// Store `error_traceback` function on the ref stack
|
||||
#[cfg(any(feature = "lua51", feature = "luajit", feature = "luau"))]
|
||||
{
|
||||
ffi::lua_pushcfunction(ref_thread, crate::util::error_traceback);
|
||||
assert_eq!(ffi::lua_gettop(ref_thread), Self::ERROR_TRACEBACK_IDX);
|
||||
}
|
||||
|
||||
#[allow(clippy::arc_with_non_send_sync)]
|
||||
let extra = XRc::new(UnsafeCell::new(ExtraData {
|
||||
lua: MaybeUninit::uninit(),
|
||||
weak: MaybeUninit::uninit(),
|
||||
owned,
|
||||
registered_userdata: FxHashMap::default(),
|
||||
registered_userdata_mt: FxHashMap::default(),
|
||||
last_checked_userdata_mt: (ptr::null(), None),
|
||||
registry_unref_list: Arc::new(Mutex::new(Some(Vec::new()))),
|
||||
app_data: AppData::default(),
|
||||
safe: false,
|
||||
libs: StdLib::NONE,
|
||||
skip_memory_check: false,
|
||||
ref_thread,
|
||||
// We need some reserved stack space to move values in and out of the ref stack.
|
||||
ref_stack_size: ffi::LUA_MINSTACK - REF_STACK_RESERVE,
|
||||
ref_stack_top: ffi::lua_gettop(ref_thread),
|
||||
ref_free: Vec::new(),
|
||||
wrapped_failure_pool: Vec::with_capacity(WRAPPED_FAILURE_POOL_SIZE),
|
||||
#[cfg(feature = "async")]
|
||||
thread_pool: Vec::new(),
|
||||
wrapped_failure_mt_ptr,
|
||||
#[cfg(feature = "async")]
|
||||
waker: NonNull::from(noop_waker_ref()),
|
||||
#[cfg(not(feature = "luau"))]
|
||||
hook_callback: None,
|
||||
#[cfg(not(feature = "luau"))]
|
||||
hook_thread: ptr::null_mut(),
|
||||
#[cfg(feature = "lua54")]
|
||||
warn_callback: None,
|
||||
#[cfg(feature = "luau")]
|
||||
interrupt_callback: None,
|
||||
#[cfg(feature = "luau")]
|
||||
sandboxed: false,
|
||||
#[cfg(feature = "luau")]
|
||||
compiler: None,
|
||||
#[cfg(feature = "luau-jit")]
|
||||
enable_jit: true,
|
||||
}));
|
||||
|
||||
// Store it in the registry
|
||||
mlua_expect!(Self::store(&extra, state), "Error while storing extra data");
|
||||
|
||||
extra
|
||||
}
|
||||
|
||||
pub(super) unsafe fn set_lua(&mut self, raw: &XRc<ReentrantMutex<RawLua>>) {
|
||||
self.lua.write(Lua {
|
||||
raw: XRc::clone(raw),
|
||||
collect_garbage: false,
|
||||
});
|
||||
if self.owned {
|
||||
XRc::decrement_strong_count(XRc::as_ptr(raw));
|
||||
}
|
||||
self.weak.write(WeakLua(XRc::downgrade(raw)));
|
||||
}
|
||||
|
||||
pub(super) 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
|
||||
return (*ffi::lua_callbacks(state)).userdata as *mut _;
|
||||
}
|
||||
|
||||
let extra_key = &EXTRA_REGISTRY_KEY as *const u8 as *const c_void;
|
||||
if ffi::lua_rawgetp(state, ffi::LUA_REGISTRYINDEX, extra_key) != ffi::LUA_TUSERDATA {
|
||||
// `ExtraData` can be null only when Lua state is foreign.
|
||||
// This case in used in `Lua::try_from_ptr()`.
|
||||
ffi::lua_pop(state, 1);
|
||||
return ptr::null_mut();
|
||||
}
|
||||
let extra_ptr = ffi::lua_touserdata(state, -1) as *mut Rc<UnsafeCell<ExtraData>>;
|
||||
ffi::lua_pop(state, 1);
|
||||
(*extra_ptr).get()
|
||||
}
|
||||
|
||||
unsafe fn store(extra: &XRc<UnsafeCell<Self>>, state: *mut ffi::lua_State) -> Result<()> {
|
||||
#[cfg(feature = "luau")]
|
||||
if cfg!(not(feature = "module")) {
|
||||
(*ffi::lua_callbacks(state)).userdata = extra.get() as *mut _;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
push_internal_userdata(state, XRc::clone(extra), true)?;
|
||||
protect_lua!(state, 1, 0, fn(state) {
|
||||
let extra_key = &EXTRA_REGISTRY_KEY as *const u8 as *const c_void;
|
||||
ffi::lua_rawsetp(state, ffi::LUA_REGISTRYINDEX, extra_key);
|
||||
})
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub(super) unsafe fn lua(&self) -> &Lua {
|
||||
self.lua.assume_init_ref()
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub(super) unsafe fn raw_lua(&self) -> &RawLua {
|
||||
&*self.lua.assume_init_ref().raw.data_ptr()
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub(super) unsafe fn weak(&self) -> &WeakLua {
|
||||
self.weak.assume_init_ref()
|
||||
}
|
||||
}
|
||||
+1351
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,180 @@
|
||||
use std::os::raw::c_int;
|
||||
use std::panic::{catch_unwind, AssertUnwindSafe};
|
||||
use std::ptr;
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
use crate::state::{ExtraData, RawLua};
|
||||
use crate::util::{self, get_internal_metatable, WrappedFailure};
|
||||
|
||||
const WRAPPED_FAILURE_POOL_SIZE: usize = 64;
|
||||
|
||||
pub(super) struct StateGuard<'a>(&'a RawLua, *mut ffi::lua_State);
|
||||
|
||||
impl<'a> StateGuard<'a> {
|
||||
pub(super) fn new(inner: &'a RawLua, mut state: *mut ffi::lua_State) -> Self {
|
||||
state = inner.state.replace(state);
|
||||
Self(inner, state)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Drop for StateGuard<'a> {
|
||||
fn drop(&mut self) {
|
||||
self.0.state.set(self.1);
|
||||
}
|
||||
}
|
||||
|
||||
// An optimized version of `callback_error` that does not allocate `WrappedFailure` userdata
|
||||
// and instead reuses unsed values from previous calls (or allocates new).
|
||||
pub(super) unsafe fn callback_error_ext<F, R>(
|
||||
state: *mut ffi::lua_State,
|
||||
mut extra: *mut ExtraData,
|
||||
f: F,
|
||||
) -> R
|
||||
where
|
||||
F: FnOnce(*mut ExtraData, c_int) -> Result<R>,
|
||||
{
|
||||
if extra.is_null() {
|
||||
extra = ExtraData::get(state);
|
||||
}
|
||||
|
||||
let nargs = ffi::lua_gettop(state);
|
||||
|
||||
enum PreallocatedFailure {
|
||||
New(*mut WrappedFailure),
|
||||
Existing(i32),
|
||||
}
|
||||
|
||||
impl PreallocatedFailure {
|
||||
unsafe fn reserve(state: *mut ffi::lua_State, extra: *mut ExtraData) -> Self {
|
||||
match (*extra).wrapped_failure_pool.pop() {
|
||||
Some(index) => PreallocatedFailure::Existing(index),
|
||||
None => {
|
||||
// We need to check stack for Luau in case when callback is called from interrupt
|
||||
// See https://github.com/Roblox/luau/issues/446 and mlua #142 and #153
|
||||
#[cfg(feature = "luau")]
|
||||
ffi::lua_rawcheckstack(state, 2);
|
||||
// Place it to the beginning of the stack
|
||||
let ud = WrappedFailure::new_userdata(state);
|
||||
ffi::lua_insert(state, 1);
|
||||
PreallocatedFailure::New(ud)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn r#use(&self, state: *mut ffi::lua_State, extra: *mut ExtraData) -> *mut WrappedFailure {
|
||||
let ref_thread = (*extra).ref_thread;
|
||||
match *self {
|
||||
PreallocatedFailure::New(ud) => {
|
||||
ffi::lua_settop(state, 1);
|
||||
ud
|
||||
}
|
||||
PreallocatedFailure::Existing(index) => {
|
||||
ffi::lua_settop(state, 0);
|
||||
#[cfg(feature = "luau")]
|
||||
ffi::lua_rawcheckstack(state, 2);
|
||||
ffi::lua_pushvalue(ref_thread, index);
|
||||
ffi::lua_xmove(ref_thread, state, 1);
|
||||
ffi::lua_pushnil(ref_thread);
|
||||
ffi::lua_replace(ref_thread, index);
|
||||
(*extra).ref_free.push(index);
|
||||
ffi::lua_touserdata(state, -1) as *mut WrappedFailure
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn release(self, state: *mut ffi::lua_State, extra: *mut ExtraData) {
|
||||
let ref_thread = (*extra).ref_thread;
|
||||
match self {
|
||||
PreallocatedFailure::New(_) => {
|
||||
if (*extra).wrapped_failure_pool.len() < WRAPPED_FAILURE_POOL_SIZE {
|
||||
ffi::lua_rotate(state, 1, -1);
|
||||
ffi::lua_xmove(state, ref_thread, 1);
|
||||
let index = ref_stack_pop(extra);
|
||||
(*extra).wrapped_failure_pool.push(index);
|
||||
} else {
|
||||
ffi::lua_remove(state, 1);
|
||||
}
|
||||
}
|
||||
PreallocatedFailure::Existing(index) => {
|
||||
if (*extra).wrapped_failure_pool.len() < WRAPPED_FAILURE_POOL_SIZE {
|
||||
(*extra).wrapped_failure_pool.push(index);
|
||||
} else {
|
||||
ffi::lua_pushnil(ref_thread);
|
||||
ffi::lua_replace(ref_thread, index);
|
||||
(*extra).ref_free.push(index);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// We cannot shadow Rust errors with Lua ones, so we need to reserve pre-allocated memory
|
||||
// to store a wrapped failure (error or panic) *before* we proceed.
|
||||
let prealloc_failure = PreallocatedFailure::reserve(state, extra);
|
||||
|
||||
match catch_unwind(AssertUnwindSafe(|| f(extra, nargs))) {
|
||||
Ok(Ok(r)) => {
|
||||
// Return unused `WrappedFailure` to the pool
|
||||
prealloc_failure.release(state, extra);
|
||||
r
|
||||
}
|
||||
Ok(Err(err)) => {
|
||||
let wrapped_error = prealloc_failure.r#use(state, extra);
|
||||
|
||||
// Build `CallbackError` with traceback
|
||||
let traceback = if ffi::lua_checkstack(state, ffi::LUA_TRACEBACK_STACK) != 0 {
|
||||
ffi::luaL_traceback(state, state, ptr::null(), 0);
|
||||
let traceback = util::to_string(state, -1);
|
||||
ffi::lua_pop(state, 1);
|
||||
traceback
|
||||
} else {
|
||||
"<not enough stack space for traceback>".to_string()
|
||||
};
|
||||
let cause = Arc::new(err);
|
||||
ptr::write(
|
||||
wrapped_error,
|
||||
WrappedFailure::Error(Error::CallbackError { traceback, cause }),
|
||||
);
|
||||
get_internal_metatable::<WrappedFailure>(state);
|
||||
ffi::lua_setmetatable(state, -2);
|
||||
|
||||
ffi::lua_error(state)
|
||||
}
|
||||
Err(p) => {
|
||||
let wrapped_panic = prealloc_failure.r#use(state, extra);
|
||||
ptr::write(wrapped_panic, WrappedFailure::Panic(Some(p)));
|
||||
get_internal_metatable::<WrappedFailure>(state);
|
||||
ffi::lua_setmetatable(state, -2);
|
||||
ffi::lua_error(state)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
+1
-6
@@ -8,12 +8,7 @@ impl StdLib {
|
||||
/// [`coroutine`](https://www.lua.org/manual/5.4/manual.html#6.2) library
|
||||
///
|
||||
/// Requires `feature = "lua54/lua53/lua52/luau"`
|
||||
#[cfg(any(
|
||||
feature = "lua54",
|
||||
feature = "lua53",
|
||||
feature = "lua52",
|
||||
feature = "luau"
|
||||
))]
|
||||
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52", feature = "luau"))]
|
||||
pub const COROUTINE: StdLib = StdLib(1);
|
||||
|
||||
/// [`table`](https://www.lua.org/manual/5.4/manual.html#6.6) library
|
||||
|
||||
+165
-92
@@ -1,8 +1,9 @@
|
||||
use std::borrow::{Borrow, Cow};
|
||||
use std::borrow::Borrow;
|
||||
use std::hash::{Hash, Hasher};
|
||||
use std::ops::Deref;
|
||||
use std::os::raw::c_void;
|
||||
use std::string::String as StdString;
|
||||
use std::{fmt, slice, str};
|
||||
use std::{cmp, fmt, slice, str};
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
use {
|
||||
@@ -11,37 +12,17 @@ use {
|
||||
};
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
use crate::types::LuaRef;
|
||||
use crate::state::Lua;
|
||||
use crate::types::ValueRef;
|
||||
|
||||
/// Handle to an internal Lua string.
|
||||
///
|
||||
/// Unlike Rust strings, Lua strings may not be valid UTF-8.
|
||||
#[derive(Clone)]
|
||||
pub struct String<'lua>(pub(crate) LuaRef<'lua>);
|
||||
pub struct String(pub(crate) ValueRef);
|
||||
|
||||
/// Owned handle to an internal Lua string.
|
||||
///
|
||||
/// The owned handle holds a *strong* reference to the current Lua instance.
|
||||
/// Be warned, if you place it into a Lua type (eg. [`UserData`] or a Rust callback), it is *very easy*
|
||||
/// to accidentally cause reference cycles that would prevent destroying Lua instance.
|
||||
///
|
||||
/// [`UserData`]: crate::UserData
|
||||
#[cfg(feature = "unstable")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "unstable")))]
|
||||
#[derive(Clone)]
|
||||
pub struct OwnedString(pub(crate) crate::types::LuaOwnedRef);
|
||||
|
||||
#[cfg(feature = "unstable")]
|
||||
impl OwnedString {
|
||||
/// Get borrowed handle to the underlying Lua string.
|
||||
#[cfg_attr(feature = "send", allow(unused))]
|
||||
pub const fn to_ref(&self) -> String {
|
||||
String(self.0.to_ref())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> String<'lua> {
|
||||
/// Get a `&str` slice if the Lua string is valid UTF-8.
|
||||
impl String {
|
||||
/// Get a [`BorrowedStr`] if the Lua string is valid UTF-8.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
@@ -60,15 +41,17 @@ impl<'lua> String<'lua> {
|
||||
/// # }
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn to_str(&self) -> Result<&str> {
|
||||
str::from_utf8(self.as_bytes()).map_err(|e| Error::FromLuaConversionError {
|
||||
pub fn to_str(&self) -> Result<BorrowedStr> {
|
||||
let BorrowedBytes(bytes, guard) = self.as_bytes();
|
||||
let s = str::from_utf8(bytes).map_err(|e| Error::FromLuaConversionError {
|
||||
from: "string",
|
||||
to: "&str",
|
||||
message: Some(e.to_string()),
|
||||
})
|
||||
})?;
|
||||
Ok(BorrowedStr(s, guard))
|
||||
}
|
||||
|
||||
/// Converts this string to a [`Cow<str>`].
|
||||
/// Converts this string to a [`StdString`].
|
||||
///
|
||||
/// Any non-Unicode sequences are replaced with [`U+FFFD REPLACEMENT CHARACTER`][U+FFFD].
|
||||
///
|
||||
@@ -87,8 +70,8 @@ impl<'lua> String<'lua> {
|
||||
/// # }
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn to_string_lossy(&self) -> Cow<'_, str> {
|
||||
StdString::from_utf8_lossy(self.as_bytes())
|
||||
pub fn to_string_lossy(&self) -> StdString {
|
||||
StdString::from_utf8_lossy(&self.as_bytes()).into_owned()
|
||||
}
|
||||
|
||||
/// Get the bytes that make up this string.
|
||||
@@ -109,14 +92,21 @@ impl<'lua> String<'lua> {
|
||||
/// # }
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn as_bytes(&self) -> &[u8] {
|
||||
let nulled = self.as_bytes_with_nul();
|
||||
&nulled[..nulled.len() - 1]
|
||||
pub fn as_bytes(&self) -> BorrowedBytes {
|
||||
let (bytes, guard) = unsafe { self.to_slice() };
|
||||
BorrowedBytes(&bytes[..bytes.len() - 1], guard)
|
||||
}
|
||||
|
||||
/// Get the bytes that make up this string, including the trailing nul byte.
|
||||
pub fn as_bytes_with_nul(&self) -> &[u8] {
|
||||
let ref_thread = self.0.lua.ref_thread();
|
||||
pub fn as_bytes_with_nul(&self) -> BorrowedBytes {
|
||||
let (bytes, guard) = unsafe { self.to_slice() };
|
||||
BorrowedBytes(bytes, guard)
|
||||
}
|
||||
|
||||
unsafe fn to_slice(&self) -> (&[u8], Lua) {
|
||||
let lua = self.0.lua.upgrade();
|
||||
let rawlua = lua.lock();
|
||||
let ref_thread = rawlua.ref_thread();
|
||||
unsafe {
|
||||
mlua_debug_assert!(
|
||||
ffi::lua_type(ref_thread, self.0.index) == ffi::LUA_TSTRING,
|
||||
@@ -128,7 +118,8 @@ impl<'lua> String<'lua> {
|
||||
// string type
|
||||
let data = ffi::lua_tolstring(ref_thread, self.0.index, &mut size);
|
||||
|
||||
slice::from_raw_parts(data as *const u8, size + 1)
|
||||
drop(rawlua);
|
||||
(slice::from_raw_parts(data as *const u8, size + 1), lua)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -141,21 +132,13 @@ impl<'lua> String<'lua> {
|
||||
pub fn to_pointer(&self) -> *const c_void {
|
||||
self.0.to_pointer()
|
||||
}
|
||||
|
||||
/// Convert this handle to owned version.
|
||||
#[cfg(all(feature = "unstable", any(not(feature = "send"), doc)))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all(feature = "unstable", not(feature = "send")))))]
|
||||
#[inline]
|
||||
pub fn into_owned(self) -> OwnedString {
|
||||
OwnedString(self.0.into_owned())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> fmt::Debug for String<'lua> {
|
||||
impl fmt::Debug for String {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
let bytes = self.as_bytes();
|
||||
// Check if the string is valid utf8
|
||||
if let Ok(s) = str::from_utf8(bytes) {
|
||||
if let Ok(s) = str::from_utf8(&bytes) {
|
||||
return s.fmt(f);
|
||||
}
|
||||
|
||||
@@ -180,27 +163,14 @@ impl<'lua> fmt::Debug for String<'lua> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> AsRef<[u8]> for String<'lua> {
|
||||
fn as_ref(&self) -> &[u8] {
|
||||
self.as_bytes()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> Borrow<[u8]> for String<'lua> {
|
||||
fn borrow(&self) -> &[u8] {
|
||||
self.as_bytes()
|
||||
}
|
||||
}
|
||||
|
||||
// Lua strings are basically &[u8] slices, so implement PartialEq for anything resembling that.
|
||||
//
|
||||
// This makes our `String` comparable with `Vec<u8>`, `[u8]`, `&str`, `String` and `mlua::String`
|
||||
// itself.
|
||||
// This makes our `String` comparable with `Vec<u8>`, `[u8]`, `&str` and `String`.
|
||||
//
|
||||
// The only downside is that this disallows a comparison with `Cow<str>`, as that only implements
|
||||
// `AsRef<str>`, which collides with this impl. Requiring `AsRef<str>` would fix that, but limit us
|
||||
// in other ways.
|
||||
impl<'lua, T> PartialEq<T> for String<'lua>
|
||||
impl<T> PartialEq<T> for String
|
||||
where
|
||||
T: AsRef<[u8]> + ?Sized,
|
||||
{
|
||||
@@ -209,55 +179,151 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> Eq for String<'lua> {}
|
||||
impl PartialEq<String> for String {
|
||||
fn eq(&self, other: &String) -> bool {
|
||||
self.as_bytes() == other.as_bytes()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> Hash for String<'lua> {
|
||||
impl PartialEq<&String> for String {
|
||||
fn eq(&self, other: &&String) -> bool {
|
||||
self.as_bytes() == other.as_bytes()
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for String {}
|
||||
|
||||
impl Hash for String {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
self.as_bytes().hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
impl<'lua> Serialize for String<'lua> {
|
||||
impl Serialize for String {
|
||||
fn serialize<S>(&self, serializer: S) -> StdResult<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
match self.to_str() {
|
||||
Ok(s) => serializer.serialize_str(s),
|
||||
Err(_) => serializer.serialize_bytes(self.as_bytes()),
|
||||
Ok(s) => serializer.serialize_str(&s),
|
||||
Err(_) => serializer.serialize_bytes(&self.as_bytes()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Additional shortcuts
|
||||
#[cfg(feature = "unstable")]
|
||||
impl OwnedString {
|
||||
/// Get a `&str` slice if the Lua string is valid UTF-8.
|
||||
///
|
||||
/// This is a shortcut for [`String::to_str()`].
|
||||
#[inline]
|
||||
pub fn to_str(&self) -> Result<&str> {
|
||||
let s = self.to_ref();
|
||||
// Reattach lifetime to &self
|
||||
unsafe { std::mem::transmute(s.to_str()) }
|
||||
}
|
||||
/// A borrowed string (`&str`) that holds a strong reference to the Lua state.
|
||||
pub struct BorrowedStr<'a>(&'a str, #[allow(unused)] Lua);
|
||||
|
||||
/// Get the bytes that make up this string.
|
||||
///
|
||||
/// This is a shortcut for [`String::as_bytes()`].
|
||||
#[inline]
|
||||
pub fn as_bytes(&self) -> &[u8] {
|
||||
let s = self.to_ref();
|
||||
// Reattach lifetime to &self
|
||||
unsafe { std::mem::transmute(s.as_bytes()) }
|
||||
impl Deref for BorrowedStr<'_> {
|
||||
type Target = str;
|
||||
|
||||
#[inline(always)]
|
||||
fn deref(&self) -> &str {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "unstable")]
|
||||
impl fmt::Debug for OwnedString {
|
||||
impl Borrow<str> for BorrowedStr<'_> {
|
||||
#[inline(always)]
|
||||
fn borrow(&self) -> &str {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<str> for BorrowedStr<'_> {
|
||||
#[inline(always)]
|
||||
fn as_ref(&self) -> &str {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for BorrowedStr<'_> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
self.to_ref().fmt(f)
|
||||
self.0.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for BorrowedStr<'_> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
self.0.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> PartialEq<T> for BorrowedStr<'_>
|
||||
where
|
||||
T: AsRef<str>,
|
||||
{
|
||||
fn eq(&self, other: &T) -> bool {
|
||||
self.0 == other.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> PartialOrd<T> for BorrowedStr<'_>
|
||||
where
|
||||
T: AsRef<str>,
|
||||
{
|
||||
fn partial_cmp(&self, other: &T) -> Option<cmp::Ordering> {
|
||||
self.0.partial_cmp(other.as_ref())
|
||||
}
|
||||
}
|
||||
|
||||
/// A borrowed byte slice (`&[u8]`) that holds a strong reference to the Lua state.
|
||||
pub struct BorrowedBytes<'a>(&'a [u8], #[allow(unused)] Lua);
|
||||
|
||||
impl Deref for BorrowedBytes<'_> {
|
||||
type Target = [u8];
|
||||
|
||||
#[inline(always)]
|
||||
fn deref(&self) -> &[u8] {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl Borrow<[u8]> for BorrowedBytes<'_> {
|
||||
#[inline(always)]
|
||||
fn borrow(&self) -> &[u8] {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<[u8]> for BorrowedBytes<'_> {
|
||||
#[inline(always)]
|
||||
fn as_ref(&self) -> &[u8] {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for BorrowedBytes<'_> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
self.0.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> PartialEq<T> for BorrowedBytes<'_>
|
||||
where
|
||||
T: AsRef<[u8]>,
|
||||
{
|
||||
fn eq(&self, other: &T) -> bool {
|
||||
self.0 == other.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> PartialOrd<T> for BorrowedBytes<'_>
|
||||
where
|
||||
T: AsRef<[u8]>,
|
||||
{
|
||||
fn partial_cmp(&self, other: &T) -> Option<cmp::Ordering> {
|
||||
self.0.partial_cmp(other.as_ref())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> IntoIterator for BorrowedBytes<'a> {
|
||||
type Item = &'a u8;
|
||||
type IntoIter = slice::Iter<'a, u8>;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.0.iter()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -265,5 +331,12 @@ impl fmt::Debug for OwnedString {
|
||||
mod assertions {
|
||||
use super::*;
|
||||
|
||||
#[cfg(not(feature = "send"))]
|
||||
static_assertions::assert_not_impl_any!(String: Send);
|
||||
#[cfg(feature = "send")]
|
||||
static_assertions::assert_impl_all!(String: Send, Sync);
|
||||
#[cfg(feature = "send")]
|
||||
static_assertions::assert_impl_all!(BorrowedBytes: Send, Sync);
|
||||
#[cfg(feature = "send")]
|
||||
static_assertions::assert_impl_all!(BorrowedStr: Send, Sync);
|
||||
}
|
||||
|
||||
+191
-296
@@ -2,6 +2,7 @@ use std::collections::HashSet;
|
||||
use std::fmt;
|
||||
use std::marker::PhantomData;
|
||||
use std::os::raw::c_void;
|
||||
use std::string::String as StdString;
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
use {
|
||||
@@ -12,40 +13,20 @@ use {
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
use crate::function::Function;
|
||||
use crate::private::Sealed;
|
||||
use crate::types::{Integer, LuaRef};
|
||||
use crate::state::{LuaGuard, RawLua};
|
||||
use crate::traits::ObjectLike;
|
||||
use crate::types::{Integer, ValueRef};
|
||||
use crate::util::{assert_stack, check_stack, StackGuard};
|
||||
use crate::value::{FromLua, FromLuaMulti, IntoLua, IntoLuaMulti, Nil, Value};
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
use futures_util::future::{self, LocalBoxFuture};
|
||||
use futures_util::future::{self, Either, Future};
|
||||
|
||||
/// Handle to an internal Lua table.
|
||||
#[derive(Clone)]
|
||||
pub struct Table<'lua>(pub(crate) LuaRef<'lua>);
|
||||
pub struct Table(pub(crate) ValueRef);
|
||||
|
||||
/// Owned handle to an internal Lua table.
|
||||
///
|
||||
/// The owned handle holds a *strong* reference to the current Lua instance.
|
||||
/// Be warned, if you place it into a Lua type (eg. [`UserData`] or a Rust callback), it is *very easy*
|
||||
/// to accidentally cause reference cycles that would prevent destroying Lua instance.
|
||||
///
|
||||
/// [`UserData`]: crate::UserData
|
||||
#[cfg(feature = "unstable")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "unstable")))]
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct OwnedTable(pub(crate) crate::types::LuaOwnedRef);
|
||||
|
||||
#[cfg(feature = "unstable")]
|
||||
impl OwnedTable {
|
||||
/// Get borrowed handle to the underlying Lua table.
|
||||
#[cfg_attr(feature = "send", allow(unused))]
|
||||
pub const fn to_ref(&self) -> Table {
|
||||
Table(self.0.to_ref())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> Table<'lua> {
|
||||
impl Table {
|
||||
/// Sets a key-value pair in the table.
|
||||
///
|
||||
/// If the value is `nil`, this will effectively remove the pair.
|
||||
@@ -79,21 +60,25 @@ impl<'lua> Table<'lua> {
|
||||
/// ```
|
||||
///
|
||||
/// [`raw_set`]: #method.raw_set
|
||||
pub fn set<K: IntoLua<'lua>, V: IntoLua<'lua>>(&self, key: K, value: V) -> Result<()> {
|
||||
// Fast track
|
||||
pub fn set(&self, key: impl IntoLua, value: impl IntoLua) -> Result<()> {
|
||||
// Fast track (skip protected call)
|
||||
if !self.has_metatable() {
|
||||
return self.raw_set(key, value);
|
||||
}
|
||||
|
||||
let lua = self.0.lua;
|
||||
self.set_protected(key, value)
|
||||
}
|
||||
|
||||
pub(crate) fn set_protected(&self, key: impl IntoLua, value: impl IntoLua) -> Result<()> {
|
||||
let lua = self.0.lua.lock();
|
||||
let state = lua.state();
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(state);
|
||||
check_stack(state, 5)?;
|
||||
|
||||
lua.push_ref(&self.0);
|
||||
key.push_into_stack(lua)?;
|
||||
value.push_into_stack(lua)?;
|
||||
key.push_into_stack(&lua)?;
|
||||
value.push_into_stack(&lua)?;
|
||||
protect_lua!(state, 3, 0, fn(state) ffi::lua_settable(state, -3))
|
||||
}
|
||||
}
|
||||
@@ -122,50 +107,54 @@ impl<'lua> Table<'lua> {
|
||||
/// ```
|
||||
///
|
||||
/// [`raw_get`]: #method.raw_get
|
||||
pub fn get<K: IntoLua<'lua>, V: FromLua<'lua>>(&self, key: K) -> Result<V> {
|
||||
// Fast track
|
||||
pub fn get<V: FromLua>(&self, key: impl IntoLua) -> Result<V> {
|
||||
// Fast track (skip protected call)
|
||||
if !self.has_metatable() {
|
||||
return self.raw_get(key);
|
||||
}
|
||||
|
||||
let lua = self.0.lua;
|
||||
self.get_protected(key)
|
||||
}
|
||||
|
||||
pub(crate) fn get_protected<V: FromLua>(&self, key: impl IntoLua) -> Result<V> {
|
||||
let lua = self.0.lua.lock();
|
||||
let state = lua.state();
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(state);
|
||||
check_stack(state, 4)?;
|
||||
|
||||
lua.push_ref(&self.0);
|
||||
key.push_into_stack(lua)?;
|
||||
key.push_into_stack(&lua)?;
|
||||
protect_lua!(state, 2, 1, fn(state) ffi::lua_gettable(state, -2))?;
|
||||
|
||||
V::from_stack(-1, lua)
|
||||
V::from_stack(-1, &lua)
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks whether the table contains a non-nil value for `key`.
|
||||
///
|
||||
/// This might invoke the `__index` metamethod.
|
||||
pub fn contains_key<K: IntoLua<'lua>>(&self, key: K) -> Result<bool> {
|
||||
Ok(self.get::<_, Value>(key)? != Value::Nil)
|
||||
pub fn contains_key(&self, key: impl IntoLua) -> Result<bool> {
|
||||
Ok(self.get::<Value>(key)? != Value::Nil)
|
||||
}
|
||||
|
||||
/// Appends a value to the back of the table.
|
||||
///
|
||||
/// This might invoke the `__len` and `__newindex` metamethods.
|
||||
pub fn push<V: IntoLua<'lua>>(&self, value: V) -> Result<()> {
|
||||
// Fast track
|
||||
pub fn push(&self, value: impl IntoLua) -> Result<()> {
|
||||
// Fast track (skip protected call)
|
||||
if !self.has_metatable() {
|
||||
return self.raw_push(value);
|
||||
}
|
||||
|
||||
let lua = self.0.lua;
|
||||
let lua = self.0.lua.lock();
|
||||
let state = lua.state();
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(state);
|
||||
check_stack(state, 4)?;
|
||||
|
||||
lua.push_ref(&self.0);
|
||||
value.push_into_stack(lua)?;
|
||||
value.push_into_stack(&lua)?;
|
||||
protect_lua!(state, 2, 0, fn(state) {
|
||||
let len = ffi::luaL_len(state, -2) as Integer;
|
||||
ffi::lua_seti(state, -2, len + 1);
|
||||
@@ -177,13 +166,13 @@ impl<'lua> Table<'lua> {
|
||||
/// Removes the last element from the table and returns it.
|
||||
///
|
||||
/// This might invoke the `__len` and `__newindex` metamethods.
|
||||
pub fn pop<V: FromLua<'lua>>(&self) -> Result<V> {
|
||||
// Fast track
|
||||
pub fn pop<V: FromLua>(&self) -> Result<V> {
|
||||
// Fast track (skip protected call)
|
||||
if !self.has_metatable() {
|
||||
return self.raw_pop();
|
||||
}
|
||||
|
||||
let lua = self.0.lua;
|
||||
let lua = self.0.lua.lock();
|
||||
let state = lua.state();
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(state);
|
||||
@@ -196,7 +185,7 @@ impl<'lua> Table<'lua> {
|
||||
ffi::lua_pushnil(state);
|
||||
ffi::lua_seti(state, -3, len);
|
||||
})?;
|
||||
V::from_stack(-1, lua)
|
||||
V::from_stack(-1, &lua)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -240,16 +229,12 @@ impl<'lua> Table<'lua> {
|
||||
// If self does not define it, then check the other table.
|
||||
if let Some(mt) = self.get_metatable() {
|
||||
if mt.contains_key("__eq")? {
|
||||
return mt
|
||||
.get::<_, Function>("__eq")?
|
||||
.call((self.clone(), other.clone()));
|
||||
return mt.get::<Function>("__eq")?.call((self, other));
|
||||
}
|
||||
}
|
||||
if let Some(mt) = other.get_metatable() {
|
||||
if mt.contains_key("__eq")? {
|
||||
return mt
|
||||
.get::<_, Function>("__eq")?
|
||||
.call((self.clone(), other.clone()));
|
||||
return mt.get::<Function>("__eq")?.call((self, other));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -257,19 +242,19 @@ impl<'lua> Table<'lua> {
|
||||
}
|
||||
|
||||
/// Sets a key-value pair without invoking metamethods.
|
||||
pub fn raw_set<K: IntoLua<'lua>, V: IntoLua<'lua>>(&self, key: K, value: V) -> Result<()> {
|
||||
pub fn raw_set(&self, key: impl IntoLua, value: impl IntoLua) -> Result<()> {
|
||||
#[cfg(feature = "luau")]
|
||||
self.check_readonly_write()?;
|
||||
|
||||
let lua = self.0.lua;
|
||||
let lua = self.0.lua.lock();
|
||||
let state = lua.state();
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(state);
|
||||
check_stack(state, 5)?;
|
||||
|
||||
lua.push_ref(&self.0);
|
||||
key.push_into_stack(lua)?;
|
||||
value.push_into_stack(lua)?;
|
||||
key.push_into_stack(&lua)?;
|
||||
value.push_into_stack(&lua)?;
|
||||
|
||||
if lua.unlikely_memory_error() {
|
||||
ffi::lua_rawset(state, -3);
|
||||
@@ -282,37 +267,37 @@ impl<'lua> Table<'lua> {
|
||||
}
|
||||
|
||||
/// Gets the value associated to `key` without invoking metamethods.
|
||||
pub fn raw_get<K: IntoLua<'lua>, V: FromLua<'lua>>(&self, key: K) -> Result<V> {
|
||||
let lua = self.0.lua;
|
||||
pub fn raw_get<V: FromLua>(&self, key: impl IntoLua) -> Result<V> {
|
||||
let lua = self.0.lua.lock();
|
||||
let state = lua.state();
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(state);
|
||||
check_stack(state, 3)?;
|
||||
|
||||
lua.push_ref(&self.0);
|
||||
key.push_into_stack(lua)?;
|
||||
key.push_into_stack(&lua)?;
|
||||
ffi::lua_rawget(state, -2);
|
||||
|
||||
V::from_stack(-1, lua)
|
||||
V::from_stack(-1, &lua)
|
||||
}
|
||||
}
|
||||
|
||||
/// Inserts element value at position `idx` to the table, shifting up the elements from `table[idx]`.
|
||||
/// The worst case complexity is O(n), where n is the table length.
|
||||
pub fn raw_insert<V: IntoLua<'lua>>(&self, idx: Integer, value: V) -> Result<()> {
|
||||
/// Inserts element value at position `idx` to the table, shifting up the elements from
|
||||
/// `table[idx]`. The worst case complexity is O(n), where n is the table length.
|
||||
pub fn raw_insert(&self, idx: Integer, value: impl IntoLua) -> Result<()> {
|
||||
let size = self.raw_len() as Integer;
|
||||
if idx < 1 || idx > size + 1 {
|
||||
return Err(Error::runtime("index out of bounds"));
|
||||
}
|
||||
|
||||
let lua = self.0.lua;
|
||||
let lua = self.0.lua.lock();
|
||||
let state = lua.state();
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(state);
|
||||
check_stack(state, 5)?;
|
||||
|
||||
lua.push_ref(&self.0);
|
||||
value.push_into_stack(lua)?;
|
||||
value.push_into_stack(&lua)?;
|
||||
protect_lua!(state, 2, 0, |state| {
|
||||
for i in (idx..=size).rev() {
|
||||
// table[i+1] = table[i]
|
||||
@@ -325,18 +310,18 @@ impl<'lua> Table<'lua> {
|
||||
}
|
||||
|
||||
/// Appends a value to the back of the table without invoking metamethods.
|
||||
pub fn raw_push<V: IntoLua<'lua>>(&self, value: V) -> Result<()> {
|
||||
pub fn raw_push(&self, value: impl IntoLua) -> Result<()> {
|
||||
#[cfg(feature = "luau")]
|
||||
self.check_readonly_write()?;
|
||||
|
||||
let lua = self.0.lua;
|
||||
let lua = self.0.lua.lock();
|
||||
let state = lua.state();
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(state);
|
||||
check_stack(state, 4)?;
|
||||
|
||||
lua.push_ref(&self.0);
|
||||
value.push_into_stack(lua)?;
|
||||
value.push_into_stack(&lua)?;
|
||||
|
||||
unsafe fn callback(state: *mut ffi::lua_State) {
|
||||
let len = ffi::lua_rawlen(state, -2) as Integer;
|
||||
@@ -353,11 +338,11 @@ impl<'lua> Table<'lua> {
|
||||
}
|
||||
|
||||
/// Removes the last element from the table and returns it, without invoking metamethods.
|
||||
pub fn raw_pop<V: FromLua<'lua>>(&self) -> Result<V> {
|
||||
pub fn raw_pop<V: FromLua>(&self) -> Result<V> {
|
||||
#[cfg(feature = "luau")]
|
||||
self.check_readonly_write()?;
|
||||
|
||||
let lua = self.0.lua;
|
||||
let lua = self.0.lua.lock();
|
||||
let state = lua.state();
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(state);
|
||||
@@ -370,7 +355,7 @@ impl<'lua> Table<'lua> {
|
||||
ffi::lua_pushnil(state);
|
||||
ffi::lua_rawseti(state, -3, len);
|
||||
|
||||
V::from_stack(-1, lua)
|
||||
V::from_stack(-1, &lua)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -381,10 +366,10 @@ impl<'lua> Table<'lua> {
|
||||
/// where n is the table length.
|
||||
///
|
||||
/// For other key types this is equivalent to setting `table[key] = nil`.
|
||||
pub fn raw_remove<K: IntoLua<'lua>>(&self, key: K) -> Result<()> {
|
||||
let lua = self.0.lua;
|
||||
pub fn raw_remove(&self, key: impl IntoLua) -> Result<()> {
|
||||
let lua = self.0.lua.lock();
|
||||
let state = lua.state();
|
||||
let key = key.into_lua(lua)?;
|
||||
let key = key.into_lua(lua.lua())?;
|
||||
match key {
|
||||
Value::Integer(idx) => {
|
||||
let size = self.raw_len() as Integer;
|
||||
@@ -418,7 +403,7 @@ impl<'lua> Table<'lua> {
|
||||
#[cfg(feature = "luau")]
|
||||
self.check_readonly_write()?;
|
||||
|
||||
let lua = self.0.lua;
|
||||
let lua = self.0.lua.lock();
|
||||
unsafe {
|
||||
#[cfg(feature = "luau")]
|
||||
ffi::lua_cleartable(lua.ref_thread(), self.0.index);
|
||||
@@ -457,12 +442,12 @@ impl<'lua> Table<'lua> {
|
||||
///
|
||||
/// [`raw_len`]: #method.raw_len
|
||||
pub fn len(&self) -> Result<Integer> {
|
||||
// Fast track
|
||||
// Fast track (skip protected call)
|
||||
if !self.has_metatable() {
|
||||
return Ok(self.raw_len() as Integer);
|
||||
}
|
||||
|
||||
let lua = self.0.lua;
|
||||
let lua = self.0.lua.lock();
|
||||
let state = lua.state();
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(state);
|
||||
@@ -475,8 +460,8 @@ impl<'lua> Table<'lua> {
|
||||
|
||||
/// Returns the result of the Lua `#` operator, without invoking the `__len` metamethod.
|
||||
pub fn raw_len(&self) -> usize {
|
||||
let ref_thread = self.0.lua.ref_thread();
|
||||
unsafe { ffi::lua_rawlen(ref_thread, self.0.index) }
|
||||
let lua = self.0.lua.lock();
|
||||
unsafe { ffi::lua_rawlen(lua.ref_thread(), self.0.index) }
|
||||
}
|
||||
|
||||
/// Returns `true` if the table is empty, without invoking metamethods.
|
||||
@@ -489,7 +474,7 @@ impl<'lua> Table<'lua> {
|
||||
}
|
||||
|
||||
// Check hash part
|
||||
let lua = self.0.lua;
|
||||
let lua = self.0.lua.lock();
|
||||
let state = lua.state();
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(state);
|
||||
@@ -508,8 +493,8 @@ impl<'lua> Table<'lua> {
|
||||
/// Returns a reference to the metatable of this table, or `None` if no metatable is set.
|
||||
///
|
||||
/// Unlike the `getmetatable` Lua function, this method ignores the `__metatable` field.
|
||||
pub fn get_metatable(&self) -> Option<Table<'lua>> {
|
||||
let lua = self.0.lua;
|
||||
pub fn get_metatable(&self) -> Option<Table> {
|
||||
let lua = self.0.lua.lock();
|
||||
let state = lua.state();
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(state);
|
||||
@@ -528,14 +513,14 @@ impl<'lua> Table<'lua> {
|
||||
///
|
||||
/// If `metatable` is `None`, the metatable is removed (if no metatable is set, this does
|
||||
/// nothing).
|
||||
pub fn set_metatable(&self, metatable: Option<Table<'lua>>) {
|
||||
pub fn set_metatable(&self, metatable: Option<Table>) {
|
||||
// Workaround to throw readonly error without returning Result
|
||||
#[cfg(feature = "luau")]
|
||||
if self.is_readonly() {
|
||||
panic!("attempt to modify a readonly table");
|
||||
}
|
||||
|
||||
let lua = self.0.lua;
|
||||
let lua = self.0.lua.lock();
|
||||
let state = lua.state();
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(state);
|
||||
@@ -555,7 +540,8 @@ impl<'lua> Table<'lua> {
|
||||
#[doc(hidden)]
|
||||
#[inline]
|
||||
pub fn has_metatable(&self) -> bool {
|
||||
let ref_thread = self.0.lua.ref_thread();
|
||||
let lua = self.0.lua.lock();
|
||||
let ref_thread = lua.ref_thread();
|
||||
unsafe {
|
||||
if ffi::lua_getmetatable(ref_thread, self.0.index) != 0 {
|
||||
ffi::lua_pop(ref_thread, 1);
|
||||
@@ -571,7 +557,8 @@ impl<'lua> Table<'lua> {
|
||||
#[cfg(any(feature = "luau", doc))]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
|
||||
pub fn set_readonly(&self, enabled: bool) {
|
||||
let ref_thread = self.0.lua.ref_thread();
|
||||
let lua = self.0.lua.lock();
|
||||
let ref_thread = lua.ref_thread();
|
||||
unsafe {
|
||||
ffi::lua_setreadonly(ref_thread, self.0.index, enabled as _);
|
||||
if !enabled {
|
||||
@@ -587,7 +574,8 @@ impl<'lua> Table<'lua> {
|
||||
#[cfg(any(feature = "luau", doc))]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
|
||||
pub fn is_readonly(&self) -> bool {
|
||||
let ref_thread = self.0.lua.ref_thread();
|
||||
let lua = self.0.lua.lock();
|
||||
let ref_thread = lua.ref_thread();
|
||||
unsafe { ffi::lua_getreadonly(ref_thread, self.0.index) != 0 }
|
||||
}
|
||||
|
||||
@@ -602,26 +590,12 @@ impl<'lua> Table<'lua> {
|
||||
self.0.to_pointer()
|
||||
}
|
||||
|
||||
/// Convert this handle to owned version.
|
||||
#[cfg(all(feature = "unstable", any(not(feature = "send"), doc)))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all(feature = "unstable", not(feature = "send")))))]
|
||||
#[inline]
|
||||
pub fn into_owned(self) -> OwnedTable {
|
||||
OwnedTable(self.0.into_owned())
|
||||
}
|
||||
|
||||
/// Consume this table and return an iterator over the pairs of the table.
|
||||
/// Returns an iterator over the pairs of the table.
|
||||
///
|
||||
/// This works like the Lua `pairs` function, but does not invoke the `__pairs` metamethod.
|
||||
///
|
||||
/// The pairs are wrapped in a [`Result`], since they are lazily converted to `K` and `V` types.
|
||||
///
|
||||
/// # Note
|
||||
///
|
||||
/// While this method consumes the `Table` object, it can not prevent code from mutating the
|
||||
/// table while the iteration is in progress. Refer to the [Lua manual] for information about
|
||||
/// the consequences of such mutation.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Iterate over all globals:
|
||||
@@ -643,9 +617,10 @@ impl<'lua> Table<'lua> {
|
||||
///
|
||||
/// [`Result`]: crate::Result
|
||||
/// [Lua manual]: http://www.lua.org/manual/5.4/manual.html#pdf-next
|
||||
pub fn pairs<K: FromLua<'lua>, V: FromLua<'lua>>(self) -> TablePairs<'lua, K, V> {
|
||||
pub fn pairs<K: FromLua, V: FromLua>(&self) -> TablePairs<K, V> {
|
||||
TablePairs {
|
||||
table: self.0,
|
||||
guard: self.0.lua.lock(),
|
||||
table: self,
|
||||
key: Some(Nil),
|
||||
_phantom: PhantomData,
|
||||
}
|
||||
@@ -657,10 +632,10 @@ impl<'lua> Table<'lua> {
|
||||
/// It does not invoke the `__pairs` metamethod.
|
||||
pub fn for_each<K, V>(&self, mut f: impl FnMut(K, V) -> Result<()>) -> Result<()>
|
||||
where
|
||||
K: FromLua<'lua>,
|
||||
V: FromLua<'lua>,
|
||||
K: FromLua,
|
||||
V: FromLua,
|
||||
{
|
||||
let lua = self.0.lua;
|
||||
let lua = self.0.lua.lock();
|
||||
let state = lua.state();
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(state);
|
||||
@@ -669,8 +644,8 @@ impl<'lua> Table<'lua> {
|
||||
lua.push_ref(&self.0);
|
||||
ffi::lua_pushnil(state);
|
||||
while ffi::lua_next(state, -2) != 0 {
|
||||
let k = K::from_stack(-2, lua)?;
|
||||
let v = V::from_stack(-1, lua)?;
|
||||
let k = K::from_stack(-2, &lua)?;
|
||||
let v = V::from_stack(-1, &lua)?;
|
||||
f(k, v)?;
|
||||
// Keep key for next iteration
|
||||
ffi::lua_pop(state, 1);
|
||||
@@ -679,18 +654,12 @@ impl<'lua> Table<'lua> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Consume this table and return an iterator over all values in the sequence part of the table.
|
||||
/// Returns an iterator over all values in the sequence part of the table.
|
||||
///
|
||||
/// The iterator will yield all values `t[1]`, `t[2]` and so on, until a `nil` value is
|
||||
/// encountered. This mirrors the behavior of Lua's `ipairs` function but does not invoke
|
||||
/// any metamethods.
|
||||
///
|
||||
/// # Note
|
||||
///
|
||||
/// While this method consumes the `Table` object, it can not prevent code from mutating the
|
||||
/// table while the iteration is in progress. Refer to the [Lua manual] for information about
|
||||
/// the consequences of such mutation.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
@@ -717,26 +686,21 @@ impl<'lua> Table<'lua> {
|
||||
/// [`pairs`]: #method.pairs
|
||||
/// [`Result`]: crate::Result
|
||||
/// [Lua manual]: http://www.lua.org/manual/5.4/manual.html#pdf-next
|
||||
pub fn sequence_values<V: FromLua<'lua>>(self) -> TableSequence<'lua, V> {
|
||||
pub fn sequence_values<V: FromLua>(&self) -> TableSequence<V> {
|
||||
TableSequence {
|
||||
table: self.0,
|
||||
guard: self.0.lua.lock(),
|
||||
table: self,
|
||||
index: 1,
|
||||
_phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
#[deprecated(since = "0.9.0", note = "use `sequence_values` instead")]
|
||||
pub fn raw_sequence_values<V: FromLua<'lua>>(self) -> TableSequence<'lua, V> {
|
||||
self.sequence_values()
|
||||
}
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
pub(crate) fn for_each_value<V>(&self, mut f: impl FnMut(V) -> Result<()>) -> Result<()>
|
||||
where
|
||||
V: FromLua<'lua>,
|
||||
V: FromLua,
|
||||
{
|
||||
let lua = self.0.lua;
|
||||
let lua = self.0.lua.lock();
|
||||
let state = lua.state();
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(state);
|
||||
@@ -746,7 +710,7 @@ impl<'lua> Table<'lua> {
|
||||
let len = ffi::lua_rawlen(state, -1);
|
||||
for i in 1..=len {
|
||||
ffi::lua_rawgeti(state, -1, i as _);
|
||||
f(V::from_stack(-1, lua)?)?;
|
||||
f(V::from_stack(-1, &lua)?)?;
|
||||
ffi::lua_pop(state, 1);
|
||||
}
|
||||
}
|
||||
@@ -755,18 +719,18 @@ impl<'lua> Table<'lua> {
|
||||
|
||||
/// Sets element value at position `idx` without invoking metamethods.
|
||||
#[doc(hidden)]
|
||||
pub fn raw_seti<V: IntoLua<'lua>>(&self, idx: usize, value: V) -> Result<()> {
|
||||
pub fn raw_seti(&self, idx: usize, value: impl IntoLua) -> Result<()> {
|
||||
#[cfg(feature = "luau")]
|
||||
self.check_readonly_write()?;
|
||||
|
||||
let lua = self.0.lua;
|
||||
let lua = self.0.lua.lock();
|
||||
let state = lua.state();
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(state);
|
||||
check_stack(state, 5)?;
|
||||
|
||||
lua.push_ref(&self.0);
|
||||
value.push_into_stack(lua)?;
|
||||
value.push_into_stack(&lua)?;
|
||||
|
||||
let idx = idx.try_into().unwrap();
|
||||
if lua.unlikely_memory_error() {
|
||||
@@ -780,7 +744,7 @@ impl<'lua> Table<'lua> {
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
pub(crate) fn is_array(&self) -> bool {
|
||||
let lua = self.0.lua;
|
||||
let lua = self.0.lua.lock();
|
||||
let state = lua.state();
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(state);
|
||||
@@ -812,9 +776,8 @@ impl<'lua> Table<'lua> {
|
||||
) -> fmt::Result {
|
||||
visited.insert(self.to_pointer());
|
||||
|
||||
let t = self.clone();
|
||||
// Collect key/value pairs into a vector so we can sort them
|
||||
let mut pairs = t.pairs::<Value, Value>().flatten().collect::<Vec<_>>();
|
||||
let mut pairs = self.pairs::<Value, Value>().flatten().collect::<Vec<_>>();
|
||||
// Sort keys
|
||||
pairs.sort_by(|(a, _), (b, _)| a.cmp(b));
|
||||
if pairs.is_empty() {
|
||||
@@ -832,7 +795,7 @@ impl<'lua> Table<'lua> {
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Table<'_> {
|
||||
impl fmt::Debug for Table {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
if fmt.alternate() {
|
||||
return self.fmt_pretty(fmt, 0, &mut HashSet::new());
|
||||
@@ -841,25 +804,25 @@ impl fmt::Debug for Table<'_> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> PartialEq for Table<'lua> {
|
||||
impl PartialEq for Table {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.0 == other.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> AsRef<Table<'lua>> for Table<'lua> {
|
||||
impl AsRef<Table> for Table {
|
||||
#[inline]
|
||||
fn as_ref(&self) -> &Self {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua, T> PartialEq<[T]> for Table<'lua>
|
||||
impl<T> PartialEq<[T]> for Table
|
||||
where
|
||||
T: IntoLua<'lua> + Clone,
|
||||
T: IntoLua + Clone,
|
||||
{
|
||||
fn eq(&self, other: &[T]) -> bool {
|
||||
let lua = self.0.lua;
|
||||
let lua = self.0.lua.lock();
|
||||
let state = lua.state();
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(state);
|
||||
@@ -874,7 +837,7 @@ where
|
||||
if val == Nil {
|
||||
return i == other.len();
|
||||
}
|
||||
match other.get(i).map(|v| v.clone().into_lua(lua)) {
|
||||
match other.get(i).map(|v| v.clone().into_lua(lua.lua())) {
|
||||
Some(Ok(other_val)) if val == other_val => continue,
|
||||
_ => return false,
|
||||
}
|
||||
@@ -884,9 +847,9 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua, T> PartialEq<&[T]> for Table<'lua>
|
||||
impl<T> PartialEq<&[T]> for Table
|
||||
where
|
||||
T: IntoLua<'lua> + Clone,
|
||||
T: IntoLua + Clone,
|
||||
{
|
||||
#[inline]
|
||||
fn eq(&self, other: &&[T]) -> bool {
|
||||
@@ -894,9 +857,9 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua, T, const N: usize> PartialEq<[T; N]> for Table<'lua>
|
||||
impl<T, const N: usize> PartialEq<[T; N]> for Table
|
||||
where
|
||||
T: IntoLua<'lua> + Clone,
|
||||
T: IntoLua + Clone,
|
||||
{
|
||||
#[inline]
|
||||
fn eq(&self, other: &[T; N]) -> bool {
|
||||
@@ -904,163 +867,94 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
/// An extension trait for `Table`s that provides a variety of convenient functionality.
|
||||
pub trait TableExt<'lua>: Sealed {
|
||||
/// Calls the table as function assuming it has `__call` metamethod.
|
||||
///
|
||||
/// The metamethod is called with the table as its first argument, followed by the passed arguments.
|
||||
fn call<A, R>(&self, args: A) -> Result<R>
|
||||
where
|
||||
A: IntoLuaMulti<'lua>,
|
||||
R: FromLuaMulti<'lua>;
|
||||
impl ObjectLike for Table {
|
||||
#[inline]
|
||||
fn get<V: FromLua>(&self, key: impl IntoLua) -> Result<V> {
|
||||
self.get(key)
|
||||
}
|
||||
|
||||
/// Asynchronously calls the table as function assuming it has `__call` metamethod.
|
||||
///
|
||||
/// The metamethod is called with the table as its first argument, followed by the passed arguments.
|
||||
#[cfg(feature = "async")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
|
||||
fn call_async<A, R>(&self, args: A) -> LocalBoxFuture<'lua, Result<R>>
|
||||
where
|
||||
A: IntoLuaMulti<'lua>,
|
||||
R: FromLuaMulti<'lua> + 'lua;
|
||||
#[inline]
|
||||
fn set(&self, key: impl IntoLua, value: impl IntoLua) -> Result<()> {
|
||||
self.set(key, value)
|
||||
}
|
||||
|
||||
/// Gets the function associated to `key` from the table and executes it,
|
||||
/// passing the table itself along with `args` as function arguments.
|
||||
///
|
||||
/// This is a shortcut for
|
||||
/// `table.get::<_, Function>(key)?.call((table.clone(), arg1, ..., argN))`
|
||||
///
|
||||
/// This might invoke the `__index` metamethod.
|
||||
fn call_method<A, R>(&self, name: &str, args: A) -> Result<R>
|
||||
#[inline]
|
||||
fn call<R>(&self, args: impl IntoLuaMulti) -> Result<R>
|
||||
where
|
||||
A: IntoLuaMulti<'lua>,
|
||||
R: FromLuaMulti<'lua>;
|
||||
|
||||
/// Gets the function associated to `key` from the table and executes it,
|
||||
/// passing `args` as function arguments.
|
||||
///
|
||||
/// This is a shortcut for
|
||||
/// `table.get::<_, Function>(key)?.call(args)`
|
||||
///
|
||||
/// This might invoke the `__index` metamethod.
|
||||
fn call_function<A, R>(&self, name: &str, args: A) -> Result<R>
|
||||
where
|
||||
A: IntoLuaMulti<'lua>,
|
||||
R: FromLuaMulti<'lua>;
|
||||
|
||||
/// Gets the function associated to `key` from the table and asynchronously executes it,
|
||||
/// passing the table itself along with `args` as function arguments and returning Future.
|
||||
///
|
||||
/// Requires `feature = "async"`
|
||||
///
|
||||
/// This might invoke the `__index` metamethod.
|
||||
#[cfg(feature = "async")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
|
||||
fn call_async_method<A, R>(&self, name: &str, args: A) -> LocalBoxFuture<'lua, Result<R>>
|
||||
where
|
||||
A: IntoLuaMulti<'lua>,
|
||||
R: FromLuaMulti<'lua> + 'lua;
|
||||
|
||||
/// Gets the function associated to `key` from the table and asynchronously executes it,
|
||||
/// passing `args` as function arguments and returning Future.
|
||||
///
|
||||
/// Requires `feature = "async"`
|
||||
///
|
||||
/// This might invoke the `__index` metamethod.
|
||||
#[cfg(feature = "async")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
|
||||
fn call_async_function<A, R>(&self, name: &str, args: A) -> LocalBoxFuture<'lua, Result<R>>
|
||||
where
|
||||
A: IntoLuaMulti<'lua>,
|
||||
R: FromLuaMulti<'lua> + 'lua;
|
||||
}
|
||||
|
||||
impl<'lua> TableExt<'lua> for Table<'lua> {
|
||||
fn call<A, R>(&self, args: A) -> Result<R>
|
||||
where
|
||||
A: IntoLuaMulti<'lua>,
|
||||
R: FromLuaMulti<'lua>,
|
||||
R: FromLuaMulti,
|
||||
{
|
||||
// Convert table to a function and call via pcall that respects the `__call` metamethod.
|
||||
Function(self.0.clone()).call(args)
|
||||
Function(self.0.copy()).call(args)
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
fn call_async<A, R>(&self, args: A) -> LocalBoxFuture<'lua, Result<R>>
|
||||
#[inline]
|
||||
fn call_async<R>(&self, args: impl IntoLuaMulti) -> impl Future<Output = Result<R>>
|
||||
where
|
||||
A: IntoLuaMulti<'lua>,
|
||||
R: FromLuaMulti<'lua> + 'lua,
|
||||
R: FromLuaMulti,
|
||||
{
|
||||
let args = match args.into_lua_multi(self.0.lua) {
|
||||
Ok(args) => args,
|
||||
Err(e) => return Box::pin(future::err(e)),
|
||||
};
|
||||
let func = Function(self.0.clone());
|
||||
Box::pin(async move { func.call_async(args).await })
|
||||
Function(self.0.copy()).call_async(args)
|
||||
}
|
||||
|
||||
fn call_method<A, R>(&self, name: &str, args: A) -> Result<R>
|
||||
#[inline]
|
||||
fn call_method<R>(&self, name: &str, args: impl IntoLuaMulti) -> Result<R>
|
||||
where
|
||||
A: IntoLuaMulti<'lua>,
|
||||
R: FromLuaMulti<'lua>,
|
||||
R: FromLuaMulti,
|
||||
{
|
||||
let lua = self.0.lua;
|
||||
let mut args = args.into_lua_multi(lua)?;
|
||||
args.push_front(Value::Table(self.clone()));
|
||||
self.get::<_, Function>(name)?.call(args)
|
||||
}
|
||||
|
||||
fn call_function<A, R>(&self, name: &str, args: A) -> Result<R>
|
||||
where
|
||||
A: IntoLuaMulti<'lua>,
|
||||
R: FromLuaMulti<'lua>,
|
||||
{
|
||||
self.get::<_, Function>(name)?.call(args)
|
||||
self.call_function(name, (self, args))
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
fn call_async_method<A, R>(&self, name: &str, args: A) -> LocalBoxFuture<'lua, Result<R>>
|
||||
fn call_async_method<R>(&self, name: &str, args: impl IntoLuaMulti) -> impl Future<Output = Result<R>>
|
||||
where
|
||||
A: IntoLuaMulti<'lua>,
|
||||
R: FromLuaMulti<'lua> + 'lua,
|
||||
R: FromLuaMulti,
|
||||
{
|
||||
let lua = self.0.lua;
|
||||
let mut args = match args.into_lua_multi(lua) {
|
||||
Ok(args) => args,
|
||||
Err(e) => return Box::pin(future::err(e)),
|
||||
};
|
||||
args.push_front(Value::Table(self.clone()));
|
||||
self.call_async_function(name, args)
|
||||
self.call_async_function(name, (self, args))
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
fn call_async_function<A, R>(&self, name: &str, args: A) -> LocalBoxFuture<'lua, Result<R>>
|
||||
where
|
||||
A: IntoLuaMulti<'lua>,
|
||||
R: FromLuaMulti<'lua> + 'lua,
|
||||
{
|
||||
let lua = self.0.lua;
|
||||
let args = match args.into_lua_multi(lua) {
|
||||
Ok(args) => args,
|
||||
Err(e) => return Box::pin(future::err(e)),
|
||||
};
|
||||
match self.get::<_, Function>(name) {
|
||||
Ok(func) => Box::pin(async move { func.call_async(args).await }),
|
||||
Err(e) => Box::pin(future::err(e)),
|
||||
#[inline]
|
||||
fn call_function<R: FromLuaMulti>(&self, name: &str, args: impl IntoLuaMulti) -> Result<R> {
|
||||
match self.get(name)? {
|
||||
Value::Function(func) => func.call(args),
|
||||
val => {
|
||||
let msg = format!("attempt to call a {} value (function '{name}')", val.type_name());
|
||||
Err(Error::runtime(msg))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
#[inline]
|
||||
fn call_async_function<R>(&self, name: &str, args: impl IntoLuaMulti) -> impl Future<Output = Result<R>>
|
||||
where
|
||||
R: FromLuaMulti,
|
||||
{
|
||||
match self.get(name) {
|
||||
Ok(Value::Function(func)) => Either::Left(func.call_async(args)),
|
||||
Ok(val) => {
|
||||
let msg = format!("attempt to call a {} value (function '{name}')", val.type_name());
|
||||
Either::Right(future::ready(Err(Error::RuntimeError(msg))))
|
||||
}
|
||||
Err(err) => Either::Right(future::ready(Err(err))),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn to_string(&self) -> Result<StdString> {
|
||||
Value::Table(Table(self.0.copy())).to_string()
|
||||
}
|
||||
}
|
||||
|
||||
/// A wrapped [`Table`] with customized serialization behavior.
|
||||
#[cfg(feature = "serialize")]
|
||||
pub(crate) struct SerializableTable<'a, 'lua> {
|
||||
table: &'a Table<'lua>,
|
||||
pub(crate) struct SerializableTable<'a> {
|
||||
table: &'a Table,
|
||||
options: crate::serde::de::Options,
|
||||
visited: Rc<RefCell<FxHashSet<*const c_void>>>,
|
||||
}
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
impl<'lua> Serialize for Table<'lua> {
|
||||
impl Serialize for Table {
|
||||
#[inline]
|
||||
fn serialize<S: Serializer>(&self, serializer: S) -> StdResult<S::Ok, S::Error> {
|
||||
SerializableTable::new(self, Default::default(), Default::default()).serialize(serializer)
|
||||
@@ -1068,10 +962,10 @@ impl<'lua> Serialize for Table<'lua> {
|
||||
}
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
impl<'a, 'lua> SerializableTable<'a, 'lua> {
|
||||
impl<'a> SerializableTable<'a> {
|
||||
#[inline]
|
||||
pub(crate) fn new(
|
||||
table: &'a Table<'lua>,
|
||||
table: &'a Table,
|
||||
options: crate::serde::de::Options,
|
||||
visited: Rc<RefCell<FxHashSet<*const c_void>>>,
|
||||
) -> Self {
|
||||
@@ -1084,7 +978,7 @@ impl<'a, 'lua> SerializableTable<'a, 'lua> {
|
||||
}
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
impl<'a, 'lua> Serialize for SerializableTable<'a, 'lua> {
|
||||
impl<'a> Serialize for SerializableTable<'a> {
|
||||
fn serialize<S>(&self, serializer: S) -> StdResult<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
@@ -1118,7 +1012,7 @@ impl<'a, 'lua> Serialize for SerializableTable<'a, 'lua> {
|
||||
seq.serialize_element(&SerializableValue::new(&value, options, Some(visited)))
|
||||
.map_err(|err| {
|
||||
serialize_err = Some(err);
|
||||
Error::SerializeError(String::new())
|
||||
Error::SerializeError(StdString::new())
|
||||
})
|
||||
});
|
||||
convert_result(res, serialize_err)?;
|
||||
@@ -1143,7 +1037,7 @@ impl<'a, 'lua> Serialize for SerializableTable<'a, 'lua> {
|
||||
)
|
||||
.map_err(|err| {
|
||||
serialize_err = Some(err);
|
||||
Error::SerializeError(String::new())
|
||||
Error::SerializeError(StdString::new())
|
||||
})
|
||||
};
|
||||
|
||||
@@ -1151,7 +1045,7 @@ impl<'a, 'lua> Serialize for SerializableTable<'a, 'lua> {
|
||||
// Fast track
|
||||
self.table.for_each(process_pair)
|
||||
} else {
|
||||
MapPairs::new(self.table.clone(), self.options.sort_keys)
|
||||
MapPairs::new(self.table, self.options.sort_keys)
|
||||
.map_err(serde::ser::Error::custom)?
|
||||
.try_for_each(|kv| {
|
||||
let (key, value) = kv?;
|
||||
@@ -1168,39 +1062,40 @@ impl<'a, 'lua> Serialize for SerializableTable<'a, 'lua> {
|
||||
/// This struct is created by the [`Table::pairs`] method.
|
||||
///
|
||||
/// [`Table::pairs`]: crate::Table::pairs
|
||||
pub struct TablePairs<'lua, K, V> {
|
||||
table: LuaRef<'lua>,
|
||||
key: Option<Value<'lua>>,
|
||||
pub struct TablePairs<'a, K, V> {
|
||||
guard: LuaGuard,
|
||||
table: &'a Table,
|
||||
key: Option<Value>,
|
||||
_phantom: PhantomData<(K, V)>,
|
||||
}
|
||||
|
||||
impl<'lua, K, V> Iterator for TablePairs<'lua, K, V>
|
||||
impl<'a, K, V> Iterator for TablePairs<'a, K, V>
|
||||
where
|
||||
K: FromLua<'lua>,
|
||||
V: FromLua<'lua>,
|
||||
K: FromLua,
|
||||
V: FromLua,
|
||||
{
|
||||
type Item = Result<(K, V)>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
if let Some(prev_key) = self.key.take() {
|
||||
let lua = self.table.lua;
|
||||
let lua: &RawLua = &self.guard;
|
||||
let state = lua.state();
|
||||
|
||||
let res = (|| unsafe {
|
||||
let _sg = StackGuard::new(state);
|
||||
check_stack(state, 5)?;
|
||||
|
||||
lua.push_ref(&self.table);
|
||||
lua.push_value(prev_key)?;
|
||||
lua.push_ref(&self.table.0);
|
||||
lua.push_value(&prev_key)?;
|
||||
|
||||
// It must be safe to call `lua_next` unprotected as deleting a key from a table is
|
||||
// a permitted operation.
|
||||
// It fails only if the key is not found (never existed) which seems impossible scenario.
|
||||
if ffi::lua_next(state, -2) != 0 {
|
||||
let key = lua.stack_value(-2);
|
||||
let key = lua.stack_value(-2, None);
|
||||
Ok(Some((
|
||||
key.clone(),
|
||||
K::from_lua(key, lua)?,
|
||||
K::from_lua(key, lua.lua())?,
|
||||
V::from_stack(-1, lua)?,
|
||||
)))
|
||||
} else {
|
||||
@@ -1227,21 +1122,21 @@ where
|
||||
/// This struct is created by the [`Table::sequence_values`] method.
|
||||
///
|
||||
/// [`Table::sequence_values`]: crate::Table::sequence_values
|
||||
pub struct TableSequence<'lua, V> {
|
||||
// TODO: Use `&Table`
|
||||
table: LuaRef<'lua>,
|
||||
pub struct TableSequence<'a, V> {
|
||||
guard: LuaGuard,
|
||||
table: &'a Table,
|
||||
index: Integer,
|
||||
_phantom: PhantomData<V>,
|
||||
}
|
||||
|
||||
impl<'lua, V> Iterator for TableSequence<'lua, V>
|
||||
impl<'a, V> Iterator for TableSequence<'a, V>
|
||||
where
|
||||
V: FromLua<'lua>,
|
||||
V: FromLua,
|
||||
{
|
||||
type Item = Result<V>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
let lua = self.table.lua;
|
||||
let lua: &RawLua = &self.guard;
|
||||
let state = lua.state();
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(state);
|
||||
@@ -1249,7 +1144,7 @@ where
|
||||
return Some(Err(err));
|
||||
}
|
||||
|
||||
lua.push_ref(&self.table);
|
||||
lua.push_ref(&self.table.0);
|
||||
match ffi::lua_rawgeti(state, -1, self.index) {
|
||||
ffi::LUA_TNIL => None,
|
||||
_ => {
|
||||
@@ -1265,8 +1160,8 @@ where
|
||||
mod assertions {
|
||||
use super::*;
|
||||
|
||||
#[cfg(not(feature = "send"))]
|
||||
static_assertions::assert_not_impl_any!(Table: Send);
|
||||
|
||||
#[cfg(feature = "unstable")]
|
||||
static_assertions::assert_not_impl_any!(OwnedTable: Send);
|
||||
#[cfg(feature = "send")]
|
||||
static_assertions::assert_impl_all!(Table: Send, Sync);
|
||||
}
|
||||
|
||||
+97
-150
@@ -2,8 +2,9 @@ use std::os::raw::{c_int, c_void};
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
#[allow(unused)]
|
||||
use crate::lua::Lua;
|
||||
use crate::types::LuaRef;
|
||||
use crate::state::Lua;
|
||||
use crate::state::RawLua;
|
||||
use crate::types::ValueRef;
|
||||
use crate::util::{check_stack, error_traceback_thread, pop_error, StackGuard};
|
||||
use crate::value::{FromLuaMulti, IntoLuaMulti};
|
||||
|
||||
@@ -15,7 +16,6 @@ use crate::{
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
use {
|
||||
crate::value::MultiValue,
|
||||
futures_util::stream::Stream,
|
||||
std::{
|
||||
future::Future,
|
||||
@@ -29,45 +29,26 @@ use {
|
||||
/// Status of a Lua thread (coroutine).
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
pub enum ThreadStatus {
|
||||
/// The thread was just created, or is suspended because it has called `coroutine.yield`.
|
||||
/// The thread was just created or is suspended (yielded).
|
||||
///
|
||||
/// If a thread is in this state, it can be resumed by calling [`Thread::resume`].
|
||||
///
|
||||
/// [`Thread::resume`]: crate::Thread::resume
|
||||
Resumable,
|
||||
/// Either the thread has finished executing, or the thread is currently running.
|
||||
Unresumable,
|
||||
/// The thread is currently running.
|
||||
Running,
|
||||
/// The thread has finished executing.
|
||||
Finished,
|
||||
/// The thread has raised a Lua error during execution.
|
||||
Error,
|
||||
}
|
||||
|
||||
/// Handle to an internal Lua thread (coroutine).
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Thread<'lua>(pub(crate) LuaRef<'lua>, pub(crate) *mut ffi::lua_State);
|
||||
pub struct Thread(pub(crate) ValueRef, pub(crate) *mut ffi::lua_State);
|
||||
|
||||
/// Owned handle to an internal Lua thread (coroutine).
|
||||
///
|
||||
/// The owned handle holds a *strong* reference to the current Lua instance.
|
||||
/// Be warned, if you place it into a Lua type (eg. [`UserData`] or a Rust callback), it is *very easy*
|
||||
/// to accidentally cause reference cycles that would prevent destroying Lua instance.
|
||||
///
|
||||
/// [`UserData`]: crate::UserData
|
||||
#[cfg(feature = "unstable")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "unstable")))]
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct OwnedThread(
|
||||
pub(crate) crate::types::LuaOwnedRef,
|
||||
pub(crate) *mut ffi::lua_State,
|
||||
);
|
||||
|
||||
#[cfg(feature = "unstable")]
|
||||
impl OwnedThread {
|
||||
/// Get borrowed handle to the underlying Lua table.
|
||||
#[cfg_attr(feature = "send", allow(unused))]
|
||||
pub const fn to_ref(&self) -> Thread {
|
||||
Thread(self.0.to_ref(), self.1)
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "send")]
|
||||
unsafe impl Send for Thread {}
|
||||
#[cfg(feature = "send")]
|
||||
unsafe impl Sync for Thread {}
|
||||
|
||||
/// Thread (coroutine) representation as an async [`Future`] or [`Stream`].
|
||||
///
|
||||
@@ -78,20 +59,15 @@ impl OwnedThread {
|
||||
#[cfg(feature = "async")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
|
||||
#[must_use = "futures do nothing unless you `.await` or poll them"]
|
||||
pub struct AsyncThread<'lua, R> {
|
||||
thread: Thread<'lua>,
|
||||
init_args: Option<Result<MultiValue<'lua>>>,
|
||||
pub struct AsyncThread<A, R> {
|
||||
thread: Thread,
|
||||
init_args: Option<A>,
|
||||
ret: PhantomData<R>,
|
||||
recycle: bool,
|
||||
}
|
||||
|
||||
impl<'lua> Thread<'lua> {
|
||||
impl Thread {
|
||||
#[inline(always)]
|
||||
pub(crate) fn new(r#ref: LuaRef<'lua>) -> Self {
|
||||
let state = unsafe { ffi::lua_tothread(r#ref.lua.ref_thread(), r#ref.index) };
|
||||
Thread(r#ref, state)
|
||||
}
|
||||
|
||||
const fn state(&self) -> *mut ffi::lua_State {
|
||||
self.1
|
||||
}
|
||||
@@ -126,50 +102,48 @@ impl<'lua> Thread<'lua> {
|
||||
/// end)
|
||||
/// "#).eval()?;
|
||||
///
|
||||
/// assert_eq!(thread.resume::<_, u32>(42)?, 123);
|
||||
/// assert_eq!(thread.resume::<_, u32>(43)?, 987);
|
||||
/// assert_eq!(thread.resume::<u32>(42)?, 123);
|
||||
/// assert_eq!(thread.resume::<u32>(43)?, 987);
|
||||
///
|
||||
/// // The coroutine has now returned, so `resume` will fail
|
||||
/// match thread.resume::<_, u32>(()) {
|
||||
/// Err(Error::CoroutineInactive) => {},
|
||||
/// match thread.resume::<u32>(()) {
|
||||
/// Err(Error::CoroutineUnresumable) => {},
|
||||
/// unexpected => panic!("unexpected result {:?}", unexpected),
|
||||
/// }
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn resume<A, R>(&self, args: A) -> Result<R>
|
||||
pub fn resume<R>(&self, args: impl IntoLuaMulti) -> Result<R>
|
||||
where
|
||||
A: IntoLuaMulti<'lua>,
|
||||
R: FromLuaMulti<'lua>,
|
||||
R: FromLuaMulti,
|
||||
{
|
||||
if self.status() != ThreadStatus::Resumable {
|
||||
return Err(Error::CoroutineInactive);
|
||||
let lua = self.0.lua.lock();
|
||||
if self.status_inner(&lua) != ThreadStatus::Resumable {
|
||||
return Err(Error::CoroutineUnresumable);
|
||||
}
|
||||
|
||||
let lua = self.0.lua;
|
||||
let state = lua.state();
|
||||
let thread_state = self.state();
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(state);
|
||||
let _thread_sg = StackGuard::with_top(thread_state, 0);
|
||||
|
||||
let nresults = self.resume_inner(args)?;
|
||||
let nresults = self.resume_inner(&lua, args)?;
|
||||
check_stack(state, nresults + 1)?;
|
||||
ffi::lua_xmove(thread_state, state, nresults);
|
||||
|
||||
R::from_stack_multi(nresults, lua)
|
||||
R::from_stack_multi(nresults, &lua)
|
||||
}
|
||||
}
|
||||
|
||||
/// Resumes execution of this thread.
|
||||
///
|
||||
/// It's similar to `resume()` but leaves `nresults` values on the thread stack.
|
||||
unsafe fn resume_inner<A: IntoLuaMulti<'lua>>(&self, args: A) -> Result<c_int> {
|
||||
let lua = self.0.lua;
|
||||
unsafe fn resume_inner(&self, lua: &RawLua, args: impl IntoLuaMulti) -> Result<c_int> {
|
||||
let state = lua.state();
|
||||
let thread_state = self.state();
|
||||
|
||||
let nargs = args.push_into_stack_multi(lua)?;
|
||||
let nargs = args.push_into_stack_multi(&lua)?;
|
||||
if nargs > 0 {
|
||||
check_stack(thread_state, nargs)?;
|
||||
ffi::lua_xmove(state, thread_state, nargs);
|
||||
@@ -183,10 +157,7 @@ impl<'lua> Thread<'lua> {
|
||||
return Err(pop_error(thread_state, ret));
|
||||
}
|
||||
check_stack(state, 3)?;
|
||||
protect_lua!(state, 0, 1, |state| error_traceback_thread(
|
||||
state,
|
||||
thread_state
|
||||
))?;
|
||||
protect_lua!(state, 0, 1, |state| error_traceback_thread(state, thread_state))?;
|
||||
return Err(pop_error(state, ret));
|
||||
}
|
||||
|
||||
@@ -195,20 +166,23 @@ impl<'lua> Thread<'lua> {
|
||||
|
||||
/// Gets the status of the thread.
|
||||
pub fn status(&self) -> ThreadStatus {
|
||||
self.status_inner(&self.0.lua.lock())
|
||||
}
|
||||
|
||||
/// Gets the status of the thread (internal implementation).
|
||||
pub(crate) fn status_inner(&self, lua: &RawLua) -> ThreadStatus {
|
||||
let thread_state = self.state();
|
||||
if thread_state == self.0.lua.state() {
|
||||
// The coroutine is currently running
|
||||
return ThreadStatus::Unresumable;
|
||||
if thread_state == lua.state() {
|
||||
// The thread is currently running
|
||||
return ThreadStatus::Running;
|
||||
}
|
||||
unsafe {
|
||||
let status = ffi::lua_status(thread_state);
|
||||
if status != ffi::LUA_OK && status != ffi::LUA_YIELD {
|
||||
ThreadStatus::Error
|
||||
} else if status == ffi::LUA_YIELD || ffi::lua_gettop(thread_state) > 0 {
|
||||
ThreadStatus::Resumable
|
||||
} else {
|
||||
ThreadStatus::Unresumable
|
||||
}
|
||||
let status = unsafe { ffi::lua_status(thread_state) };
|
||||
if status != ffi::LUA_OK && status != ffi::LUA_YIELD {
|
||||
ThreadStatus::Error
|
||||
} else if status == ffi::LUA_YIELD || unsafe { ffi::lua_gettop(thread_state) > 0 } {
|
||||
ThreadStatus::Resumable
|
||||
} else {
|
||||
ThreadStatus::Finished
|
||||
}
|
||||
}
|
||||
|
||||
@@ -222,7 +196,7 @@ impl<'lua> Thread<'lua> {
|
||||
where
|
||||
F: Fn(&Lua, Debug) -> Result<()> + MaybeSend + 'static,
|
||||
{
|
||||
let lua = self.0.lua;
|
||||
let lua = self.0.lua.lock();
|
||||
unsafe {
|
||||
lua.set_thread_hook(self.state(), triggers, callback);
|
||||
}
|
||||
@@ -244,12 +218,13 @@ impl<'lua> Thread<'lua> {
|
||||
/// [Lua 5.4]: https://www.lua.org/manual/5.4/manual.html#lua_closethread
|
||||
#[cfg(any(feature = "lua54", feature = "luau"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(any(feature = "lua54", feature = "luau"))))]
|
||||
pub fn reset(&self, func: crate::function::Function<'lua>) -> Result<()> {
|
||||
let lua = self.0.lua;
|
||||
let thread_state = self.state();
|
||||
if thread_state == lua.state() {
|
||||
pub fn reset(&self, func: crate::function::Function) -> Result<()> {
|
||||
let lua = self.0.lua.lock();
|
||||
if self.status_inner(&lua) == ThreadStatus::Running {
|
||||
return Err(Error::runtime("cannot reset a running thread"));
|
||||
}
|
||||
|
||||
let thread_state = self.state();
|
||||
unsafe {
|
||||
#[cfg(all(feature = "lua54", not(feature = "vendored")))]
|
||||
let status = ffi::lua_resetthread(thread_state);
|
||||
@@ -296,7 +271,7 @@ impl<'lua> Thread<'lua> {
|
||||
///
|
||||
/// ```
|
||||
/// # use mlua::{Lua, Result, Thread};
|
||||
/// use futures::stream::TryStreamExt;
|
||||
/// use futures_util::stream::TryStreamExt;
|
||||
/// # #[tokio::main]
|
||||
/// # async fn main() -> Result<()> {
|
||||
/// # let lua = Lua::new();
|
||||
@@ -310,7 +285,7 @@ impl<'lua> Thread<'lua> {
|
||||
/// end)
|
||||
/// "#).eval()?;
|
||||
///
|
||||
/// let mut stream = thread.into_async::<_, i64>(1);
|
||||
/// let mut stream = thread.into_async::<i64>(1);
|
||||
/// let mut sum = 0;
|
||||
/// while let Some(n) = stream.try_next().await? {
|
||||
/// sum += n;
|
||||
@@ -323,12 +298,10 @@ impl<'lua> Thread<'lua> {
|
||||
/// ```
|
||||
#[cfg(feature = "async")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
|
||||
pub fn into_async<A, R>(self, args: A) -> AsyncThread<'lua, R>
|
||||
pub fn into_async<R>(self, args: impl IntoLuaMulti) -> AsyncThread<impl IntoLuaMulti, R>
|
||||
where
|
||||
A: IntoLuaMulti<'lua>,
|
||||
R: FromLuaMulti<'lua>,
|
||||
R: FromLuaMulti,
|
||||
{
|
||||
let args = args.into_lua_multi(self.0.lua);
|
||||
AsyncThread {
|
||||
thread: self,
|
||||
init_args: Some(args),
|
||||
@@ -355,14 +328,14 @@ impl<'lua> Thread<'lua> {
|
||||
/// let lua = Lua::new();
|
||||
/// let thread = lua.create_thread(lua.create_function(|lua2, ()| {
|
||||
/// lua2.load("var = 123").exec()?;
|
||||
/// assert_eq!(lua2.globals().get::<_, u32>("var")?, 123);
|
||||
/// assert_eq!(lua2.globals().get::<u32>("var")?, 123);
|
||||
/// Ok(())
|
||||
/// })?)?;
|
||||
/// thread.sandbox()?;
|
||||
/// thread.resume(())?;
|
||||
///
|
||||
/// // The global environment should be unchanged
|
||||
/// assert_eq!(lua.globals().get::<_, Option<u32>>("var")?, None);
|
||||
/// assert_eq!(lua.globals().get::<Option<u32>>("var")?, None);
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
@@ -372,7 +345,7 @@ impl<'lua> Thread<'lua> {
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
|
||||
#[doc(hidden)]
|
||||
pub fn sandbox(&self) -> Result<()> {
|
||||
let lua = self.0.lua;
|
||||
let lua = self.0.lua.lock();
|
||||
let state = lua.state();
|
||||
let thread_state = self.state();
|
||||
unsafe {
|
||||
@@ -391,44 +364,16 @@ impl<'lua> Thread<'lua> {
|
||||
pub fn to_pointer(&self) -> *const c_void {
|
||||
self.0.to_pointer()
|
||||
}
|
||||
|
||||
/// Convert this handle to owned version.
|
||||
#[cfg(all(feature = "unstable", any(not(feature = "send"), doc)))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all(feature = "unstable", not(feature = "send")))))]
|
||||
#[inline]
|
||||
pub fn into_owned(self) -> OwnedThread {
|
||||
OwnedThread(self.0.into_owned(), self.1)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> PartialEq for Thread<'lua> {
|
||||
impl PartialEq for Thread {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.0 == other.0
|
||||
}
|
||||
}
|
||||
|
||||
// Additional shortcuts
|
||||
#[cfg(feature = "unstable")]
|
||||
impl OwnedThread {
|
||||
/// Resumes execution of this thread.
|
||||
///
|
||||
/// See [`Thread::resume()`] for more details.
|
||||
pub fn resume<'lua, A, R>(&'lua self, args: A) -> Result<R>
|
||||
where
|
||||
A: IntoLuaMulti<'lua>,
|
||||
R: FromLuaMulti<'lua>,
|
||||
{
|
||||
self.to_ref().resume(args)
|
||||
}
|
||||
|
||||
/// Gets the status of the thread.
|
||||
pub fn status(&self) -> ThreadStatus {
|
||||
self.to_ref().status()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
impl<'lua, R> AsyncThread<'lua, R> {
|
||||
impl<A, R> AsyncThread<A, R> {
|
||||
#[inline]
|
||||
pub(crate) fn set_recyclable(&mut self, recyclable: bool) {
|
||||
self.recycle = recyclable;
|
||||
@@ -437,19 +382,20 @@ impl<'lua, R> AsyncThread<'lua, R> {
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
#[cfg(any(feature = "lua54", feature = "luau"))]
|
||||
impl<'lua, R> Drop for AsyncThread<'lua, R> {
|
||||
impl<A, R> Drop for AsyncThread<A, R> {
|
||||
fn drop(&mut self) {
|
||||
if self.recycle {
|
||||
unsafe {
|
||||
let lua = self.thread.0.lua;
|
||||
// For Lua 5.4 this also closes all pending to-be-closed variables
|
||||
if !lua.recycle_thread(&mut self.thread) {
|
||||
#[cfg(feature = "lua54")]
|
||||
if self.thread.status() == ThreadStatus::Error {
|
||||
#[cfg(not(feature = "vendored"))]
|
||||
ffi::lua_resetthread(self.thread.state());
|
||||
#[cfg(feature = "vendored")]
|
||||
ffi::lua_closethread(self.thread.state(), lua.state());
|
||||
if let Some(lua) = self.thread.0.lua.try_lock() {
|
||||
unsafe {
|
||||
// For Lua 5.4 this also closes all pending to-be-closed variables
|
||||
if !lua.recycle_thread(&mut self.thread) {
|
||||
#[cfg(feature = "lua54")]
|
||||
if self.thread.status_inner(&lua) == ThreadStatus::Error {
|
||||
#[cfg(not(feature = "vendored"))]
|
||||
ffi::lua_resetthread(self.thread.state());
|
||||
#[cfg(feature = "vendored")]
|
||||
ffi::lua_closethread(self.thread.state(), lua.state());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -458,31 +404,28 @@ impl<'lua, R> Drop for AsyncThread<'lua, R> {
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
impl<'lua, R> Stream for AsyncThread<'lua, R>
|
||||
where
|
||||
R: FromLuaMulti<'lua>,
|
||||
{
|
||||
impl<A: IntoLuaMulti, R: FromLuaMulti> Stream for AsyncThread<A, R> {
|
||||
type Item = Result<R>;
|
||||
|
||||
fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
|
||||
if self.thread.status() != ThreadStatus::Resumable {
|
||||
let lua = self.thread.0.lua.lock();
|
||||
if self.thread.status_inner(&lua) != ThreadStatus::Resumable {
|
||||
return Poll::Ready(None);
|
||||
}
|
||||
|
||||
let lua = self.thread.0.lua;
|
||||
let state = lua.state();
|
||||
let thread_state = self.thread.state();
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(state);
|
||||
let _thread_sg = StackGuard::with_top(thread_state, 0);
|
||||
let _wg = WakerGuard::new(lua, cx.waker());
|
||||
let _wg = WakerGuard::new(&lua, cx.waker());
|
||||
|
||||
// This is safe as we are not moving the whole struct
|
||||
let this = self.get_unchecked_mut();
|
||||
let nresults = if let Some(args) = this.init_args.take() {
|
||||
this.thread.resume_inner(args?)?
|
||||
this.thread.resume_inner(&lua, args)?
|
||||
} else {
|
||||
this.thread.resume_inner(())?
|
||||
this.thread.resume_inner(&lua, ())?
|
||||
};
|
||||
|
||||
if nresults == 1 && is_poll_pending(thread_state) {
|
||||
@@ -493,37 +436,34 @@ where
|
||||
ffi::lua_xmove(thread_state, state, nresults);
|
||||
|
||||
cx.waker().wake_by_ref();
|
||||
Poll::Ready(Some(R::from_stack_multi(nresults, lua)))
|
||||
Poll::Ready(Some(R::from_stack_multi(nresults, &lua)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
impl<'lua, R> Future for AsyncThread<'lua, R>
|
||||
where
|
||||
R: FromLuaMulti<'lua>,
|
||||
{
|
||||
impl<A: IntoLuaMulti, R: FromLuaMulti> Future for AsyncThread<A, R> {
|
||||
type Output = Result<R>;
|
||||
|
||||
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
if self.thread.status() != ThreadStatus::Resumable {
|
||||
return Poll::Ready(Err(Error::CoroutineInactive));
|
||||
let lua = self.thread.0.lua.lock();
|
||||
if self.thread.status_inner(&lua) != ThreadStatus::Resumable {
|
||||
return Poll::Ready(Err(Error::CoroutineUnresumable));
|
||||
}
|
||||
|
||||
let lua = self.thread.0.lua;
|
||||
let state = lua.state();
|
||||
let thread_state = self.thread.state();
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(state);
|
||||
let _thread_sg = StackGuard::with_top(thread_state, 0);
|
||||
let _wg = WakerGuard::new(lua, cx.waker());
|
||||
let _wg = WakerGuard::new(&lua, cx.waker());
|
||||
|
||||
// This is safe as we are not moving the whole struct
|
||||
let this = self.get_unchecked_mut();
|
||||
let nresults = if let Some(args) = this.init_args.take() {
|
||||
this.thread.resume_inner(args?)?
|
||||
this.thread.resume_inner(&lua, args)?
|
||||
} else {
|
||||
this.thread.resume_inner(())?
|
||||
this.thread.resume_inner(&lua, ())?
|
||||
};
|
||||
|
||||
if nresults == 1 && is_poll_pending(thread_state) {
|
||||
@@ -539,7 +479,7 @@ where
|
||||
check_stack(state, nresults + 1)?;
|
||||
ffi::lua_xmove(thread_state, state, nresults);
|
||||
|
||||
Poll::Ready(R::from_stack_multi(nresults, lua))
|
||||
Poll::Ready(R::from_stack_multi(nresults, &lua))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -552,7 +492,7 @@ unsafe fn is_poll_pending(state: *mut ffi::lua_State) -> bool {
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
struct WakerGuard<'lua, 'a> {
|
||||
lua: &'lua Lua,
|
||||
lua: &'lua RawLua,
|
||||
prev: NonNull<Waker>,
|
||||
_phantom: PhantomData<&'a ()>,
|
||||
}
|
||||
@@ -560,7 +500,7 @@ struct WakerGuard<'lua, 'a> {
|
||||
#[cfg(feature = "async")]
|
||||
impl<'lua, 'a> WakerGuard<'lua, 'a> {
|
||||
#[inline]
|
||||
pub fn new(lua: &'lua Lua, waker: &'a Waker) -> Result<WakerGuard<'lua, 'a>> {
|
||||
pub fn new(lua: &'lua RawLua, waker: &'a Waker) -> Result<WakerGuard<'lua, 'a>> {
|
||||
let prev = unsafe { lua.set_waker(NonNull::from(waker)) };
|
||||
Ok(WakerGuard {
|
||||
lua,
|
||||
@@ -581,5 +521,12 @@ impl<'lua, 'a> Drop for WakerGuard<'lua, 'a> {
|
||||
mod assertions {
|
||||
use super::*;
|
||||
|
||||
#[cfg(not(feature = "send"))]
|
||||
static_assertions::assert_not_impl_any!(Thread: Send);
|
||||
#[cfg(feature = "send")]
|
||||
static_assertions::assert_impl_all!(Thread: Send, Sync);
|
||||
#[cfg(all(feature = "async", not(feature = "send")))]
|
||||
static_assertions::assert_not_impl_any!(AsyncThread<(), ()>: Send);
|
||||
#[cfg(all(feature = "async", feature = "send"))]
|
||||
static_assertions::assert_impl_all!(AsyncThread<(), ()>: Send, Sync);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
use std::string::String as StdString;
|
||||
|
||||
use crate::error::Result;
|
||||
use crate::private::Sealed;
|
||||
use crate::value::{FromLua, FromLuaMulti, IntoLua, IntoLuaMulti};
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
use std::future::Future;
|
||||
|
||||
/// A trait for types that can be used as Lua objects (usually table and userdata).
|
||||
pub trait ObjectLike: Sealed {
|
||||
/// Gets the value associated to `key` from the object, assuming it has `__index` metamethod.
|
||||
fn get<V: FromLua>(&self, key: impl IntoLua) -> Result<V>;
|
||||
|
||||
/// Sets the value associated to `key` in the object, assuming it has `__newindex` metamethod.
|
||||
fn set(&self, key: impl IntoLua, value: impl IntoLua) -> Result<()>;
|
||||
|
||||
/// Calls the object as a function assuming it has `__call` metamethod.
|
||||
///
|
||||
/// The metamethod is called with the object as its first argument, followed by the passed
|
||||
/// arguments.
|
||||
fn call<R>(&self, args: impl IntoLuaMulti) -> Result<R>
|
||||
where
|
||||
R: FromLuaMulti;
|
||||
|
||||
/// Asynchronously calls the object as a function assuming it has `__call` metamethod.
|
||||
///
|
||||
/// The metamethod is called with the object as its first argument, followed by the passed
|
||||
/// arguments.
|
||||
#[cfg(feature = "async")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
|
||||
fn call_async<R>(&self, args: impl IntoLuaMulti) -> impl Future<Output = Result<R>>
|
||||
where
|
||||
R: FromLuaMulti;
|
||||
|
||||
/// Gets the function associated to key `name` from the object and calls it,
|
||||
/// passing the object itself along with `args` as function arguments.
|
||||
fn call_method<R>(&self, name: &str, args: impl IntoLuaMulti) -> Result<R>
|
||||
where
|
||||
R: FromLuaMulti;
|
||||
|
||||
/// Gets the function associated to key `name` from the object and asynchronously calls it,
|
||||
/// passing the object itself along with `args` as function arguments.
|
||||
///
|
||||
/// Requires `feature = "async"`
|
||||
///
|
||||
/// This might invoke the `__index` metamethod.
|
||||
#[cfg(feature = "async")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
|
||||
fn call_async_method<R>(&self, name: &str, args: impl IntoLuaMulti) -> impl Future<Output = Result<R>>
|
||||
where
|
||||
R: FromLuaMulti;
|
||||
|
||||
/// Gets the function associated to key `name` from the object and calls it,
|
||||
/// passing `args` as function arguments.
|
||||
///
|
||||
/// This might invoke the `__index` metamethod.
|
||||
fn call_function<R>(&self, name: &str, args: impl IntoLuaMulti) -> Result<R>
|
||||
where
|
||||
R: FromLuaMulti;
|
||||
|
||||
/// Gets the function associated to key `name` from the object and asynchronously calls it,
|
||||
/// passing `args` as function arguments.
|
||||
///
|
||||
/// Requires `feature = "async"`
|
||||
///
|
||||
/// This might invoke the `__index` metamethod.
|
||||
#[cfg(feature = "async")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
|
||||
fn call_async_function<R>(&self, name: &str, args: impl IntoLuaMulti) -> impl Future<Output = Result<R>>
|
||||
where
|
||||
R: FromLuaMulti;
|
||||
|
||||
/// Converts the object to a string in a human-readable format.
|
||||
///
|
||||
/// This might invoke the `__tostring` metamethod.
|
||||
fn to_string(&self) -> Result<StdString>;
|
||||
}
|
||||
+87
-429
@@ -1,28 +1,26 @@
|
||||
use std::any::{Any, TypeId};
|
||||
use std::cell::{Cell, Ref, RefCell, RefMut, UnsafeCell};
|
||||
use std::hash::{Hash, Hasher};
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use std::cell::UnsafeCell;
|
||||
use std::fmt;
|
||||
use std::os::raw::{c_int, c_void};
|
||||
use std::result::Result as StdResult;
|
||||
use std::sync::atomic::{AtomicI32, Ordering};
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::{fmt, mem, ptr};
|
||||
|
||||
use rustc_hash::FxHashMap;
|
||||
use std::rc::Rc;
|
||||
|
||||
use crate::error::Result;
|
||||
#[cfg(not(feature = "luau"))]
|
||||
use crate::hook::Debug;
|
||||
use crate::lua::{ExtraData, Lua};
|
||||
use crate::state::{ExtraData, Lua, RawLua, WeakLua};
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
use {crate::value::MultiValue, futures_util::future::LocalBoxFuture};
|
||||
// Re-export mutex wrappers
|
||||
pub(crate) use sync::{ArcReentrantMutexGuard, ReentrantMutex, ReentrantMutexGuard, XRc, XWeak};
|
||||
|
||||
#[cfg(feature = "unstable")]
|
||||
use {crate::lua::LuaInner, std::marker::PhantomData};
|
||||
#[cfg(all(feature = "async", feature = "send"))]
|
||||
pub(crate) type BoxFuture<'a, T> = futures_util::future::BoxFuture<'a, T>;
|
||||
|
||||
#[cfg(all(feature = "luau", feature = "serialize"))]
|
||||
use serde::ser::{Serialize, SerializeTupleStruct, Serializer};
|
||||
#[cfg(all(feature = "async", not(feature = "send")))]
|
||||
pub(crate) type BoxFuture<'a, T> = futures_util::future::LocalBoxFuture<'a, T>;
|
||||
|
||||
pub use app_data::{AppData, AppDataRef, AppDataRefMut};
|
||||
pub use registry_key::RegistryKey;
|
||||
#[cfg(any(feature = "luau", doc))]
|
||||
pub use vector::Vector;
|
||||
|
||||
/// Type of Lua integer numbers.
|
||||
pub type Integer = ffi::lua_Integer;
|
||||
@@ -43,24 +41,37 @@ pub(crate) enum SubtypeId {
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
pub struct LightUserData(pub *mut c_void);
|
||||
|
||||
pub(crate) type Callback<'lua, 'a> = Box<dyn Fn(&'lua Lua, c_int) -> Result<c_int> + 'a>;
|
||||
#[cfg(feature = "send")]
|
||||
unsafe impl Send for LightUserData {}
|
||||
#[cfg(feature = "send")]
|
||||
unsafe impl Sync for LightUserData {}
|
||||
|
||||
#[cfg(feature = "send")]
|
||||
pub(crate) type Callback = Box<dyn Fn(&RawLua, c_int) -> Result<c_int> + Send + 'static>;
|
||||
|
||||
#[cfg(not(feature = "send"))]
|
||||
pub(crate) type Callback = Box<dyn Fn(&RawLua, c_int) -> Result<c_int> + 'static>;
|
||||
|
||||
pub(crate) struct Upvalue<T> {
|
||||
pub(crate) data: T,
|
||||
pub(crate) extra: Arc<UnsafeCell<ExtraData>>,
|
||||
pub(crate) extra: XRc<UnsafeCell<ExtraData>>,
|
||||
}
|
||||
|
||||
pub(crate) type CallbackUpvalue = Upvalue<Callback<'static, 'static>>;
|
||||
pub(crate) type CallbackUpvalue = Upvalue<Callback>;
|
||||
|
||||
#[cfg(all(feature = "async", feature = "send"))]
|
||||
pub(crate) type AsyncCallback =
|
||||
Box<dyn for<'a> Fn(&'a RawLua, c_int) -> BoxFuture<'a, Result<c_int>> + Send + 'static>;
|
||||
|
||||
#[cfg(all(feature = "async", not(feature = "send")))]
|
||||
pub(crate) type AsyncCallback =
|
||||
Box<dyn for<'a> Fn(&'a RawLua, c_int) -> BoxFuture<'a, Result<c_int>> + 'static>;
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
pub(crate) type AsyncCallback<'lua, 'a> =
|
||||
Box<dyn Fn(&'lua Lua, MultiValue<'lua>) -> LocalBoxFuture<'lua, Result<c_int>> + 'a>;
|
||||
pub(crate) type AsyncCallbackUpvalue = Upvalue<AsyncCallback>;
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
pub(crate) type AsyncCallbackUpvalue = Upvalue<AsyncCallback<'static, 'static>>;
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
pub(crate) type AsyncPollUpvalue = Upvalue<LocalBoxFuture<'static, Result<c_int>>>;
|
||||
pub(crate) type AsyncPollUpvalue = Upvalue<BoxFuture<'static, Result<c_int>>>;
|
||||
|
||||
/// Type to set next Luau VM action after executing interrupt function.
|
||||
#[cfg(any(feature = "luau", doc))]
|
||||
@@ -71,16 +82,16 @@ pub enum VmState {
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "send", not(feature = "luau")))]
|
||||
pub(crate) type HookCallback = Arc<dyn Fn(&Lua, Debug) -> Result<()> + Send>;
|
||||
pub(crate) type HookCallback = Rc<dyn Fn(&Lua, Debug) -> Result<()> + Send>;
|
||||
|
||||
#[cfg(all(not(feature = "send"), not(feature = "luau")))]
|
||||
pub(crate) type HookCallback = Arc<dyn Fn(&Lua, Debug) -> Result<()>>;
|
||||
pub(crate) type HookCallback = Rc<dyn Fn(&Lua, Debug) -> Result<()>>;
|
||||
|
||||
#[cfg(all(feature = "luau", feature = "send"))]
|
||||
pub(crate) type InterruptCallback = Arc<dyn Fn(&Lua) -> Result<VmState> + Send>;
|
||||
#[cfg(all(feature = "send", feature = "luau"))]
|
||||
pub(crate) type InterruptCallback = Rc<dyn Fn(&Lua) -> Result<VmState> + Send>;
|
||||
|
||||
#[cfg(all(feature = "luau", not(feature = "send")))]
|
||||
pub(crate) type InterruptCallback = Arc<dyn Fn(&Lua) -> Result<VmState>>;
|
||||
#[cfg(all(not(feature = "send"), feature = "luau"))]
|
||||
pub(crate) type InterruptCallback = Rc<dyn Fn(&Lua) -> Result<VmState>>;
|
||||
|
||||
#[cfg(all(feature = "send", feature = "lua54"))]
|
||||
pub(crate) type WarnCallback = Box<dyn Fn(&Lua, &str, bool) -> Result<()> + Send>;
|
||||
@@ -88,6 +99,7 @@ pub(crate) type WarnCallback = Box<dyn Fn(&Lua, &str, bool) -> Result<()> + Send
|
||||
#[cfg(all(not(feature = "send"), feature = "lua54"))]
|
||||
pub(crate) type WarnCallback = Box<dyn Fn(&Lua, &str, bool) -> Result<()>>;
|
||||
|
||||
/// A trait that adds `Send` requirement if `send` feature is enabled.
|
||||
#[cfg(feature = "send")]
|
||||
pub trait MaybeSend: Send {}
|
||||
#[cfg(feature = "send")]
|
||||
@@ -98,191 +110,19 @@ pub trait MaybeSend {}
|
||||
#[cfg(not(feature = "send"))]
|
||||
impl<T> MaybeSend for T {}
|
||||
|
||||
/// A Luau vector type.
|
||||
///
|
||||
/// By default vectors are 3-dimensional, but can be 4-dimensional
|
||||
/// if the `luau-vector4` feature is enabled.
|
||||
#[cfg(any(feature = "luau", doc))]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
|
||||
#[derive(Debug, Default, Clone, Copy, PartialEq)]
|
||||
pub struct Vector(pub(crate) [f32; Self::SIZE]);
|
||||
|
||||
#[cfg(any(feature = "luau", doc))]
|
||||
impl fmt::Display for Vector {
|
||||
#[rustfmt::skip]
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
#[cfg(not(feature = "luau-vector4"))]
|
||||
return write!(f, "vector({}, {}, {})", self.x(), self.y(), self.z());
|
||||
#[cfg(feature = "luau-vector4")]
|
||||
return write!(f, "vector({}, {}, {}, {})", self.x(), self.y(), self.z(), self.w());
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "luau", doc))]
|
||||
impl Vector {
|
||||
pub(crate) const SIZE: usize = if cfg!(feature = "luau-vector4") { 4 } else { 3 };
|
||||
|
||||
/// Creates a new vector.
|
||||
#[cfg(not(feature = "luau-vector4"))]
|
||||
pub const fn new(x: f32, y: f32, z: f32) -> Self {
|
||||
Self([x, y, z])
|
||||
}
|
||||
|
||||
/// Creates a new vector.
|
||||
#[cfg(feature = "luau-vector4")]
|
||||
pub const fn new(x: f32, y: f32, z: f32, w: f32) -> Self {
|
||||
Self([x, y, z, w])
|
||||
}
|
||||
|
||||
/// Creates a new vector with all components set to `0.0`.
|
||||
#[doc(hidden)]
|
||||
pub const fn zero() -> Self {
|
||||
Self([0.0; Self::SIZE])
|
||||
}
|
||||
|
||||
/// Returns 1st component of the vector.
|
||||
pub const fn x(&self) -> f32 {
|
||||
self.0[0]
|
||||
}
|
||||
|
||||
/// Returns 2nd component of the vector.
|
||||
pub const fn y(&self) -> f32 {
|
||||
self.0[1]
|
||||
}
|
||||
|
||||
/// Returns 3rd component of the vector.
|
||||
pub const fn z(&self) -> f32 {
|
||||
self.0[2]
|
||||
}
|
||||
|
||||
/// Returns 4th component of the vector.
|
||||
#[cfg(any(feature = "luau-vector4", doc))]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "luau-vector4")))]
|
||||
pub const fn w(&self) -> f32 {
|
||||
self.0[3]
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "luau", feature = "serialize"))]
|
||||
impl Serialize for Vector {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) -> StdResult<S::Ok, S::Error> {
|
||||
let mut ts = serializer.serialize_tuple_struct("Vector", Self::SIZE)?;
|
||||
ts.serialize_field(&self.x())?;
|
||||
ts.serialize_field(&self.y())?;
|
||||
ts.serialize_field(&self.z())?;
|
||||
#[cfg(feature = "luau-vector4")]
|
||||
ts.serialize_field(&self.w())?;
|
||||
ts.end()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "luau", doc))]
|
||||
impl PartialEq<[f32; Self::SIZE]> for Vector {
|
||||
#[inline]
|
||||
fn eq(&self, other: &[f32; Self::SIZE]) -> bool {
|
||||
self.0 == *other
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct DestructedUserdata;
|
||||
|
||||
/// An auto generated key into the Lua registry.
|
||||
///
|
||||
/// This is a handle to a value stored inside the Lua registry. It is not automatically
|
||||
/// garbage collected on Drop, but it can be removed with [`Lua::remove_registry_value`],
|
||||
/// and instances not manually removed can be garbage collected with [`Lua::expire_registry_values`].
|
||||
///
|
||||
/// Be warned, If you place this into Lua via a [`UserData`] type or a rust callback, it is *very
|
||||
/// easy* to accidentally cause reference cycles that the Lua garbage collector cannot resolve.
|
||||
/// Instead of placing a [`RegistryKey`] into a [`UserData`] type, prefer instead to use
|
||||
/// [`AnyUserData::set_user_value`] / [`AnyUserData::user_value`].
|
||||
///
|
||||
/// [`UserData`]: crate::UserData
|
||||
/// [`RegistryKey`]: crate::RegistryKey
|
||||
/// [`Lua::remove_registry_value`]: crate::Lua::remove_registry_value
|
||||
/// [`Lua::expire_registry_values`]: crate::Lua::expire_registry_values
|
||||
/// [`AnyUserData::set_user_value`]: crate::AnyUserData::set_user_value
|
||||
/// [`AnyUserData::user_value`]: crate::AnyUserData::user_value
|
||||
pub struct RegistryKey {
|
||||
pub(crate) registry_id: AtomicI32,
|
||||
pub(crate) unref_list: Arc<Mutex<Option<Vec<c_int>>>>,
|
||||
}
|
||||
|
||||
impl fmt::Debug for RegistryKey {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "RegistryKey({})", self.id())
|
||||
}
|
||||
}
|
||||
|
||||
impl Hash for RegistryKey {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
self.id().hash(state)
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for RegistryKey {
|
||||
fn eq(&self, other: &RegistryKey) -> bool {
|
||||
self.id() == other.id() && Arc::ptr_eq(&self.unref_list, &other.unref_list)
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for RegistryKey {}
|
||||
|
||||
impl Drop for RegistryKey {
|
||||
fn drop(&mut self) {
|
||||
let registry_id = self.id();
|
||||
// We don't need to collect nil slot
|
||||
if registry_id > ffi::LUA_REFNIL {
|
||||
let mut unref_list = mlua_expect!(self.unref_list.lock(), "unref list poisoned");
|
||||
if let Some(list) = unref_list.as_mut() {
|
||||
list.push(registry_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl RegistryKey {
|
||||
/// Creates a new instance of `RegistryKey`
|
||||
pub(crate) const fn new(id: c_int, unref_list: Arc<Mutex<Option<Vec<c_int>>>>) -> Self {
|
||||
RegistryKey {
|
||||
registry_id: AtomicI32::new(id),
|
||||
unref_list,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the underlying Lua reference of this `RegistryKey`
|
||||
#[inline(always)]
|
||||
pub fn id(&self) -> c_int {
|
||||
self.registry_id.load(Ordering::Relaxed)
|
||||
}
|
||||
|
||||
/// Sets the unique Lua reference key of this `RegistryKey`
|
||||
#[inline(always)]
|
||||
pub(crate) fn set_id(&self, id: c_int) {
|
||||
self.registry_id.store(id, Ordering::Relaxed);
|
||||
}
|
||||
|
||||
/// Destroys the `RegistryKey` without adding to the unref list
|
||||
pub(crate) fn take(self) -> i32 {
|
||||
let registry_id = self.id();
|
||||
unsafe {
|
||||
ptr::read(&self.unref_list);
|
||||
mem::forget(self);
|
||||
}
|
||||
registry_id
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct LuaRef<'lua> {
|
||||
pub(crate) lua: &'lua Lua,
|
||||
pub(crate) struct ValueRef {
|
||||
pub(crate) lua: WeakLua,
|
||||
pub(crate) index: c_int,
|
||||
pub(crate) drop: bool,
|
||||
}
|
||||
|
||||
impl<'lua> LuaRef<'lua> {
|
||||
pub(crate) const fn new(lua: &'lua Lua, index: c_int) -> Self {
|
||||
LuaRef {
|
||||
lua,
|
||||
impl ValueRef {
|
||||
#[inline]
|
||||
pub(crate) fn new(lua: &RawLua, index: c_int) -> Self {
|
||||
ValueRef {
|
||||
lua: lua.weak().clone(),
|
||||
index,
|
||||
drop: true,
|
||||
}
|
||||
@@ -290,249 +130,67 @@ impl<'lua> LuaRef<'lua> {
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn to_pointer(&self) -> *const c_void {
|
||||
unsafe { ffi::lua_topointer(self.lua.ref_thread(), self.index) }
|
||||
let lua = self.lua.lock();
|
||||
unsafe { ffi::lua_topointer(lua.ref_thread(), self.index) }
|
||||
}
|
||||
|
||||
#[cfg(feature = "unstable")]
|
||||
/// Returns a copy of the value, which is valid as long as the original value is held.
|
||||
#[inline]
|
||||
pub(crate) fn into_owned(self) -> LuaOwnedRef {
|
||||
assert!(self.drop, "Cannot turn non-drop reference into owned");
|
||||
let owned_ref = LuaOwnedRef::new(self.lua.clone(), self.index);
|
||||
mem::forget(self);
|
||||
owned_ref
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> fmt::Debug for LuaRef<'lua> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "Ref({:p})", self.to_pointer())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> Clone for LuaRef<'lua> {
|
||||
fn clone(&self) -> Self {
|
||||
self.lua.clone_ref(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> Drop for LuaRef<'lua> {
|
||||
fn drop(&mut self) {
|
||||
if self.drop {
|
||||
self.lua.drop_ref_index(self.index);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> PartialEq for LuaRef<'lua> {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
let ref_thread = self.lua.ref_thread();
|
||||
assert!(
|
||||
ref_thread == other.lua.ref_thread(),
|
||||
"Lua instance passed Value created from a different main Lua state"
|
||||
);
|
||||
unsafe { ffi::lua_rawequal(ref_thread, self.index, other.index) == 1 }
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "unstable")]
|
||||
pub(crate) struct LuaOwnedRef {
|
||||
pub(crate) inner: Arc<LuaInner>,
|
||||
pub(crate) index: c_int,
|
||||
_non_send: PhantomData<*const ()>,
|
||||
}
|
||||
|
||||
#[cfg(feature = "unstable")]
|
||||
impl fmt::Debug for LuaOwnedRef {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "OwnedRef({:p})", self.to_ref().to_pointer())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "unstable")]
|
||||
impl Clone for LuaOwnedRef {
|
||||
fn clone(&self) -> Self {
|
||||
self.to_ref().clone().into_owned()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "unstable")]
|
||||
impl Drop for LuaOwnedRef {
|
||||
fn drop(&mut self) {
|
||||
let lua: &Lua = unsafe { mem::transmute(&self.inner) };
|
||||
lua.drop_ref_index(self.index);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "unstable")]
|
||||
impl LuaOwnedRef {
|
||||
pub(crate) const fn new(inner: Arc<LuaInner>, index: c_int) -> Self {
|
||||
LuaOwnedRef {
|
||||
inner,
|
||||
index,
|
||||
_non_send: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) const fn to_ref(&self) -> LuaRef {
|
||||
LuaRef {
|
||||
lua: unsafe { mem::transmute(&self.inner) },
|
||||
pub(crate) fn copy(&self) -> Self {
|
||||
ValueRef {
|
||||
lua: self.lua.clone(),
|
||||
index: self.index,
|
||||
drop: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub(crate) struct AppData {
|
||||
#[cfg(not(feature = "send"))]
|
||||
container: UnsafeCell<FxHashMap<TypeId, RefCell<Box<dyn Any>>>>,
|
||||
#[cfg(feature = "send")]
|
||||
container: UnsafeCell<FxHashMap<TypeId, RefCell<Box<dyn Any + Send>>>>,
|
||||
borrow: Cell<usize>,
|
||||
}
|
||||
|
||||
impl AppData {
|
||||
#[track_caller]
|
||||
pub(crate) fn insert<T: MaybeSend + 'static>(&self, data: T) -> Option<T> {
|
||||
match self.try_insert(data) {
|
||||
Ok(data) => data,
|
||||
Err(_) => panic!("cannot mutably borrow app data container"),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn try_insert<T: MaybeSend + 'static>(&self, data: T) -> StdResult<Option<T>, T> {
|
||||
if self.borrow.get() != 0 {
|
||||
return Err(data);
|
||||
}
|
||||
// SAFETY: we checked that there are no other references to the container
|
||||
Ok(unsafe { &mut *self.container.get() }
|
||||
.insert(TypeId::of::<T>(), RefCell::new(Box::new(data)))
|
||||
.and_then(|data| data.into_inner().downcast::<T>().ok().map(|data| *data)))
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
pub(crate) fn borrow<T: 'static>(&self) -> Option<AppDataRef<T>> {
|
||||
let data = unsafe { &*self.container.get() }
|
||||
.get(&TypeId::of::<T>())?
|
||||
.borrow();
|
||||
self.borrow.set(self.borrow.get() + 1);
|
||||
Some(AppDataRef {
|
||||
data: Ref::filter_map(data, |data| data.downcast_ref()).ok()?,
|
||||
borrow: &self.borrow,
|
||||
})
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
pub(crate) fn borrow_mut<T: 'static>(&self) -> Option<AppDataRefMut<T>> {
|
||||
let data = unsafe { &*self.container.get() }
|
||||
.get(&TypeId::of::<T>())?
|
||||
.borrow_mut();
|
||||
self.borrow.set(self.borrow.get() + 1);
|
||||
Some(AppDataRefMut {
|
||||
data: RefMut::filter_map(data, |data| data.downcast_mut()).ok()?,
|
||||
borrow: &self.borrow,
|
||||
})
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
pub(crate) fn remove<T: 'static>(&self) -> Option<T> {
|
||||
if self.borrow.get() != 0 {
|
||||
panic!("cannot mutably borrow app data container");
|
||||
}
|
||||
// SAFETY: we checked that there are no other references to the container
|
||||
unsafe { &mut *self.container.get() }
|
||||
.remove(&TypeId::of::<T>())?
|
||||
.into_inner()
|
||||
.downcast::<T>()
|
||||
.ok()
|
||||
.map(|data| *data)
|
||||
impl fmt::Debug for ValueRef {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "Ref({:p})", self.to_pointer())
|
||||
}
|
||||
}
|
||||
|
||||
/// A wrapper type for an immutably borrowed value from an app data container.
|
||||
///
|
||||
/// This type is similar to [`Ref`].
|
||||
pub struct AppDataRef<'a, T: ?Sized + 'a> {
|
||||
data: Ref<'a, T>,
|
||||
borrow: &'a Cell<usize>,
|
||||
impl Clone for ValueRef {
|
||||
fn clone(&self) -> Self {
|
||||
unsafe { self.lua.lock().clone_ref(self) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized> Drop for AppDataRef<'_, T> {
|
||||
impl Drop for ValueRef {
|
||||
fn drop(&mut self) {
|
||||
self.borrow.set(self.borrow.get() - 1);
|
||||
if self.drop {
|
||||
if let Some(lua) = self.lua.try_lock() {
|
||||
unsafe { lua.drop_ref(self) };
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized> Deref for AppDataRef<'_, T> {
|
||||
type Target = T;
|
||||
|
||||
#[inline]
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.data
|
||||
impl PartialEq for ValueRef {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
assert!(
|
||||
self.lua == other.lua,
|
||||
"Lua instance passed Value created from a different main Lua state"
|
||||
);
|
||||
let lua = self.lua.lock();
|
||||
unsafe { ffi::lua_rawequal(lua.ref_thread(), self.index, other.index) == 1 }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized + fmt::Display> fmt::Display for AppDataRef<'_, T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
(**self).fmt(f)
|
||||
}
|
||||
}
|
||||
mod app_data;
|
||||
mod registry_key;
|
||||
mod sync;
|
||||
|
||||
impl<T: ?Sized + fmt::Debug> fmt::Debug for AppDataRef<'_, T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
(**self).fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
/// A wrapper type for a mutably borrowed value from an app data container.
|
||||
///
|
||||
/// This type is similar to [`RefMut`].
|
||||
pub struct AppDataRefMut<'a, T: ?Sized + 'a> {
|
||||
data: RefMut<'a, T>,
|
||||
borrow: &'a Cell<usize>,
|
||||
}
|
||||
|
||||
impl<T: ?Sized> Drop for AppDataRefMut<'_, T> {
|
||||
fn drop(&mut self) {
|
||||
self.borrow.set(self.borrow.get() - 1);
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized> Deref for AppDataRefMut<'_, T> {
|
||||
type Target = T;
|
||||
|
||||
#[inline]
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.data
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized> DerefMut for AppDataRefMut<'_, T> {
|
||||
#[inline]
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.data
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized + fmt::Display> fmt::Display for AppDataRefMut<'_, T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
(**self).fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized + fmt::Debug> fmt::Debug for AppDataRefMut<'_, T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
(**self).fmt(f)
|
||||
}
|
||||
}
|
||||
#[cfg(any(feature = "luau", doc))]
|
||||
mod vector;
|
||||
|
||||
#[cfg(test)]
|
||||
mod assertions {
|
||||
use super::*;
|
||||
|
||||
static_assertions::assert_impl_all!(RegistryKey: Send, Sync);
|
||||
static_assertions::assert_not_impl_any!(LuaRef: Send);
|
||||
|
||||
#[cfg(feature = "unstable")]
|
||||
static_assertions::assert_not_impl_any!(LuaOwnedRef: Send);
|
||||
#[cfg(not(feature = "send"))]
|
||||
static_assertions::assert_not_impl_any!(ValueRef: Send);
|
||||
#[cfg(feature = "send")]
|
||||
static_assertions::assert_impl_all!(ValueRef: Send, Sync);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,176 @@
|
||||
use std::any::{Any, TypeId};
|
||||
use std::cell::{Cell, Ref, RefCell, RefMut, UnsafeCell};
|
||||
use std::fmt;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use std::result::Result as StdResult;
|
||||
|
||||
use rustc_hash::FxHashMap;
|
||||
|
||||
use super::MaybeSend;
|
||||
use crate::state::LuaGuard;
|
||||
|
||||
#[cfg(not(feature = "send"))]
|
||||
type Container = UnsafeCell<FxHashMap<TypeId, RefCell<Box<dyn Any>>>>;
|
||||
|
||||
#[cfg(feature = "send")]
|
||||
type Container = UnsafeCell<FxHashMap<TypeId, RefCell<Box<dyn Any + Send>>>>;
|
||||
|
||||
/// A container for arbitrary data associated with the Lua state.
|
||||
#[derive(Debug, Default)]
|
||||
pub struct AppData {
|
||||
container: Container,
|
||||
borrow: Cell<usize>,
|
||||
}
|
||||
|
||||
impl AppData {
|
||||
#[track_caller]
|
||||
pub(crate) fn insert<T: MaybeSend + 'static>(&self, data: T) -> Option<T> {
|
||||
match self.try_insert(data) {
|
||||
Ok(data) => data,
|
||||
Err(_) => panic!("cannot mutably borrow app data container"),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn try_insert<T: MaybeSend + 'static>(&self, data: T) -> StdResult<Option<T>, T> {
|
||||
if self.borrow.get() != 0 {
|
||||
return Err(data);
|
||||
}
|
||||
// SAFETY: we checked that there are no other references to the container
|
||||
Ok(unsafe { &mut *self.container.get() }
|
||||
.insert(TypeId::of::<T>(), RefCell::new(Box::new(data)))
|
||||
.and_then(|data| data.into_inner().downcast::<T>().ok().map(|data| *data)))
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
pub(crate) fn borrow<T: 'static>(&self, guard: Option<LuaGuard>) -> Option<AppDataRef<T>> {
|
||||
let data = unsafe { &*self.container.get() }
|
||||
.get(&TypeId::of::<T>())?
|
||||
.borrow();
|
||||
self.borrow.set(self.borrow.get() + 1);
|
||||
Some(AppDataRef {
|
||||
data: Ref::filter_map(data, |data| data.downcast_ref()).ok()?,
|
||||
borrow: &self.borrow,
|
||||
_guard: guard,
|
||||
})
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
pub(crate) fn borrow_mut<T: 'static>(&self, guard: Option<LuaGuard>) -> Option<AppDataRefMut<T>> {
|
||||
let data = unsafe { &*self.container.get() }
|
||||
.get(&TypeId::of::<T>())?
|
||||
.borrow_mut();
|
||||
self.borrow.set(self.borrow.get() + 1);
|
||||
Some(AppDataRefMut {
|
||||
data: RefMut::filter_map(data, |data| data.downcast_mut()).ok()?,
|
||||
borrow: &self.borrow,
|
||||
_guard: guard,
|
||||
})
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
pub(crate) fn remove<T: 'static>(&self) -> Option<T> {
|
||||
if self.borrow.get() != 0 {
|
||||
panic!("cannot mutably borrow app data container");
|
||||
}
|
||||
// SAFETY: we checked that there are no other references to the container
|
||||
unsafe { &mut *self.container.get() }
|
||||
.remove(&TypeId::of::<T>())?
|
||||
.into_inner()
|
||||
.downcast::<T>()
|
||||
.ok()
|
||||
.map(|data| *data)
|
||||
}
|
||||
}
|
||||
|
||||
/// A wrapper type for an immutably borrowed value from an app data container.
|
||||
///
|
||||
/// This type is similar to [`Ref`].
|
||||
pub struct AppDataRef<'a, T: ?Sized + 'a> {
|
||||
data: Ref<'a, T>,
|
||||
borrow: &'a Cell<usize>,
|
||||
_guard: Option<LuaGuard>,
|
||||
}
|
||||
|
||||
impl<T: ?Sized> Drop for AppDataRef<'_, T> {
|
||||
fn drop(&mut self) {
|
||||
self.borrow.set(self.borrow.get() - 1);
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized> Deref for AppDataRef<'_, T> {
|
||||
type Target = T;
|
||||
|
||||
#[inline]
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.data
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized + fmt::Display> fmt::Display for AppDataRef<'_, T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
(**self).fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized + fmt::Debug> fmt::Debug for AppDataRef<'_, T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
(**self).fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
/// A wrapper type for a mutably borrowed value from an app data container.
|
||||
///
|
||||
/// This type is similar to [`RefMut`].
|
||||
pub struct AppDataRefMut<'a, T: ?Sized + 'a> {
|
||||
data: RefMut<'a, T>,
|
||||
borrow: &'a Cell<usize>,
|
||||
_guard: Option<LuaGuard>,
|
||||
}
|
||||
|
||||
impl<T: ?Sized> Drop for AppDataRefMut<'_, T> {
|
||||
fn drop(&mut self) {
|
||||
self.borrow.set(self.borrow.get() - 1);
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized> Deref for AppDataRefMut<'_, T> {
|
||||
type Target = T;
|
||||
|
||||
#[inline]
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.data
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized> DerefMut for AppDataRefMut<'_, T> {
|
||||
#[inline]
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.data
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized + fmt::Display> fmt::Display for AppDataRefMut<'_, T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
(**self).fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized + fmt::Debug> fmt::Debug for AppDataRefMut<'_, T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
(**self).fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod assertions {
|
||||
use super::*;
|
||||
|
||||
#[cfg(not(feature = "send"))]
|
||||
static_assertions::assert_not_impl_any!(AppData: Send);
|
||||
#[cfg(feature = "send")]
|
||||
static_assertions::assert_impl_all!(AppData: Send);
|
||||
|
||||
// Must be !Send
|
||||
static_assertions::assert_not_impl_any!(AppDataRef<()>: Send);
|
||||
static_assertions::assert_not_impl_any!(AppDataRefMut<()>: Send);
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
use std::hash::{Hash, Hasher};
|
||||
use std::os::raw::c_int;
|
||||
use std::sync::Arc;
|
||||
use std::{fmt, mem, ptr};
|
||||
|
||||
use parking_lot::Mutex;
|
||||
|
||||
/// An auto generated key into the Lua registry.
|
||||
///
|
||||
/// This is a handle to a value stored inside the Lua registry. It is not automatically
|
||||
/// garbage collected on Drop, but it can be removed with [`Lua::remove_registry_value`],
|
||||
/// and instances not manually removed can be garbage collected with
|
||||
/// [`Lua::expire_registry_values`].
|
||||
///
|
||||
/// Be warned, If you place this into Lua via a [`UserData`] type or a rust callback, it is *very
|
||||
/// easy* to accidentally cause reference cycles that the Lua garbage collector cannot resolve.
|
||||
/// Instead of placing a [`RegistryKey`] into a [`UserData`] type, prefer instead to use
|
||||
/// [`AnyUserData::set_user_value`] / [`AnyUserData::user_value`].
|
||||
///
|
||||
/// [`UserData`]: crate::UserData
|
||||
/// [`RegistryKey`]: crate::RegistryKey
|
||||
/// [`Lua::remove_registry_value`]: crate::Lua::remove_registry_value
|
||||
/// [`Lua::expire_registry_values`]: crate::Lua::expire_registry_values
|
||||
/// [`AnyUserData::set_user_value`]: crate::AnyUserData::set_user_value
|
||||
/// [`AnyUserData::user_value`]: crate::AnyUserData::user_value
|
||||
pub struct RegistryKey {
|
||||
pub(crate) registry_id: i32,
|
||||
pub(crate) unref_list: Arc<Mutex<Option<Vec<c_int>>>>,
|
||||
}
|
||||
|
||||
impl fmt::Debug for RegistryKey {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "RegistryKey({})", self.id())
|
||||
}
|
||||
}
|
||||
|
||||
impl Hash for RegistryKey {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
self.id().hash(state)
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for RegistryKey {
|
||||
fn eq(&self, other: &RegistryKey) -> bool {
|
||||
self.id() == other.id() && Arc::ptr_eq(&self.unref_list, &other.unref_list)
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for RegistryKey {}
|
||||
|
||||
impl Drop for RegistryKey {
|
||||
fn drop(&mut self) {
|
||||
let registry_id = self.id();
|
||||
// We don't need to collect nil slot
|
||||
if registry_id > ffi::LUA_REFNIL {
|
||||
let mut unref_list = self.unref_list.lock();
|
||||
if let Some(list) = unref_list.as_mut() {
|
||||
list.push(registry_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl RegistryKey {
|
||||
/// Creates a new instance of `RegistryKey`
|
||||
pub(crate) const fn new(id: c_int, unref_list: Arc<Mutex<Option<Vec<c_int>>>>) -> Self {
|
||||
RegistryKey {
|
||||
registry_id: id,
|
||||
unref_list,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the underlying Lua reference of this `RegistryKey`
|
||||
#[inline(always)]
|
||||
pub fn id(&self) -> c_int {
|
||||
self.registry_id
|
||||
}
|
||||
|
||||
/// Sets the unique Lua reference key of this `RegistryKey`
|
||||
#[inline(always)]
|
||||
pub(crate) fn set_id(&mut self, id: c_int) {
|
||||
self.registry_id = id;
|
||||
}
|
||||
|
||||
/// Destroys the `RegistryKey` without adding to the unref list
|
||||
pub(crate) fn take(self) -> i32 {
|
||||
let registry_id = self.id();
|
||||
unsafe {
|
||||
ptr::read(&self.unref_list);
|
||||
mem::forget(self);
|
||||
}
|
||||
registry_id
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod assertions {
|
||||
use super::*;
|
||||
|
||||
static_assertions::assert_impl_all!(RegistryKey: Send, Sync);
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
#[cfg(feature = "send")]
|
||||
mod inner {
|
||||
use parking_lot::{RawMutex, RawThreadId};
|
||||
use std::sync::{Arc, Weak};
|
||||
|
||||
pub(crate) type XRc<T> = Arc<T>;
|
||||
pub(crate) type XWeak<T> = Weak<T>;
|
||||
|
||||
pub(crate) type ReentrantMutex<T> = parking_lot::ReentrantMutex<T>;
|
||||
|
||||
pub(crate) type ReentrantMutexGuard<'a, T> = parking_lot::ReentrantMutexGuard<'a, T>;
|
||||
|
||||
pub(crate) type ArcReentrantMutexGuard<T> =
|
||||
parking_lot::lock_api::ArcReentrantMutexGuard<RawMutex, RawThreadId, T>;
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "send"))]
|
||||
mod inner {
|
||||
use std::ops::Deref;
|
||||
use std::rc::{Rc, Weak};
|
||||
|
||||
pub(crate) type XRc<T> = Rc<T>;
|
||||
pub(crate) type XWeak<T> = Weak<T>;
|
||||
|
||||
pub(crate) struct ReentrantMutex<T>(T);
|
||||
|
||||
impl<T> ReentrantMutex<T> {
|
||||
#[inline(always)]
|
||||
pub(crate) fn new(val: T) -> Self {
|
||||
ReentrantMutex(val)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub(crate) fn lock(&self) -> ReentrantMutexGuard<T> {
|
||||
ReentrantMutexGuard(&self.0)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub(crate) fn lock_arc(self: &XRc<Self>) -> ArcReentrantMutexGuard<T> {
|
||||
ArcReentrantMutexGuard(Rc::clone(self))
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub(crate) fn into_lock_arc(self: XRc<Self>) -> ArcReentrantMutexGuard<T> {
|
||||
ArcReentrantMutexGuard(self)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub(crate) fn data_ptr(&self) -> *const T {
|
||||
&self.0 as *const _
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct ReentrantMutexGuard<'a, T>(&'a T);
|
||||
|
||||
impl<'a, T> Deref for ReentrantMutexGuard<'a, T> {
|
||||
type Target = T;
|
||||
|
||||
#[inline(always)]
|
||||
fn deref(&self) -> &Self::Target {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct ArcReentrantMutexGuard<T>(XRc<ReentrantMutex<T>>);
|
||||
|
||||
impl<T> Deref for ArcReentrantMutexGuard<T> {
|
||||
type Target = T;
|
||||
|
||||
#[inline(always)]
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0 .0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) use inner::{ArcReentrantMutexGuard, ReentrantMutex, ReentrantMutexGuard, XRc, XWeak};
|
||||
@@ -0,0 +1,86 @@
|
||||
use std::fmt;
|
||||
|
||||
#[cfg(all(any(feature = "luau", doc), feature = "serialize"))]
|
||||
use serde::ser::{Serialize, SerializeTupleStruct, Serializer};
|
||||
|
||||
/// A Luau vector type.
|
||||
///
|
||||
/// By default vectors are 3-dimensional, but can be 4-dimensional
|
||||
/// if the `luau-vector4` feature is enabled.
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
|
||||
#[derive(Debug, Default, Clone, Copy, PartialEq)]
|
||||
pub struct Vector(pub(crate) [f32; Self::SIZE]);
|
||||
|
||||
impl fmt::Display for Vector {
|
||||
#[rustfmt::skip]
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
#[cfg(not(feature = "luau-vector4"))]
|
||||
return write!(f, "vector({}, {}, {})", self.x(), self.y(), self.z());
|
||||
#[cfg(feature = "luau-vector4")]
|
||||
return write!(f, "vector({}, {}, {}, {})", self.x(), self.y(), self.z(), self.w());
|
||||
}
|
||||
}
|
||||
|
||||
impl Vector {
|
||||
pub(crate) const SIZE: usize = if cfg!(feature = "luau-vector4") { 4 } else { 3 };
|
||||
|
||||
/// Creates a new vector.
|
||||
#[cfg(not(feature = "luau-vector4"))]
|
||||
pub const fn new(x: f32, y: f32, z: f32) -> Self {
|
||||
Self([x, y, z])
|
||||
}
|
||||
|
||||
/// Creates a new vector.
|
||||
#[cfg(feature = "luau-vector4")]
|
||||
pub const fn new(x: f32, y: f32, z: f32, w: f32) -> Self {
|
||||
Self([x, y, z, w])
|
||||
}
|
||||
|
||||
/// Creates a new vector with all components set to `0.0`.
|
||||
#[doc(hidden)]
|
||||
pub const fn zero() -> Self {
|
||||
Self([0.0; Self::SIZE])
|
||||
}
|
||||
|
||||
/// Returns 1st component of the vector.
|
||||
pub const fn x(&self) -> f32 {
|
||||
self.0[0]
|
||||
}
|
||||
|
||||
/// Returns 2nd component of the vector.
|
||||
pub const fn y(&self) -> f32 {
|
||||
self.0[1]
|
||||
}
|
||||
|
||||
/// Returns 3rd component of the vector.
|
||||
pub const fn z(&self) -> f32 {
|
||||
self.0[2]
|
||||
}
|
||||
|
||||
/// Returns 4th component of the vector.
|
||||
#[cfg(any(feature = "luau-vector4", doc))]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "luau-vector4")))]
|
||||
pub const fn w(&self) -> f32 {
|
||||
self.0[3]
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(any(feature = "luau", doc), feature = "serialize"))]
|
||||
impl Serialize for Vector {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error> {
|
||||
let mut ts = serializer.serialize_tuple_struct("Vector", Self::SIZE)?;
|
||||
ts.serialize_field(&self.x())?;
|
||||
ts.serialize_field(&self.y())?;
|
||||
ts.serialize_field(&self.z())?;
|
||||
#[cfg(feature = "luau-vector4")]
|
||||
ts.serialize_field(&self.w())?;
|
||||
ts.end()
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq<[f32; Self::SIZE]> for Vector {
|
||||
#[inline]
|
||||
fn eq(&self, other: &[f32; Self::SIZE]) -> bool {
|
||||
self.0 == *other
|
||||
}
|
||||
}
|
||||
+193
-466
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,391 @@
|
||||
use std::any::{type_name, TypeId};
|
||||
use std::cell::UnsafeCell;
|
||||
use std::fmt;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use std::os::raw::c_int;
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
use serde::ser::{Serialize, Serializer};
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
use crate::state::{Lua, RawLua};
|
||||
use crate::types::{MaybeSend, XRc};
|
||||
use crate::userdata::AnyUserData;
|
||||
use crate::util::get_userdata;
|
||||
use crate::value::{FromLua, Value};
|
||||
|
||||
use super::lock::{RawLock, UserDataLock};
|
||||
|
||||
#[cfg(all(feature = "serialize", not(feature = "send")))]
|
||||
type DynSerialize = dyn erased_serde::Serialize;
|
||||
|
||||
#[cfg(all(feature = "serialize", feature = "send"))]
|
||||
type DynSerialize = dyn erased_serde::Serialize + Send;
|
||||
|
||||
// A enum for storing userdata values.
|
||||
// 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")]
|
||||
Serializable(XRc<UserDataCell<Box<DynSerialize>>>),
|
||||
}
|
||||
|
||||
impl<T> Clone for UserDataVariant<T> {
|
||||
#[inline]
|
||||
fn clone(&self) -> Self {
|
||||
match self {
|
||||
Self::Default(inner) => Self::Default(XRc::clone(inner)),
|
||||
#[cfg(feature = "serialize")]
|
||||
Self::Serializable(inner) => Self::Serializable(XRc::clone(inner)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> UserDataVariant<T> {
|
||||
#[inline(always)]
|
||||
pub(crate) fn new(data: T) -> Self {
|
||||
Self::Default(XRc::new(UserDataCell::new(data)))
|
||||
}
|
||||
|
||||
// Immutably borrows the wrapped value in-place.
|
||||
#[inline(always)]
|
||||
pub(crate) fn try_borrow(&self) -> Result<UserDataBorrowRef<T>> {
|
||||
UserDataBorrowRef::try_from(self)
|
||||
}
|
||||
|
||||
// Immutably borrows the wrapped value and returns an owned reference.
|
||||
#[inline(always)]
|
||||
pub(crate) fn try_borrow_owned(&self) -> Result<UserDataRef<T>> {
|
||||
UserDataRef::try_from(self.clone())
|
||||
}
|
||||
|
||||
// Mutably borrows the wrapped value in-place.
|
||||
#[inline(always)]
|
||||
pub(crate) fn try_borrow_mut(&self) -> Result<UserDataBorrowMut<T>> {
|
||||
UserDataBorrowMut::try_from(self)
|
||||
}
|
||||
|
||||
// Mutably borrows the wrapped value and returns an owned reference.
|
||||
#[inline(always)]
|
||||
pub(crate) fn try_borrow_owned_mut(&self) -> Result<UserDataRefMut<T>> {
|
||||
UserDataRefMut::try_from(self.clone())
|
||||
}
|
||||
|
||||
// Returns the wrapped value.
|
||||
//
|
||||
// This method checks that we have exclusive access to the value.
|
||||
pub(crate) fn into_inner(self) -> Result<T> {
|
||||
if !self.raw_lock().try_lock_exclusive() {
|
||||
return Err(Error::UserDataBorrowMutError);
|
||||
}
|
||||
Ok(match self {
|
||||
Self::Default(inner) => XRc::into_inner(inner).unwrap().value.into_inner(),
|
||||
#[cfg(feature = "serialize")]
|
||||
Self::Serializable(inner) => unsafe {
|
||||
let raw = Box::into_raw(XRc::into_inner(inner).unwrap().value.into_inner());
|
||||
*Box::from_raw(raw as *mut T)
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn raw_lock(&self) -> &RawLock {
|
||||
match self {
|
||||
Self::Default(inner) => &inner.raw_lock,
|
||||
#[cfg(feature = "serialize")]
|
||||
Self::Serializable(inner) => &inner.raw_lock,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn as_ptr(&self) -> *mut T {
|
||||
match self {
|
||||
Self::Default(inner) => inner.value.get(),
|
||||
#[cfg(feature = "serialize")]
|
||||
Self::Serializable(inner) => unsafe { &mut **(inner.value.get() as *mut Box<T>) },
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
impl<T: Serialize + MaybeSend + 'static> UserDataVariant<T> {
|
||||
#[inline(always)]
|
||||
pub(crate) fn new_ser(data: T) -> Self {
|
||||
let data = Box::new(data) as Box<DynSerialize>;
|
||||
Self::Serializable(XRc::new(UserDataCell::new(data)))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
impl Serialize for UserDataVariant<()> {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error> {
|
||||
match self {
|
||||
Self::Default(_) => Err(serde::ser::Error::custom("cannot serialize <userdata>")),
|
||||
Self::Serializable(inner) => unsafe {
|
||||
// We need to borrow the inner value exclusively to serialize it.
|
||||
#[cfg(feature = "send")]
|
||||
let _guard = self.try_borrow_mut().map_err(serde::ser::Error::custom)?;
|
||||
// No need to do this if the `send` feature is disabled.
|
||||
#[cfg(not(feature = "send"))]
|
||||
let _guard = self.try_borrow().map_err(serde::ser::Error::custom)?;
|
||||
(*inner.value.get()).serialize(serializer)
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A type that provides interior mutability for a userdata value (thread-safe).
|
||||
pub(crate) struct UserDataCell<T> {
|
||||
raw_lock: RawLock,
|
||||
value: UnsafeCell<T>,
|
||||
}
|
||||
|
||||
unsafe impl<T: Send> Send for UserDataCell<T> {}
|
||||
unsafe impl<T: Send> Sync for UserDataCell<T> {}
|
||||
|
||||
impl<T> UserDataCell<T> {
|
||||
#[inline(always)]
|
||||
pub fn new(value: T) -> Self {
|
||||
UserDataCell {
|
||||
raw_lock: RawLock::INIT,
|
||||
value: UnsafeCell::new(value),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A wrapper type for a [`UserData`] value that provides read access.
|
||||
///
|
||||
/// It implements [`FromLua`] and can be used to receive a typed userdata from Lua.
|
||||
pub struct UserDataRef<T>(UserDataVariant<T>);
|
||||
|
||||
impl<T> Deref for UserDataRef<T> {
|
||||
type Target = T;
|
||||
|
||||
#[inline]
|
||||
fn deref(&self) -> &T {
|
||||
unsafe { &*self.0.as_ptr() }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Drop for UserDataRef<T> {
|
||||
#[inline]
|
||||
fn drop(&mut self) {
|
||||
unsafe { self.0.raw_lock().unlock_shared() };
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: fmt::Debug> fmt::Debug for UserDataRef<T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
(**self).fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: fmt::Display> fmt::Display for UserDataRef<T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
(**self).fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> TryFrom<UserDataVariant<T>> for UserDataRef<T> {
|
||||
type Error = Error;
|
||||
|
||||
#[inline]
|
||||
fn try_from(variant: UserDataVariant<T>) -> Result<Self> {
|
||||
if !variant.raw_lock().try_lock_shared() {
|
||||
return Err(Error::UserDataBorrowError);
|
||||
}
|
||||
Ok(UserDataRef(variant))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: 'static> FromLua for UserDataRef<T> {
|
||||
fn from_lua(value: Value, _: &Lua) -> Result<Self> {
|
||||
try_value_to_userdata::<T>(value)?.borrow()
|
||||
}
|
||||
|
||||
unsafe fn from_stack(idx: c_int, lua: &RawLua) -> Result<Self> {
|
||||
let type_id = lua.get_userdata_type_id(idx)?;
|
||||
match type_id {
|
||||
Some(type_id) if type_id == TypeId::of::<T>() => {
|
||||
(*get_userdata::<UserDataVariant<T>>(lua.state(), idx)).try_borrow_owned()
|
||||
}
|
||||
_ => Err(Error::UserDataTypeMismatch),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A wrapper type for a mutably borrowed value from a `AnyUserData`.
|
||||
///
|
||||
/// It implements [`FromLua`] and can be used to receive a typed userdata from Lua.
|
||||
pub struct UserDataRefMut<T>(UserDataVariant<T>);
|
||||
|
||||
impl<T> Deref for UserDataRefMut<T> {
|
||||
type Target = T;
|
||||
|
||||
#[inline]
|
||||
fn deref(&self) -> &Self::Target {
|
||||
unsafe { &*self.0.as_ptr() }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> DerefMut for UserDataRefMut<T> {
|
||||
#[inline]
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
unsafe { &mut *self.0.as_ptr() }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Drop for UserDataRefMut<T> {
|
||||
#[inline]
|
||||
fn drop(&mut self) {
|
||||
unsafe { self.0.raw_lock().unlock_exclusive() };
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: fmt::Debug> fmt::Debug for UserDataRefMut<T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
(**self).fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: fmt::Display> fmt::Display for UserDataRefMut<T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
(**self).fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> TryFrom<UserDataVariant<T>> for UserDataRefMut<T> {
|
||||
type Error = Error;
|
||||
|
||||
#[inline]
|
||||
fn try_from(variant: UserDataVariant<T>) -> Result<Self> {
|
||||
if !variant.raw_lock().try_lock_exclusive() {
|
||||
return Err(Error::UserDataBorrowMutError);
|
||||
}
|
||||
Ok(UserDataRefMut(variant))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: 'static> FromLua for UserDataRefMut<T> {
|
||||
fn from_lua(value: Value, _: &Lua) -> Result<Self> {
|
||||
try_value_to_userdata::<T>(value)?.borrow_mut()
|
||||
}
|
||||
|
||||
unsafe fn from_stack(idx: c_int, lua: &RawLua) -> Result<Self> {
|
||||
let type_id = lua.get_userdata_type_id(idx)?;
|
||||
match type_id {
|
||||
Some(type_id) if type_id == TypeId::of::<T>() => {
|
||||
(*get_userdata::<UserDataVariant<T>>(lua.state(), idx)).try_borrow_owned_mut()
|
||||
}
|
||||
_ => Err(Error::UserDataTypeMismatch),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A type that provides read access to a userdata value (borrowing the value).
|
||||
pub(crate) struct UserDataBorrowRef<'a, T>(&'a UserDataVariant<T>);
|
||||
|
||||
impl<'a, T> Drop for UserDataBorrowRef<'a, T> {
|
||||
#[inline]
|
||||
fn drop(&mut self) {
|
||||
unsafe { self.0.raw_lock().unlock_shared() };
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> Deref for UserDataBorrowRef<'a, T> {
|
||||
type Target = T;
|
||||
|
||||
#[inline]
|
||||
fn deref(&self) -> &T {
|
||||
// SAFETY: `UserDataBorrowRef` is only created with shared access to the value.
|
||||
unsafe { &*self.0.as_ptr() }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> TryFrom<&'a UserDataVariant<T>> for UserDataBorrowRef<'a, T> {
|
||||
type Error = Error;
|
||||
|
||||
#[inline(always)]
|
||||
fn try_from(variant: &'a UserDataVariant<T>) -> Result<Self> {
|
||||
if !variant.raw_lock().try_lock_shared() {
|
||||
return Err(Error::UserDataBorrowError);
|
||||
}
|
||||
Ok(UserDataBorrowRef(variant))
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct UserDataBorrowMut<'a, T>(&'a UserDataVariant<T>);
|
||||
|
||||
impl<'a, T> Drop for UserDataBorrowMut<'a, T> {
|
||||
#[inline]
|
||||
fn drop(&mut self) {
|
||||
unsafe { self.0.raw_lock().unlock_exclusive() };
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> Deref for UserDataBorrowMut<'a, T> {
|
||||
type Target = T;
|
||||
|
||||
#[inline]
|
||||
fn deref(&self) -> &T {
|
||||
unsafe { &*self.0.as_ptr() }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> DerefMut for UserDataBorrowMut<'a, T> {
|
||||
#[inline]
|
||||
fn deref_mut(&mut self) -> &mut T {
|
||||
unsafe { &mut *self.0.as_ptr() }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> TryFrom<&'a UserDataVariant<T>> for UserDataBorrowMut<'a, T> {
|
||||
type Error = Error;
|
||||
|
||||
#[inline(always)]
|
||||
fn try_from(variant: &'a UserDataVariant<T>) -> Result<Self> {
|
||||
if !variant.raw_lock().try_lock_exclusive() {
|
||||
return Err(Error::UserDataBorrowMutError);
|
||||
}
|
||||
Ok(UserDataBorrowMut(variant))
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn try_value_to_userdata<T>(value: Value) -> Result<AnyUserData> {
|
||||
match value {
|
||||
Value::UserData(ud) => Ok(ud),
|
||||
_ => Err(Error::FromLuaConversionError {
|
||||
from: value.type_name(),
|
||||
to: "userdata",
|
||||
message: Some(format!("expected userdata of type {}", type_name::<T>())),
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod assertions {
|
||||
use super::*;
|
||||
|
||||
#[cfg(feature = "send")]
|
||||
static_assertions::assert_impl_all!(UserDataRef<()>: Send, Sync);
|
||||
#[cfg(feature = "send")]
|
||||
static_assertions::assert_not_impl_all!(UserDataRef<std::rc::Rc<()>>: Send, Sync);
|
||||
#[cfg(feature = "send")]
|
||||
static_assertions::assert_impl_all!(UserDataRefMut<()>: Sync, Send);
|
||||
#[cfg(feature = "send")]
|
||||
static_assertions::assert_not_impl_all!(UserDataRefMut<std::rc::Rc<()>>: Send, Sync);
|
||||
#[cfg(feature = "send")]
|
||||
static_assertions::assert_impl_all!(UserDataBorrowRef<'_, ()>: Send, Sync);
|
||||
#[cfg(feature = "send")]
|
||||
static_assertions::assert_impl_all!(UserDataBorrowMut<'_, ()>: Send, Sync);
|
||||
|
||||
#[cfg(not(feature = "send"))]
|
||||
static_assertions::assert_not_impl_all!(UserDataRef<()>: Send, Sync);
|
||||
#[cfg(not(feature = "send"))]
|
||||
static_assertions::assert_not_impl_all!(UserDataRefMut<()>: Send, Sync);
|
||||
#[cfg(not(feature = "send"))]
|
||||
static_assertions::assert_not_impl_all!(UserDataBorrowRef<'_, ()>: Send, Sync);
|
||||
#[cfg(not(feature = "send"))]
|
||||
static_assertions::assert_not_impl_all!(UserDataBorrowMut<'_, ()>: Send, Sync);
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
pub(crate) trait UserDataLock {
|
||||
const INIT: Self;
|
||||
|
||||
fn try_lock_shared(&self) -> bool;
|
||||
fn try_lock_exclusive(&self) -> bool;
|
||||
|
||||
unsafe fn unlock_shared(&self);
|
||||
unsafe fn unlock_exclusive(&self);
|
||||
}
|
||||
|
||||
pub(crate) use lock_impl::RawLock;
|
||||
|
||||
#[cfg(not(feature = "send"))]
|
||||
mod lock_impl {
|
||||
use std::cell::Cell;
|
||||
|
||||
// Positive values represent the number of read references.
|
||||
// Negative values represent the number of write references (only one allowed).
|
||||
pub(crate) type RawLock = Cell<isize>;
|
||||
|
||||
const UNUSED: isize = 0;
|
||||
|
||||
impl super::UserDataLock for RawLock {
|
||||
#[allow(clippy::declare_interior_mutable_const)]
|
||||
const INIT: Self = Cell::new(UNUSED);
|
||||
|
||||
#[inline(always)]
|
||||
fn try_lock_shared(&self) -> bool {
|
||||
let flag = self.get().wrapping_add(1);
|
||||
if flag <= UNUSED {
|
||||
return false;
|
||||
}
|
||||
self.set(flag);
|
||||
true
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn try_lock_exclusive(&self) -> bool {
|
||||
let flag = self.get();
|
||||
if flag != UNUSED {
|
||||
return false;
|
||||
}
|
||||
self.set(UNUSED - 1);
|
||||
true
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
unsafe fn unlock_shared(&self) {
|
||||
let flag = self.get();
|
||||
debug_assert!(flag > UNUSED);
|
||||
self.set(flag - 1);
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
unsafe fn unlock_exclusive(&self) {
|
||||
let flag = self.get();
|
||||
debug_assert!(flag < UNUSED);
|
||||
self.set(flag + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "send")]
|
||||
mod lock_impl {
|
||||
use parking_lot::lock_api::RawMutex;
|
||||
|
||||
pub(crate) type RawLock = parking_lot::RawMutex;
|
||||
|
||||
impl super::UserDataLock for RawLock {
|
||||
#[allow(clippy::declare_interior_mutable_const)]
|
||||
const INIT: Self = <Self as parking_lot::lock_api::RawMutex>::INIT;
|
||||
|
||||
#[inline(always)]
|
||||
fn try_lock_shared(&self) -> bool {
|
||||
RawLock::try_lock(self)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn try_lock_exclusive(&self) -> bool {
|
||||
RawLock::try_lock(self)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
unsafe fn unlock_shared(&self) {
|
||||
RawLock::unlock(self)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
unsafe fn unlock_exclusive(&self) {
|
||||
RawLock::unlock(self)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
use std::string::String as StdString;
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
use crate::table::Table;
|
||||
use crate::traits::ObjectLike;
|
||||
use crate::userdata::AnyUserData;
|
||||
use crate::value::{FromLua, FromLuaMulti, IntoLua, IntoLuaMulti, Value};
|
||||
use crate::Function;
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
use futures_util::future::{self, Either, Future};
|
||||
|
||||
impl ObjectLike for AnyUserData {
|
||||
#[inline]
|
||||
fn get<V: FromLua>(&self, key: impl IntoLua) -> Result<V> {
|
||||
// `lua_gettable` method used under the hood can work with any Lua value
|
||||
// that has `__index` metamethod
|
||||
Table(self.0.copy()).get_protected(key)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn set(&self, key: impl IntoLua, value: impl IntoLua) -> Result<()> {
|
||||
// `lua_settable` method used under the hood can work with any Lua value
|
||||
// that has `__newindex` metamethod
|
||||
Table(self.0.copy()).set_protected(key, value)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn call<R>(&self, args: impl IntoLuaMulti) -> Result<R>
|
||||
where
|
||||
R: FromLuaMulti,
|
||||
{
|
||||
Function(self.0.copy()).call(args)
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
#[inline]
|
||||
fn call_async<R>(&self, args: impl IntoLuaMulti) -> impl Future<Output = Result<R>>
|
||||
where
|
||||
R: FromLuaMulti,
|
||||
{
|
||||
Function(self.0.copy()).call_async(args)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn call_method<R>(&self, name: &str, args: impl IntoLuaMulti) -> Result<R>
|
||||
where
|
||||
R: FromLuaMulti,
|
||||
{
|
||||
self.call_function(name, (self, args))
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
fn call_async_method<R>(&self, name: &str, args: impl IntoLuaMulti) -> impl Future<Output = Result<R>>
|
||||
where
|
||||
R: FromLuaMulti,
|
||||
{
|
||||
self.call_async_function(name, (self, args))
|
||||
}
|
||||
|
||||
fn call_function<R>(&self, name: &str, args: impl IntoLuaMulti) -> Result<R>
|
||||
where
|
||||
R: FromLuaMulti,
|
||||
{
|
||||
match self.get(name)? {
|
||||
Value::Function(func) => func.call(args),
|
||||
val => {
|
||||
let msg = format!("attempt to call a {} value (function '{name}')", val.type_name());
|
||||
Err(Error::RuntimeError(msg))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
fn call_async_function<R>(&self, name: &str, args: impl IntoLuaMulti) -> impl Future<Output = Result<R>>
|
||||
where
|
||||
R: FromLuaMulti,
|
||||
{
|
||||
match self.get(name) {
|
||||
Ok(Value::Function(func)) => Either::Left(func.call_async(args)),
|
||||
Ok(val) => {
|
||||
let msg = format!("attempt to call a {} value (function '{name}')", val.type_name());
|
||||
Either::Right(future::ready(Err(Error::RuntimeError(msg))))
|
||||
}
|
||||
Err(err) => Either::Right(future::ready(Err(err))),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn to_string(&self) -> Result<StdString> {
|
||||
Value::UserData(AnyUserData(self.0.copy(), self.1)).to_string()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,582 @@
|
||||
#![allow(clippy::await_holding_refcell_ref, clippy::await_holding_lock)]
|
||||
|
||||
use std::any::TypeId;
|
||||
use std::cell::RefCell;
|
||||
use std::marker::PhantomData;
|
||||
use std::os::raw::c_int;
|
||||
use std::string::String as StdString;
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
use crate::state::{Lua, RawLua};
|
||||
use crate::types::{Callback, MaybeSend};
|
||||
use crate::userdata::{
|
||||
AnyUserData, MetaMethod, UserData, UserDataFields, UserDataMethods, UserDataRef, UserDataRefMut,
|
||||
};
|
||||
use crate::util::{get_userdata, short_type_name};
|
||||
use crate::value::{FromLua, FromLuaMulti, IntoLua, IntoLuaMulti, Value};
|
||||
|
||||
use super::cell::{UserDataBorrowMut, UserDataBorrowRef, UserDataVariant};
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
use {
|
||||
crate::types::AsyncCallback,
|
||||
std::future::{self, Future},
|
||||
};
|
||||
|
||||
type StaticFieldCallback = Box<dyn FnOnce(&RawLua) -> Result<()> + 'static>;
|
||||
|
||||
/// Handle to registry for userdata methods and metamethods.
|
||||
pub struct UserDataRegistry<T: 'static> {
|
||||
// Fields
|
||||
pub(crate) fields: Vec<(String, StaticFieldCallback)>,
|
||||
pub(crate) field_getters: Vec<(String, Callback)>,
|
||||
pub(crate) field_setters: Vec<(String, Callback)>,
|
||||
pub(crate) meta_fields: Vec<(String, StaticFieldCallback)>,
|
||||
|
||||
// Methods
|
||||
pub(crate) methods: Vec<(String, Callback)>,
|
||||
#[cfg(feature = "async")]
|
||||
pub(crate) async_methods: Vec<(String, AsyncCallback)>,
|
||||
pub(crate) meta_methods: Vec<(String, Callback)>,
|
||||
#[cfg(feature = "async")]
|
||||
pub(crate) async_meta_methods: Vec<(String, AsyncCallback)>,
|
||||
|
||||
_type: PhantomData<T>,
|
||||
}
|
||||
|
||||
impl<T: 'static> UserDataRegistry<T> {
|
||||
pub(crate) const fn new() -> Self {
|
||||
UserDataRegistry {
|
||||
fields: Vec::new(),
|
||||
field_getters: Vec::new(),
|
||||
field_setters: Vec::new(),
|
||||
meta_fields: Vec::new(),
|
||||
methods: Vec::new(),
|
||||
#[cfg(feature = "async")]
|
||||
async_methods: Vec::new(),
|
||||
meta_methods: Vec::new(),
|
||||
#[cfg(feature = "async")]
|
||||
async_meta_methods: Vec::new(),
|
||||
_type: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
fn box_method<M, A, R>(name: &str, method: M) -> Callback
|
||||
where
|
||||
M: Fn(&Lua, &T, A) -> Result<R> + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
R: IntoLuaMulti,
|
||||
{
|
||||
let name = get_function_name::<T>(name);
|
||||
macro_rules! try_self_arg {
|
||||
($res:expr) => {
|
||||
$res.map_err(|err| Error::bad_self_argument(&name, err))?
|
||||
};
|
||||
}
|
||||
|
||||
Box::new(move |rawlua, nargs| unsafe {
|
||||
if nargs == 0 {
|
||||
let err = Error::from_lua_conversion("missing argument", "userdata", None);
|
||||
try_self_arg!(Err(err));
|
||||
}
|
||||
let state = rawlua.state();
|
||||
// Find absolute "self" index before processing args
|
||||
let self_index = ffi::lua_absindex(state, -nargs);
|
||||
// Self was at position 1, so we pass 2 here
|
||||
let args = A::from_stack_args(nargs - 1, 2, Some(&name), rawlua);
|
||||
|
||||
match try_self_arg!(rawlua.get_userdata_type_id(self_index)) {
|
||||
Some(id) if id == TypeId::of::<T>() => {
|
||||
let ud = try_self_arg!(borrow_userdata_ref::<T>(state, self_index));
|
||||
method(rawlua.lua(), &ud, args?)?.push_into_stack_multi(rawlua)
|
||||
}
|
||||
_ => Err(Error::bad_self_argument(&name, Error::UserDataTypeMismatch)),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn box_method_mut<M, A, R>(name: &str, method: M) -> Callback
|
||||
where
|
||||
M: FnMut(&Lua, &mut T, A) -> Result<R> + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
R: IntoLuaMulti,
|
||||
{
|
||||
let name = get_function_name::<T>(name);
|
||||
macro_rules! try_self_arg {
|
||||
($res:expr) => {
|
||||
$res.map_err(|err| Error::bad_self_argument(&name, err))?
|
||||
};
|
||||
}
|
||||
|
||||
let method = RefCell::new(method);
|
||||
Box::new(move |rawlua, nargs| unsafe {
|
||||
let mut method = method.try_borrow_mut().map_err(|_| Error::RecursiveMutCallback)?;
|
||||
if nargs == 0 {
|
||||
let err = Error::from_lua_conversion("missing argument", "userdata", None);
|
||||
try_self_arg!(Err(err));
|
||||
}
|
||||
let state = rawlua.state();
|
||||
// Find absolute "self" index before processing args
|
||||
let self_index = ffi::lua_absindex(state, -nargs);
|
||||
// Self was at position 1, so we pass 2 here
|
||||
let args = A::from_stack_args(nargs - 1, 2, Some(&name), rawlua);
|
||||
|
||||
match try_self_arg!(rawlua.get_userdata_type_id(self_index)) {
|
||||
Some(id) if id == TypeId::of::<T>() => {
|
||||
let mut ud = try_self_arg!(borrow_userdata_mut::<T>(state, self_index));
|
||||
method(rawlua.lua(), &mut ud, args?)?.push_into_stack_multi(rawlua)
|
||||
}
|
||||
_ => Err(Error::bad_self_argument(&name, Error::UserDataTypeMismatch)),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
fn box_async_method<M, A, MR, R>(name: &str, method: M) -> AsyncCallback
|
||||
where
|
||||
M: Fn(Lua, UserDataRef<T>, A) -> MR + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
MR: Future<Output = Result<R>> + MaybeSend + 'static,
|
||||
R: IntoLuaMulti,
|
||||
{
|
||||
let name = get_function_name::<T>(name);
|
||||
macro_rules! try_self_arg {
|
||||
($res:expr) => {
|
||||
match $res {
|
||||
Ok(res) => res,
|
||||
Err(err) => return Box::pin(future::ready(Err(Error::bad_self_argument(&name, err)))),
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
Box::new(move |rawlua, nargs| unsafe {
|
||||
if nargs == 0 {
|
||||
let err = Error::from_lua_conversion("missing argument", "userdata", None);
|
||||
try_self_arg!(Err(err));
|
||||
}
|
||||
// Stack will be empty when polling the future, keep `self` on the ref thread
|
||||
let self_ud = try_self_arg!(AnyUserData::from_stack(-nargs, rawlua));
|
||||
let args = A::from_stack_args(nargs - 1, 2, Some(&name), rawlua);
|
||||
|
||||
let self_ud = try_self_arg!(self_ud.borrow());
|
||||
let args = match args {
|
||||
Ok(args) => args,
|
||||
Err(e) => return Box::pin(future::ready(Err(e))),
|
||||
};
|
||||
let lua = rawlua.lua();
|
||||
let fut = method(lua.clone(), self_ud, args);
|
||||
// Lua is locked when the future is polled
|
||||
Box::pin(async move { fut.await?.push_into_stack_multi(lua.raw_lua()) })
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
fn box_async_method_mut<M, A, MR, R>(name: &str, method: M) -> AsyncCallback
|
||||
where
|
||||
M: Fn(Lua, UserDataRefMut<T>, A) -> MR + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
MR: Future<Output = Result<R>> + MaybeSend + 'static,
|
||||
R: IntoLuaMulti,
|
||||
{
|
||||
let name = get_function_name::<T>(name);
|
||||
macro_rules! try_self_arg {
|
||||
($res:expr) => {
|
||||
match $res {
|
||||
Ok(res) => res,
|
||||
Err(err) => return Box::pin(future::ready(Err(Error::bad_self_argument(&name, err)))),
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
Box::new(move |rawlua, nargs| unsafe {
|
||||
if nargs == 0 {
|
||||
let err = Error::from_lua_conversion("missing argument", "userdata", None);
|
||||
try_self_arg!(Err(err));
|
||||
}
|
||||
// Stack will be empty when polling the future, keep `self` on the ref thread
|
||||
let self_ud = try_self_arg!(AnyUserData::from_stack(-nargs, rawlua));
|
||||
let args = A::from_stack_args(nargs - 1, 2, Some(&name), rawlua);
|
||||
|
||||
let self_ud = try_self_arg!(self_ud.borrow_mut());
|
||||
let args = match args {
|
||||
Ok(args) => args,
|
||||
Err(e) => return Box::pin(future::ready(Err(e))),
|
||||
};
|
||||
let lua = rawlua.lua();
|
||||
let fut = method(lua.clone(), self_ud, args);
|
||||
// Lua is locked when the future is polled
|
||||
Box::pin(async move { fut.await?.push_into_stack_multi(lua.raw_lua()) })
|
||||
})
|
||||
}
|
||||
|
||||
fn box_function<F, A, R>(name: &str, function: F) -> Callback
|
||||
where
|
||||
F: Fn(&Lua, A) -> Result<R> + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
R: IntoLuaMulti,
|
||||
{
|
||||
let name = get_function_name::<T>(name);
|
||||
Box::new(move |lua, nargs| unsafe {
|
||||
let args = A::from_stack_args(nargs, 1, Some(&name), lua)?;
|
||||
function(lua.lua(), args)?.push_into_stack_multi(lua)
|
||||
})
|
||||
}
|
||||
|
||||
fn box_function_mut<F, A, R>(name: &str, function: F) -> Callback
|
||||
where
|
||||
F: FnMut(&Lua, A) -> Result<R> + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
R: IntoLuaMulti,
|
||||
{
|
||||
let name = get_function_name::<T>(name);
|
||||
let function = RefCell::new(function);
|
||||
Box::new(move |lua, nargs| unsafe {
|
||||
let function = &mut *function
|
||||
.try_borrow_mut()
|
||||
.map_err(|_| Error::RecursiveMutCallback)?;
|
||||
let args = A::from_stack_args(nargs, 1, Some(&name), lua)?;
|
||||
function(lua.lua(), args)?.push_into_stack_multi(lua)
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
fn box_async_function<F, A, FR, R>(name: &str, function: F) -> AsyncCallback
|
||||
where
|
||||
F: Fn(Lua, A) -> FR + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
FR: Future<Output = Result<R>> + MaybeSend + 'static,
|
||||
R: IntoLuaMulti,
|
||||
{
|
||||
let name = get_function_name::<T>(name);
|
||||
Box::new(move |rawlua, nargs| unsafe {
|
||||
let args = match A::from_stack_args(nargs, 1, Some(&name), rawlua) {
|
||||
Ok(args) => args,
|
||||
Err(e) => return Box::pin(future::ready(Err(e))),
|
||||
};
|
||||
let lua = rawlua.lua();
|
||||
let fut = function(lua.clone(), args);
|
||||
Box::pin(async move { fut.await?.push_into_stack_multi(lua.raw_lua()) })
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn check_meta_field(lua: &Lua, name: &str, value: impl IntoLua) -> Result<Value> {
|
||||
let value = value.into_lua(lua)?;
|
||||
if name == MetaMethod::Index || name == MetaMethod::NewIndex {
|
||||
match value {
|
||||
Value::Nil | Value::Table(_) | Value::Function(_) => {}
|
||||
_ => {
|
||||
return Err(Error::MetaMethodTypeError {
|
||||
method: name.to_string(),
|
||||
type_name: value.type_name(),
|
||||
message: Some("expected nil, table or function".to_string()),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
value.into_lua(lua)
|
||||
}
|
||||
}
|
||||
|
||||
// Returns function name for the type `T`, without the module path
|
||||
fn get_function_name<T>(name: &str) -> StdString {
|
||||
format!("{}.{name}", short_type_name::<T>())
|
||||
}
|
||||
|
||||
impl<T: 'static> UserDataFields<T> for UserDataRegistry<T> {
|
||||
fn add_field<V>(&mut self, name: impl ToString, value: V)
|
||||
where
|
||||
V: IntoLua + 'static,
|
||||
{
|
||||
let name = name.to_string();
|
||||
self.fields.push((
|
||||
name,
|
||||
Box::new(move |rawlua| unsafe { value.push_into_stack(rawlua) }),
|
||||
));
|
||||
}
|
||||
|
||||
fn add_field_method_get<M, R>(&mut self, name: impl ToString, method: M)
|
||||
where
|
||||
M: Fn(&Lua, &T) -> Result<R> + MaybeSend + 'static,
|
||||
R: IntoLua,
|
||||
{
|
||||
let name = name.to_string();
|
||||
let callback = Self::box_method(&name, move |lua, data, ()| method(lua, data));
|
||||
self.field_getters.push((name, callback));
|
||||
}
|
||||
|
||||
fn add_field_method_set<M, A>(&mut self, name: impl ToString, method: M)
|
||||
where
|
||||
M: FnMut(&Lua, &mut T, A) -> Result<()> + MaybeSend + 'static,
|
||||
A: FromLua,
|
||||
{
|
||||
let name = name.to_string();
|
||||
let callback = Self::box_method_mut(&name, method);
|
||||
self.field_setters.push((name, callback));
|
||||
}
|
||||
|
||||
fn add_field_function_get<F, R>(&mut self, name: impl ToString, function: F)
|
||||
where
|
||||
F: Fn(&Lua, AnyUserData) -> Result<R> + MaybeSend + 'static,
|
||||
R: IntoLua,
|
||||
{
|
||||
let name = name.to_string();
|
||||
let callback = Self::box_function(&name, function);
|
||||
self.field_getters.push((name, callback));
|
||||
}
|
||||
|
||||
fn add_field_function_set<F, A>(&mut self, name: impl ToString, mut function: F)
|
||||
where
|
||||
F: FnMut(&Lua, AnyUserData, A) -> Result<()> + MaybeSend + 'static,
|
||||
A: FromLua,
|
||||
{
|
||||
let name = name.to_string();
|
||||
let callback = Self::box_function_mut(&name, move |lua, (data, val)| function(lua, data, val));
|
||||
self.field_setters.push((name, callback));
|
||||
}
|
||||
|
||||
fn add_meta_field<V>(&mut self, name: impl ToString, value: V)
|
||||
where
|
||||
V: IntoLua + 'static,
|
||||
{
|
||||
let name = name.to_string();
|
||||
self.meta_fields.push((
|
||||
name.clone(),
|
||||
Box::new(move |rawlua| unsafe {
|
||||
Self::check_meta_field(rawlua.lua(), &name, value)?.push_into_stack(rawlua)
|
||||
}),
|
||||
));
|
||||
}
|
||||
|
||||
fn add_meta_field_with<F, R>(&mut self, name: impl ToString, f: F)
|
||||
where
|
||||
F: FnOnce(&Lua) -> Result<R> + 'static,
|
||||
R: IntoLua,
|
||||
{
|
||||
let name = name.to_string();
|
||||
self.meta_fields.push((
|
||||
name.clone(),
|
||||
Box::new(move |rawlua| unsafe {
|
||||
let lua = rawlua.lua();
|
||||
Self::check_meta_field(lua, &name, f(lua)?)?.push_into_stack(rawlua)
|
||||
}),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: 'static> UserDataMethods<T> for UserDataRegistry<T> {
|
||||
fn add_method<M, A, R>(&mut self, name: impl ToString, method: M)
|
||||
where
|
||||
M: Fn(&Lua, &T, A) -> Result<R> + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
R: IntoLuaMulti,
|
||||
{
|
||||
let name = name.to_string();
|
||||
let callback = Self::box_method(&name, method);
|
||||
self.methods.push((name, callback));
|
||||
}
|
||||
|
||||
fn add_method_mut<M, A, R>(&mut self, name: impl ToString, method: M)
|
||||
where
|
||||
M: FnMut(&Lua, &mut T, A) -> Result<R> + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
R: IntoLuaMulti,
|
||||
{
|
||||
let name = name.to_string();
|
||||
let callback = Self::box_method_mut(&name, method);
|
||||
self.methods.push((name, callback));
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
fn add_async_method<M, A, MR, R>(&mut self, name: impl ToString, method: M)
|
||||
where
|
||||
M: Fn(Lua, UserDataRef<T>, A) -> MR + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
MR: Future<Output = Result<R>> + MaybeSend + 'static,
|
||||
R: IntoLuaMulti,
|
||||
{
|
||||
let name = name.to_string();
|
||||
let callback = Self::box_async_method(&name, method);
|
||||
self.async_methods.push((name, callback));
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
fn add_async_method_mut<M, A, MR, R>(&mut self, name: impl ToString, method: M)
|
||||
where
|
||||
M: Fn(Lua, UserDataRefMut<T>, A) -> MR + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
MR: Future<Output = Result<R>> + MaybeSend + 'static,
|
||||
R: IntoLuaMulti,
|
||||
{
|
||||
let name = name.to_string();
|
||||
let callback = Self::box_async_method_mut(&name, method);
|
||||
self.async_methods.push((name, callback));
|
||||
}
|
||||
|
||||
fn add_function<F, A, R>(&mut self, name: impl ToString, function: F)
|
||||
where
|
||||
F: Fn(&Lua, A) -> Result<R> + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
R: IntoLuaMulti,
|
||||
{
|
||||
let name = name.to_string();
|
||||
let callback = Self::box_function(&name, function);
|
||||
self.methods.push((name, callback));
|
||||
}
|
||||
|
||||
fn add_function_mut<F, A, R>(&mut self, name: impl ToString, function: F)
|
||||
where
|
||||
F: FnMut(&Lua, A) -> Result<R> + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
R: IntoLuaMulti,
|
||||
{
|
||||
let name = name.to_string();
|
||||
let callback = Self::box_function_mut(&name, function);
|
||||
self.methods.push((name, callback));
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
fn add_async_function<F, A, FR, R>(&mut self, name: impl ToString, function: F)
|
||||
where
|
||||
F: Fn(Lua, A) -> FR + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
FR: Future<Output = Result<R>> + MaybeSend + 'static,
|
||||
R: IntoLuaMulti,
|
||||
{
|
||||
let name = name.to_string();
|
||||
let callback = Self::box_async_function(&name, function);
|
||||
self.async_methods.push((name, callback));
|
||||
}
|
||||
|
||||
fn add_meta_method<M, A, R>(&mut self, name: impl ToString, method: M)
|
||||
where
|
||||
M: Fn(&Lua, &T, A) -> Result<R> + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
R: IntoLuaMulti,
|
||||
{
|
||||
let name = name.to_string();
|
||||
let callback = Self::box_method(&name, method);
|
||||
self.meta_methods.push((name, callback));
|
||||
}
|
||||
|
||||
fn add_meta_method_mut<M, A, R>(&mut self, name: impl ToString, method: M)
|
||||
where
|
||||
M: FnMut(&Lua, &mut T, A) -> Result<R> + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
R: IntoLuaMulti,
|
||||
{
|
||||
let name = name.to_string();
|
||||
let callback = Self::box_method_mut(&name, method);
|
||||
self.meta_methods.push((name, callback));
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "async", not(any(feature = "lua51", feature = "luau"))))]
|
||||
fn add_async_meta_method<M, A, MR, R>(&mut self, name: impl ToString, method: M)
|
||||
where
|
||||
M: Fn(Lua, UserDataRef<T>, A) -> MR + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
MR: Future<Output = Result<R>> + MaybeSend + 'static,
|
||||
R: IntoLuaMulti,
|
||||
{
|
||||
let name = name.to_string();
|
||||
let callback = Self::box_async_method(&name, method);
|
||||
self.async_meta_methods.push((name, callback));
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "async", not(any(feature = "lua51", feature = "luau"))))]
|
||||
fn add_async_meta_method_mut<M, A, MR, R>(&mut self, name: impl ToString, method: M)
|
||||
where
|
||||
M: Fn(Lua, UserDataRefMut<T>, A) -> MR + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
MR: Future<Output = Result<R>> + MaybeSend + 'static,
|
||||
R: IntoLuaMulti,
|
||||
{
|
||||
let name = name.to_string();
|
||||
let callback = Self::box_async_method_mut(&name, method);
|
||||
self.async_meta_methods.push((name, callback));
|
||||
}
|
||||
|
||||
fn add_meta_function<F, A, R>(&mut self, name: impl ToString, function: F)
|
||||
where
|
||||
F: Fn(&Lua, A) -> Result<R> + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
R: IntoLuaMulti,
|
||||
{
|
||||
let name = name.to_string();
|
||||
let callback = Self::box_function(&name, function);
|
||||
self.meta_methods.push((name, callback));
|
||||
}
|
||||
|
||||
fn add_meta_function_mut<F, A, R>(&mut self, name: impl ToString, function: F)
|
||||
where
|
||||
F: FnMut(&Lua, A) -> Result<R> + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
R: IntoLuaMulti,
|
||||
{
|
||||
let name = name.to_string();
|
||||
let callback = Self::box_function_mut(&name, function);
|
||||
self.meta_methods.push((name, callback));
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "async", not(any(feature = "lua51", feature = "luau"))))]
|
||||
fn add_async_meta_function<F, A, FR, R>(&mut self, name: impl ToString, function: F)
|
||||
where
|
||||
F: Fn(Lua, A) -> FR + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
FR: Future<Output = Result<R>> + MaybeSend + 'static,
|
||||
R: IntoLuaMulti,
|
||||
{
|
||||
let name = name.to_string();
|
||||
let callback = Self::box_async_function(&name, function);
|
||||
self.async_meta_methods.push((name, callback));
|
||||
}
|
||||
}
|
||||
|
||||
// Borrow the userdata in-place from the Lua stack
|
||||
#[inline(always)]
|
||||
unsafe fn borrow_userdata_ref<'a, T>(
|
||||
state: *mut ffi::lua_State,
|
||||
index: c_int,
|
||||
) -> Result<UserDataBorrowRef<'a, T>> {
|
||||
let ud = get_userdata::<UserDataVariant<T>>(state, index);
|
||||
(*ud).try_borrow()
|
||||
}
|
||||
|
||||
// Borrow the userdata mutably in-place from the Lua stack
|
||||
#[inline(always)]
|
||||
unsafe fn borrow_userdata_mut<'a, T>(
|
||||
state: *mut ffi::lua_State,
|
||||
index: c_int,
|
||||
) -> Result<UserDataBorrowMut<'a, T>> {
|
||||
let ud = get_userdata::<UserDataVariant<T>>(state, index);
|
||||
(*ud).try_borrow_mut()
|
||||
}
|
||||
|
||||
macro_rules! lua_userdata_impl {
|
||||
($type:ty) => {
|
||||
impl<T: UserData + 'static> UserData for $type {
|
||||
fn register(registry: &mut UserDataRegistry<Self>) {
|
||||
let mut orig_registry = UserDataRegistry::new();
|
||||
T::register(&mut orig_registry);
|
||||
|
||||
// Copy all fields, methods, etc. from the original registry
|
||||
registry.fields.extend(orig_registry.fields);
|
||||
registry.field_getters.extend(orig_registry.field_getters);
|
||||
registry.field_setters.extend(orig_registry.field_setters);
|
||||
registry.meta_fields.extend(orig_registry.meta_fields);
|
||||
registry.methods.extend(orig_registry.methods);
|
||||
#[cfg(feature = "async")]
|
||||
registry.async_methods.extend(orig_registry.async_methods);
|
||||
registry.meta_methods.extend(orig_registry.meta_methods);
|
||||
#[cfg(feature = "async")]
|
||||
registry
|
||||
.async_meta_methods
|
||||
.extend(orig_registry.async_meta_methods);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// A special proxy object for UserData
|
||||
pub(crate) struct UserDataProxy<T>(pub(crate) PhantomData<T>);
|
||||
|
||||
lua_userdata_impl!(UserDataProxy<T>);
|
||||
@@ -1,190 +0,0 @@
|
||||
use crate::error::{Error, Result};
|
||||
use crate::private::Sealed;
|
||||
use crate::userdata::{AnyUserData, MetaMethod};
|
||||
use crate::value::{FromLua, FromLuaMulti, IntoLua, IntoLuaMulti, Value};
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
use futures_util::future::{self, LocalBoxFuture};
|
||||
|
||||
/// An extension trait for [`AnyUserData`] that provides a variety of convenient functionality.
|
||||
pub trait AnyUserDataExt<'lua>: Sealed {
|
||||
/// Gets the value associated to `key` from the userdata, assuming it has `__index` metamethod.
|
||||
fn get<K: IntoLua<'lua>, V: FromLua<'lua>>(&self, key: K) -> Result<V>;
|
||||
|
||||
/// Sets the value associated to `key` in the userdata, assuming it has `__newindex` metamethod.
|
||||
fn set<K: IntoLua<'lua>, V: IntoLua<'lua>>(&self, key: K, value: V) -> Result<()>;
|
||||
|
||||
/// Calls the userdata as a function assuming it has `__call` metamethod.
|
||||
///
|
||||
/// The metamethod is called with the userdata as its first argument, followed by the passed arguments.
|
||||
fn call<A, R>(&self, args: A) -> Result<R>
|
||||
where
|
||||
A: IntoLuaMulti<'lua>,
|
||||
R: FromLuaMulti<'lua>;
|
||||
|
||||
/// Asynchronously calls the userdata as a function assuming it has `__call` metamethod.
|
||||
///
|
||||
/// The metamethod is called with the userdata as its first argument, followed by the passed arguments.
|
||||
#[cfg(feature = "async")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
|
||||
fn call_async<A, R>(&self, args: A) -> LocalBoxFuture<'lua, Result<R>>
|
||||
where
|
||||
A: IntoLuaMulti<'lua>,
|
||||
R: FromLuaMulti<'lua> + 'lua;
|
||||
|
||||
/// Calls the userdata method, assuming it has `__index` metamethod
|
||||
/// and a function associated to `name`.
|
||||
fn call_method<A, R>(&self, name: &str, args: A) -> Result<R>
|
||||
where
|
||||
A: IntoLuaMulti<'lua>,
|
||||
R: FromLuaMulti<'lua>;
|
||||
|
||||
/// Gets the function associated to `key` from the table and asynchronously executes it,
|
||||
/// passing the table itself along with `args` as function arguments and returning Future.
|
||||
///
|
||||
/// Requires `feature = "async"`
|
||||
///
|
||||
/// This might invoke the `__index` metamethod.
|
||||
#[cfg(feature = "async")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
|
||||
fn call_async_method<A, R>(&self, name: &str, args: A) -> LocalBoxFuture<'lua, Result<R>>
|
||||
where
|
||||
A: IntoLuaMulti<'lua>,
|
||||
R: FromLuaMulti<'lua> + 'lua;
|
||||
|
||||
/// Gets the function associated to `key` from the table and executes it,
|
||||
/// passing `args` as function arguments.
|
||||
///
|
||||
/// This is a shortcut for
|
||||
/// `table.get::<_, Function>(key)?.call(args)`
|
||||
///
|
||||
/// This might invoke the `__index` metamethod.
|
||||
fn call_function<A, R>(&self, name: &str, args: A) -> Result<R>
|
||||
where
|
||||
A: IntoLuaMulti<'lua>,
|
||||
R: FromLuaMulti<'lua>;
|
||||
|
||||
/// Gets the function associated to `key` from the table and asynchronously executes it,
|
||||
/// passing `args` as function arguments and returning Future.
|
||||
///
|
||||
/// Requires `feature = "async"`
|
||||
///
|
||||
/// This might invoke the `__index` metamethod.
|
||||
#[cfg(feature = "async")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
|
||||
fn call_async_function<A, R>(&self, name: &str, args: A) -> LocalBoxFuture<'lua, Result<R>>
|
||||
where
|
||||
A: IntoLuaMulti<'lua>,
|
||||
R: FromLuaMulti<'lua> + 'lua;
|
||||
}
|
||||
|
||||
impl<'lua> AnyUserDataExt<'lua> for AnyUserData<'lua> {
|
||||
fn get<K: IntoLua<'lua>, V: FromLua<'lua>>(&self, key: K) -> Result<V> {
|
||||
let metatable = self.get_metatable()?;
|
||||
match metatable.get::<Value>(MetaMethod::Index)? {
|
||||
Value::Table(table) => table.raw_get(key),
|
||||
Value::Function(func) => func.call((self.clone(), key)),
|
||||
_ => Err(Error::runtime("attempt to index a userdata value")),
|
||||
}
|
||||
}
|
||||
|
||||
fn set<K: IntoLua<'lua>, V: IntoLua<'lua>>(&self, key: K, value: V) -> Result<()> {
|
||||
let metatable = self.get_metatable()?;
|
||||
match metatable.get::<Value>(MetaMethod::NewIndex)? {
|
||||
Value::Table(table) => table.raw_set(key, value),
|
||||
Value::Function(func) => func.call((self.clone(), key, value)),
|
||||
_ => Err(Error::runtime("attempt to index a userdata value")),
|
||||
}
|
||||
}
|
||||
|
||||
fn call<A, R>(&self, args: A) -> Result<R>
|
||||
where
|
||||
A: IntoLuaMulti<'lua>,
|
||||
R: FromLuaMulti<'lua>,
|
||||
{
|
||||
let metatable = self.get_metatable()?;
|
||||
match metatable.get::<Value>(MetaMethod::Call)? {
|
||||
Value::Function(func) => func.call((self.clone(), args)),
|
||||
_ => Err(Error::runtime("attempt to call a userdata value")),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
fn call_async<A, R>(&self, args: A) -> LocalBoxFuture<'lua, Result<R>>
|
||||
where
|
||||
A: IntoLuaMulti<'lua>,
|
||||
R: FromLuaMulti<'lua> + 'lua,
|
||||
{
|
||||
let metatable = match self.get_metatable() {
|
||||
Ok(metatable) => metatable,
|
||||
Err(err) => return Box::pin(future::err(err)),
|
||||
};
|
||||
match metatable.get::<Value>(MetaMethod::Call) {
|
||||
Ok(Value::Function(func)) => {
|
||||
let mut args = match args.into_lua_multi(self.0.lua) {
|
||||
Ok(args) => args,
|
||||
Err(e) => return Box::pin(future::err(e)),
|
||||
};
|
||||
args.push_front(Value::UserData(self.clone()));
|
||||
Box::pin(async move { func.call_async(args).await })
|
||||
}
|
||||
Ok(_) => Box::pin(future::err(Error::runtime(
|
||||
"attempt to call a userdata value",
|
||||
))),
|
||||
Err(err) => Box::pin(future::err(err)),
|
||||
}
|
||||
}
|
||||
|
||||
fn call_method<A, R>(&self, name: &str, args: A) -> Result<R>
|
||||
where
|
||||
A: IntoLuaMulti<'lua>,
|
||||
R: FromLuaMulti<'lua>,
|
||||
{
|
||||
self.call_function(name, (self.clone(), args))
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
fn call_async_method<A, R>(&self, name: &str, args: A) -> LocalBoxFuture<'lua, Result<R>>
|
||||
where
|
||||
A: IntoLuaMulti<'lua>,
|
||||
R: FromLuaMulti<'lua> + 'lua,
|
||||
{
|
||||
self.call_async_function(name, (self.clone(), args))
|
||||
}
|
||||
|
||||
fn call_function<A, R>(&self, name: &str, args: A) -> Result<R>
|
||||
where
|
||||
A: IntoLuaMulti<'lua>,
|
||||
R: FromLuaMulti<'lua>,
|
||||
{
|
||||
match self.get(name)? {
|
||||
Value::Function(func) => func.call(args),
|
||||
val => {
|
||||
let msg = format!("attempt to call a {} value", val.type_name());
|
||||
Err(Error::runtime(msg))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
fn call_async_function<A, R>(&self, name: &str, args: A) -> LocalBoxFuture<'lua, Result<R>>
|
||||
where
|
||||
A: IntoLuaMulti<'lua>,
|
||||
R: FromLuaMulti<'lua> + 'lua,
|
||||
{
|
||||
match self.get(name) {
|
||||
Ok(Value::Function(func)) => {
|
||||
let args = match args.into_lua_multi(self.0.lua) {
|
||||
Ok(args) => args,
|
||||
Err(e) => return Box::pin(future::err(e)),
|
||||
};
|
||||
Box::pin(async move { func.call_async(args).await })
|
||||
}
|
||||
Ok(val) => {
|
||||
let msg = format!("attempt to call a {} value", val.type_name());
|
||||
Box::pin(future::err(Error::runtime(msg)))
|
||||
}
|
||||
Err(err) => Box::pin(future::err(err)),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,797 +0,0 @@
|
||||
#![allow(clippy::await_holding_refcell_ref, clippy::await_holding_lock)]
|
||||
|
||||
use std::any::TypeId;
|
||||
use std::cell::{Ref, RefCell, RefMut};
|
||||
use std::marker::PhantomData;
|
||||
use std::os::raw::c_int;
|
||||
use std::string::String as StdString;
|
||||
use std::sync::{Arc, Mutex, RwLock};
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
use crate::lua::Lua;
|
||||
use crate::types::{Callback, MaybeSend};
|
||||
use crate::userdata::{
|
||||
AnyUserData, MetaMethod, UserData, UserDataCell, UserDataFields, UserDataMethods,
|
||||
};
|
||||
use crate::util::{get_userdata, short_type_name};
|
||||
use crate::value::{FromLua, FromLuaMulti, IntoLua, IntoLuaMulti, Value};
|
||||
|
||||
#[cfg(not(feature = "send"))]
|
||||
use std::rc::Rc;
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
use {crate::types::AsyncCallback, futures_util::future, std::future::Future};
|
||||
|
||||
/// Handle to registry for userdata methods and metamethods.
|
||||
pub struct UserDataRegistry<'lua, T: 'static> {
|
||||
// Fields
|
||||
pub(crate) fields: Vec<(String, Callback<'lua, 'static>)>,
|
||||
pub(crate) field_getters: Vec<(String, Callback<'lua, 'static>)>,
|
||||
pub(crate) field_setters: Vec<(String, Callback<'lua, 'static>)>,
|
||||
pub(crate) meta_fields: Vec<(String, Callback<'lua, 'static>)>,
|
||||
|
||||
// Methods
|
||||
pub(crate) methods: Vec<(String, Callback<'lua, 'static>)>,
|
||||
#[cfg(feature = "async")]
|
||||
pub(crate) async_methods: Vec<(String, AsyncCallback<'lua, 'static>)>,
|
||||
pub(crate) meta_methods: Vec<(String, Callback<'lua, 'static>)>,
|
||||
#[cfg(feature = "async")]
|
||||
pub(crate) async_meta_methods: Vec<(String, AsyncCallback<'lua, 'static>)>,
|
||||
|
||||
_type: PhantomData<T>,
|
||||
}
|
||||
|
||||
impl<'lua, T: 'static> UserDataRegistry<'lua, T> {
|
||||
pub(crate) const fn new() -> Self {
|
||||
UserDataRegistry {
|
||||
fields: Vec::new(),
|
||||
field_getters: Vec::new(),
|
||||
field_setters: Vec::new(),
|
||||
meta_fields: Vec::new(),
|
||||
methods: Vec::new(),
|
||||
#[cfg(feature = "async")]
|
||||
async_methods: Vec::new(),
|
||||
meta_methods: Vec::new(),
|
||||
#[cfg(feature = "async")]
|
||||
async_meta_methods: Vec::new(),
|
||||
_type: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
fn box_method<M, A, R>(name: &str, method: M) -> Callback<'lua, 'static>
|
||||
where
|
||||
M: Fn(&'lua Lua, &T, A) -> Result<R> + MaybeSend + 'static,
|
||||
A: FromLuaMulti<'lua>,
|
||||
R: IntoLuaMulti<'lua>,
|
||||
{
|
||||
let name = get_function_name::<T>(name);
|
||||
macro_rules! try_self_arg {
|
||||
($res:expr) => {
|
||||
$res.map_err(|err| Error::bad_self_argument(&name, err))?
|
||||
};
|
||||
($res:expr, $err:expr) => {
|
||||
$res.map_err(|_| Error::bad_self_argument(&name, $err))?
|
||||
};
|
||||
}
|
||||
|
||||
Box::new(move |lua, nargs| unsafe {
|
||||
if nargs == 0 {
|
||||
let err = Error::from_lua_conversion("missing argument", "userdata", None);
|
||||
try_self_arg!(Err(err));
|
||||
}
|
||||
let state = lua.state();
|
||||
// Find absolute "self" index before processing args
|
||||
let index = ffi::lua_absindex(state, -nargs);
|
||||
// Self was at position 1, so we pass 2 here
|
||||
let args = A::from_stack_args(nargs - 1, 2, Some(&name), lua);
|
||||
|
||||
match try_self_arg!(lua.get_userdata_type_id(index)) {
|
||||
Some(id) if id == TypeId::of::<T>() => {
|
||||
let ud = try_self_arg!(get_userdata_ref::<T>(state, index));
|
||||
method(lua, &ud, args?)?.push_into_stack_multi(lua)
|
||||
}
|
||||
#[cfg(not(feature = "send"))]
|
||||
Some(id) if id == TypeId::of::<Rc<T>>() => {
|
||||
let ud = try_self_arg!(get_userdata_ref::<Rc<T>>(state, index));
|
||||
method(lua, &ud, args?)?.push_into_stack_multi(lua)
|
||||
}
|
||||
#[cfg(not(feature = "send"))]
|
||||
Some(id) if id == TypeId::of::<Rc<RefCell<T>>>() => {
|
||||
let ud = try_self_arg!(get_userdata_ref::<Rc<RefCell<T>>>(state, index));
|
||||
let ud = try_self_arg!(ud.try_borrow(), Error::UserDataBorrowError);
|
||||
method(lua, &ud, args?)?.push_into_stack_multi(lua)
|
||||
}
|
||||
Some(id) if id == TypeId::of::<Arc<T>>() => {
|
||||
let ud = try_self_arg!(get_userdata_ref::<Arc<T>>(state, index));
|
||||
method(lua, &ud, args?)?.push_into_stack_multi(lua)
|
||||
}
|
||||
Some(id) if id == TypeId::of::<Arc<Mutex<T>>>() => {
|
||||
let ud = try_self_arg!(get_userdata_ref::<Arc<Mutex<T>>>(state, index));
|
||||
let ud = try_self_arg!(ud.try_lock(), Error::UserDataBorrowError);
|
||||
method(lua, &ud, args?)?.push_into_stack_multi(lua)
|
||||
}
|
||||
#[cfg(feature = "parking_lot")]
|
||||
Some(id) if id == TypeId::of::<Arc<parking_lot::Mutex<T>>>() => {
|
||||
let ud = get_userdata_ref::<Arc<parking_lot::Mutex<T>>>(state, index);
|
||||
let ud = try_self_arg!(ud);
|
||||
let ud = try_self_arg!(ud.try_lock().ok_or(Error::UserDataBorrowError));
|
||||
method(lua, &ud, args?)?.push_into_stack_multi(lua)
|
||||
}
|
||||
Some(id) if id == TypeId::of::<Arc<RwLock<T>>>() => {
|
||||
let ud = try_self_arg!(get_userdata_ref::<Arc<RwLock<T>>>(state, index));
|
||||
let ud = try_self_arg!(ud.try_read(), Error::UserDataBorrowError);
|
||||
method(lua, &ud, args?)?.push_into_stack_multi(lua)
|
||||
}
|
||||
#[cfg(feature = "parking_lot")]
|
||||
Some(id) if id == TypeId::of::<Arc<parking_lot::RwLock<T>>>() => {
|
||||
let ud = get_userdata_ref::<Arc<parking_lot::RwLock<T>>>(state, index);
|
||||
let ud = try_self_arg!(ud);
|
||||
let ud = try_self_arg!(ud.try_read().ok_or(Error::UserDataBorrowError));
|
||||
method(lua, &ud, args?)?.push_into_stack_multi(lua)
|
||||
}
|
||||
_ => Err(Error::bad_self_argument(&name, Error::UserDataTypeMismatch)),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn box_method_mut<M, A, R>(name: &str, method: M) -> Callback<'lua, 'static>
|
||||
where
|
||||
M: FnMut(&'lua Lua, &mut T, A) -> Result<R> + MaybeSend + 'static,
|
||||
A: FromLuaMulti<'lua>,
|
||||
R: IntoLuaMulti<'lua>,
|
||||
{
|
||||
let name = get_function_name::<T>(name);
|
||||
macro_rules! try_self_arg {
|
||||
($res:expr) => {
|
||||
$res.map_err(|err| Error::bad_self_argument(&name, err))?
|
||||
};
|
||||
($res:expr, $err:expr) => {
|
||||
$res.map_err(|_| Error::bad_self_argument(&name, $err))?
|
||||
};
|
||||
}
|
||||
|
||||
let method = RefCell::new(method);
|
||||
Box::new(move |lua, nargs| unsafe {
|
||||
let mut method = method
|
||||
.try_borrow_mut()
|
||||
.map_err(|_| Error::RecursiveMutCallback)?;
|
||||
if nargs == 0 {
|
||||
let err = Error::from_lua_conversion("missing argument", "userdata", None);
|
||||
try_self_arg!(Err(err));
|
||||
}
|
||||
let state = lua.state();
|
||||
// Find absolute "self" index before processing args
|
||||
let index = ffi::lua_absindex(state, -nargs);
|
||||
// Self was at position 1, so we pass 2 here
|
||||
let args = A::from_stack_args(nargs - 1, 2, Some(&name), lua);
|
||||
|
||||
match try_self_arg!(lua.get_userdata_type_id(index)) {
|
||||
Some(id) if id == TypeId::of::<T>() => {
|
||||
let mut ud = try_self_arg!(get_userdata_mut::<T>(state, index));
|
||||
method(lua, &mut ud, args?)?.push_into_stack_multi(lua)
|
||||
}
|
||||
#[cfg(not(feature = "send"))]
|
||||
Some(id) if id == TypeId::of::<Rc<T>>() => Err(Error::UserDataBorrowMutError),
|
||||
#[cfg(not(feature = "send"))]
|
||||
Some(id) if id == TypeId::of::<Rc<RefCell<T>>>() => {
|
||||
let ud = try_self_arg!(get_userdata_mut::<Rc<RefCell<T>>>(state, index));
|
||||
let mut ud = try_self_arg!(ud.try_borrow_mut(), Error::UserDataBorrowMutError);
|
||||
method(lua, &mut ud, args?)?.push_into_stack_multi(lua)
|
||||
}
|
||||
Some(id) if id == TypeId::of::<Arc<T>>() => Err(Error::UserDataBorrowMutError),
|
||||
Some(id) if id == TypeId::of::<Arc<Mutex<T>>>() => {
|
||||
let ud = try_self_arg!(get_userdata_mut::<Arc<Mutex<T>>>(state, index));
|
||||
let mut ud = try_self_arg!(ud.try_lock(), Error::UserDataBorrowMutError);
|
||||
method(lua, &mut ud, args?)?.push_into_stack_multi(lua)
|
||||
}
|
||||
#[cfg(feature = "parking_lot")]
|
||||
Some(id) if id == TypeId::of::<Arc<parking_lot::Mutex<T>>>() => {
|
||||
let ud = get_userdata_mut::<Arc<parking_lot::Mutex<T>>>(state, index);
|
||||
let ud = try_self_arg!(ud);
|
||||
let mut ud = try_self_arg!(ud.try_lock().ok_or(Error::UserDataBorrowMutError));
|
||||
method(lua, &mut ud, args?)?.push_into_stack_multi(lua)
|
||||
}
|
||||
Some(id) if id == TypeId::of::<Arc<RwLock<T>>>() => {
|
||||
let ud = try_self_arg!(get_userdata_mut::<Arc<RwLock<T>>>(state, index));
|
||||
let mut ud = try_self_arg!(ud.try_write(), Error::UserDataBorrowMutError);
|
||||
method(lua, &mut ud, args?)?.push_into_stack_multi(lua)
|
||||
}
|
||||
#[cfg(feature = "parking_lot")]
|
||||
Some(id) if id == TypeId::of::<Arc<parking_lot::RwLock<T>>>() => {
|
||||
let ud = get_userdata_mut::<Arc<parking_lot::RwLock<T>>>(state, index);
|
||||
let ud = try_self_arg!(ud);
|
||||
let mut ud = try_self_arg!(ud.try_write().ok_or(Error::UserDataBorrowMutError));
|
||||
method(lua, &mut ud, args?)?.push_into_stack_multi(lua)
|
||||
}
|
||||
_ => Err(Error::bad_self_argument(&name, Error::UserDataTypeMismatch)),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
fn box_async_method<'s, M, A, MR, R>(name: &str, method: M) -> AsyncCallback<'lua, 'static>
|
||||
where
|
||||
'lua: 's,
|
||||
T: 'static,
|
||||
M: Fn(&'lua Lua, &'s T, A) -> MR + MaybeSend + 'static,
|
||||
A: FromLuaMulti<'lua>,
|
||||
MR: Future<Output = Result<R>> + 's,
|
||||
R: IntoLuaMulti<'lua>,
|
||||
{
|
||||
let name = Arc::new(get_function_name::<T>(name));
|
||||
let method = Arc::new(method);
|
||||
|
||||
Box::new(move |lua, mut args| unsafe {
|
||||
let name = name.clone();
|
||||
let method = method.clone();
|
||||
macro_rules! try_self_arg {
|
||||
($res:expr) => {
|
||||
$res.map_err(|err| Error::bad_self_argument(&name, err))?
|
||||
};
|
||||
($res:expr, $err:expr) => {
|
||||
$res.map_err(|_| Error::bad_self_argument(&name, $err))?
|
||||
};
|
||||
}
|
||||
|
||||
Box::pin(async move {
|
||||
let this = args.pop_front().ok_or_else(|| {
|
||||
Error::from_lua_conversion("missing argument", "userdata", None)
|
||||
});
|
||||
let this = try_self_arg!(AnyUserData::from_lua(try_self_arg!(this), lua));
|
||||
let args = A::from_lua_args(args, 2, Some(&name), lua);
|
||||
|
||||
let (ref_thread, index) = (lua.ref_thread(), this.0.index);
|
||||
match try_self_arg!(this.type_id()) {
|
||||
Some(id) if id == TypeId::of::<T>() => {
|
||||
let ud = try_self_arg!(get_userdata_ref::<T>(ref_thread, index));
|
||||
let ud = std::mem::transmute::<&T, &T>(&ud);
|
||||
method(lua, ud, args?).await?.push_into_stack_multi(lua)
|
||||
}
|
||||
#[cfg(not(feature = "send"))]
|
||||
Some(id) if id == TypeId::of::<Rc<T>>() => {
|
||||
let ud = try_self_arg!(get_userdata_ref::<Rc<T>>(ref_thread, index));
|
||||
let ud = std::mem::transmute::<&T, &T>(&ud);
|
||||
method(lua, ud, args?).await?.push_into_stack_multi(lua)
|
||||
}
|
||||
#[cfg(not(feature = "send"))]
|
||||
Some(id) if id == TypeId::of::<Rc<RefCell<T>>>() => {
|
||||
let ud =
|
||||
try_self_arg!(get_userdata_ref::<Rc<RefCell<T>>>(ref_thread, index));
|
||||
let ud = try_self_arg!(ud.try_borrow(), Error::UserDataBorrowError);
|
||||
let ud = std::mem::transmute::<&T, &T>(&ud);
|
||||
method(lua, ud, args?).await?.push_into_stack_multi(lua)
|
||||
}
|
||||
Some(id) if id == TypeId::of::<Arc<T>>() => {
|
||||
let ud = try_self_arg!(get_userdata_ref::<Arc<T>>(ref_thread, index));
|
||||
let ud = std::mem::transmute::<&T, &T>(&ud);
|
||||
method(lua, ud, args?).await?.push_into_stack_multi(lua)
|
||||
}
|
||||
Some(id) if id == TypeId::of::<Arc<Mutex<T>>>() => {
|
||||
let ud =
|
||||
try_self_arg!(get_userdata_ref::<Arc<Mutex<T>>>(ref_thread, index));
|
||||
let ud = try_self_arg!(ud.try_lock(), Error::UserDataBorrowError);
|
||||
let ud = std::mem::transmute::<&T, &T>(&ud);
|
||||
method(lua, ud, args?).await?.push_into_stack_multi(lua)
|
||||
}
|
||||
#[cfg(feature = "parking_lot")]
|
||||
Some(id) if id == TypeId::of::<Arc<parking_lot::Mutex<T>>>() => {
|
||||
let ud = get_userdata_ref::<Arc<parking_lot::Mutex<T>>>(ref_thread, index);
|
||||
let ud = try_self_arg!(ud);
|
||||
let ud = try_self_arg!(ud.try_lock().ok_or(Error::UserDataBorrowError));
|
||||
let ud = std::mem::transmute::<&T, &T>(&ud);
|
||||
method(lua, ud, args?).await?.push_into_stack_multi(lua)
|
||||
}
|
||||
Some(id) if id == TypeId::of::<Arc<RwLock<T>>>() => {
|
||||
let ud =
|
||||
try_self_arg!(get_userdata_ref::<Arc<RwLock<T>>>(ref_thread, index));
|
||||
let ud = try_self_arg!(ud.try_read(), Error::UserDataBorrowError);
|
||||
let ud = std::mem::transmute::<&T, &T>(&ud);
|
||||
method(lua, ud, args?).await?.push_into_stack_multi(lua)
|
||||
}
|
||||
#[cfg(feature = "parking_lot")]
|
||||
Some(id) if id == TypeId::of::<Arc<parking_lot::RwLock<T>>>() => {
|
||||
let ud = get_userdata_ref::<Arc<parking_lot::RwLock<T>>>(ref_thread, index);
|
||||
let ud = try_self_arg!(ud);
|
||||
let ud = try_self_arg!(ud.try_read().ok_or(Error::UserDataBorrowError));
|
||||
let ud = std::mem::transmute::<&T, &T>(&ud);
|
||||
method(lua, ud, args?).await?.push_into_stack_multi(lua)
|
||||
}
|
||||
_ => Err(Error::bad_self_argument(&name, Error::UserDataTypeMismatch)),
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
fn box_async_method_mut<'s, M, A, MR, R>(name: &str, method: M) -> AsyncCallback<'lua, 'static>
|
||||
where
|
||||
'lua: 's,
|
||||
T: 'static,
|
||||
M: Fn(&'lua Lua, &'s mut T, A) -> MR + MaybeSend + 'static,
|
||||
A: FromLuaMulti<'lua>,
|
||||
MR: Future<Output = Result<R>> + 's,
|
||||
R: IntoLuaMulti<'lua>,
|
||||
{
|
||||
let name = Arc::new(get_function_name::<T>(name));
|
||||
let method = Arc::new(method);
|
||||
|
||||
Box::new(move |lua, mut args| unsafe {
|
||||
let name = name.clone();
|
||||
let method = method.clone();
|
||||
macro_rules! try_self_arg {
|
||||
($res:expr) => {
|
||||
$res.map_err(|err| Error::bad_self_argument(&name, err))?
|
||||
};
|
||||
($res:expr, $err:expr) => {
|
||||
$res.map_err(|_| Error::bad_self_argument(&name, $err))?
|
||||
};
|
||||
}
|
||||
|
||||
Box::pin(async move {
|
||||
let this = args.pop_front().ok_or_else(|| {
|
||||
Error::from_lua_conversion("missing argument", "userdata", None)
|
||||
});
|
||||
let this = try_self_arg!(AnyUserData::from_lua(try_self_arg!(this), lua));
|
||||
let args = A::from_lua_args(args, 2, Some(&name), lua);
|
||||
|
||||
let (ref_thread, index) = (lua.ref_thread(), this.0.index);
|
||||
match try_self_arg!(this.type_id()) {
|
||||
Some(id) if id == TypeId::of::<T>() => {
|
||||
let mut ud = try_self_arg!(get_userdata_mut::<T>(ref_thread, index));
|
||||
let ud = std::mem::transmute::<&mut T, &mut T>(&mut ud);
|
||||
method(lua, ud, args?).await?.push_into_stack_multi(lua)
|
||||
}
|
||||
#[cfg(not(feature = "send"))]
|
||||
Some(id) if id == TypeId::of::<Rc<RefCell<T>>>() => {
|
||||
Err(Error::UserDataBorrowMutError)
|
||||
}
|
||||
#[cfg(not(feature = "send"))]
|
||||
Some(id) if id == TypeId::of::<Rc<RefCell<T>>>() => {
|
||||
let ud =
|
||||
try_self_arg!(get_userdata_mut::<Rc<RefCell<T>>>(ref_thread, index));
|
||||
let mut ud =
|
||||
try_self_arg!(ud.try_borrow_mut(), Error::UserDataBorrowMutError);
|
||||
let ud = std::mem::transmute::<&mut T, &mut T>(&mut ud);
|
||||
method(lua, ud, args?).await?.push_into_stack_multi(lua)
|
||||
}
|
||||
#[cfg(not(feature = "send"))]
|
||||
Some(id) if id == TypeId::of::<Arc<T>>() => Err(Error::UserDataBorrowMutError),
|
||||
Some(id) if id == TypeId::of::<Arc<Mutex<T>>>() => {
|
||||
let ud =
|
||||
try_self_arg!(get_userdata_mut::<Arc<Mutex<T>>>(ref_thread, index));
|
||||
let mut ud = try_self_arg!(ud.try_lock(), Error::UserDataBorrowMutError);
|
||||
let ud = std::mem::transmute::<&mut T, &mut T>(&mut ud);
|
||||
method(lua, ud, args?).await?.push_into_stack_multi(lua)
|
||||
}
|
||||
#[cfg(feature = "parking_lot")]
|
||||
Some(id) if id == TypeId::of::<Arc<parking_lot::Mutex<T>>>() => {
|
||||
let ud = get_userdata_mut::<Arc<parking_lot::Mutex<T>>>(ref_thread, index);
|
||||
let ud = try_self_arg!(ud);
|
||||
let mut ud =
|
||||
try_self_arg!(ud.try_lock().ok_or(Error::UserDataBorrowMutError));
|
||||
let ud = std::mem::transmute::<&mut T, &mut T>(&mut ud);
|
||||
method(lua, ud, args?).await?.push_into_stack_multi(lua)
|
||||
}
|
||||
Some(id) if id == TypeId::of::<Arc<RwLock<T>>>() => {
|
||||
let ud =
|
||||
try_self_arg!(get_userdata_mut::<Arc<RwLock<T>>>(ref_thread, index));
|
||||
let mut ud = try_self_arg!(ud.try_write(), Error::UserDataBorrowMutError);
|
||||
let ud = std::mem::transmute::<&mut T, &mut T>(&mut ud);
|
||||
method(lua, ud, args?).await?.push_into_stack_multi(lua)
|
||||
}
|
||||
#[cfg(feature = "parking_lot")]
|
||||
Some(id) if id == TypeId::of::<Arc<parking_lot::RwLock<T>>>() => {
|
||||
let ud = get_userdata_mut::<Arc<parking_lot::RwLock<T>>>(ref_thread, index);
|
||||
let ud = try_self_arg!(ud);
|
||||
let mut ud =
|
||||
try_self_arg!(ud.try_write().ok_or(Error::UserDataBorrowMutError));
|
||||
let ud = std::mem::transmute::<&mut T, &mut T>(&mut ud);
|
||||
method(lua, ud, args?).await?.push_into_stack_multi(lua)
|
||||
}
|
||||
_ => Err(Error::bad_self_argument(&name, Error::UserDataTypeMismatch)),
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
fn box_function<F, A, R>(name: &str, function: F) -> Callback<'lua, 'static>
|
||||
where
|
||||
F: Fn(&'lua Lua, A) -> Result<R> + MaybeSend + 'static,
|
||||
A: FromLuaMulti<'lua>,
|
||||
R: IntoLuaMulti<'lua>,
|
||||
{
|
||||
let name = get_function_name::<T>(name);
|
||||
Box::new(move |lua, nargs| unsafe {
|
||||
let args = A::from_stack_args(nargs, 1, Some(&name), lua)?;
|
||||
function(lua, args)?.push_into_stack_multi(lua)
|
||||
})
|
||||
}
|
||||
|
||||
fn box_function_mut<F, A, R>(name: &str, function: F) -> Callback<'lua, 'static>
|
||||
where
|
||||
F: FnMut(&'lua Lua, A) -> Result<R> + MaybeSend + 'static,
|
||||
A: FromLuaMulti<'lua>,
|
||||
R: IntoLuaMulti<'lua>,
|
||||
{
|
||||
let name = get_function_name::<T>(name);
|
||||
let function = RefCell::new(function);
|
||||
Box::new(move |lua, nargs| unsafe {
|
||||
let function = &mut *function
|
||||
.try_borrow_mut()
|
||||
.map_err(|_| Error::RecursiveMutCallback)?;
|
||||
let args = A::from_stack_args(nargs, 1, Some(&name), lua)?;
|
||||
function(lua, args)?.push_into_stack_multi(lua)
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
fn box_async_function<F, A, FR, R>(name: &str, function: F) -> AsyncCallback<'lua, 'static>
|
||||
where
|
||||
F: Fn(&'lua Lua, A) -> FR + MaybeSend + 'static,
|
||||
A: FromLuaMulti<'lua>,
|
||||
FR: Future<Output = Result<R>> + 'lua,
|
||||
R: IntoLuaMulti<'lua>,
|
||||
{
|
||||
let name = get_function_name::<T>(name);
|
||||
Box::new(move |lua, args| unsafe {
|
||||
let args = match A::from_lua_args(args, 1, Some(&name), lua) {
|
||||
Ok(args) => args,
|
||||
Err(e) => return Box::pin(future::err(e)),
|
||||
};
|
||||
let fut = function(lua, args);
|
||||
Box::pin(async move { fut.await?.push_into_stack_multi(lua) })
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn check_meta_field<V>(lua: &'lua Lua, name: &str, value: V) -> Result<Value<'lua>>
|
||||
where
|
||||
V: IntoLua<'lua>,
|
||||
{
|
||||
let value = value.into_lua(lua)?;
|
||||
if name == MetaMethod::Index || name == MetaMethod::NewIndex {
|
||||
match value {
|
||||
Value::Nil | Value::Table(_) | Value::Function(_) => {}
|
||||
_ => {
|
||||
return Err(Error::MetaMethodTypeError {
|
||||
method: name.to_string(),
|
||||
type_name: value.type_name(),
|
||||
message: Some("expected nil, table or function".to_string()),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
value.into_lua(lua)
|
||||
}
|
||||
}
|
||||
|
||||
// Returns function name for the type `T`, without the module path
|
||||
fn get_function_name<T>(name: &str) -> StdString {
|
||||
format!("{}.{name}", short_type_name::<T>())
|
||||
}
|
||||
|
||||
impl<'lua, T: 'static> UserDataFields<'lua, T> for UserDataRegistry<'lua, T> {
|
||||
fn add_field<V>(&mut self, name: impl AsRef<str>, value: V)
|
||||
where
|
||||
V: IntoLua<'lua> + Clone + 'static,
|
||||
{
|
||||
let name = name.as_ref().to_string();
|
||||
self.fields.push((
|
||||
name,
|
||||
Box::new(move |lua, _| unsafe { value.clone().push_into_stack_multi(lua) }),
|
||||
));
|
||||
}
|
||||
|
||||
fn add_field_method_get<M, R>(&mut self, name: impl AsRef<str>, method: M)
|
||||
where
|
||||
M: Fn(&'lua Lua, &T) -> Result<R> + MaybeSend + 'static,
|
||||
R: IntoLua<'lua>,
|
||||
{
|
||||
let name = name.as_ref();
|
||||
let method = Self::box_method(name, move |lua, data, ()| method(lua, data));
|
||||
self.field_getters.push((name.into(), method));
|
||||
}
|
||||
|
||||
fn add_field_method_set<M, A>(&mut self, name: impl AsRef<str>, method: M)
|
||||
where
|
||||
M: FnMut(&'lua Lua, &mut T, A) -> Result<()> + MaybeSend + 'static,
|
||||
A: FromLua<'lua>,
|
||||
{
|
||||
let name = name.as_ref();
|
||||
let method = Self::box_method_mut(name, method);
|
||||
self.field_setters.push((name.into(), method));
|
||||
}
|
||||
|
||||
fn add_field_function_get<F, R>(&mut self, name: impl AsRef<str>, function: F)
|
||||
where
|
||||
F: Fn(&'lua Lua, AnyUserData<'lua>) -> Result<R> + MaybeSend + 'static,
|
||||
R: IntoLua<'lua>,
|
||||
{
|
||||
let name = name.as_ref();
|
||||
let func = Self::box_function(name, function);
|
||||
self.field_getters.push((name.into(), func));
|
||||
}
|
||||
|
||||
fn add_field_function_set<F, A>(&mut self, name: impl AsRef<str>, mut function: F)
|
||||
where
|
||||
F: FnMut(&'lua Lua, AnyUserData<'lua>, A) -> Result<()> + MaybeSend + 'static,
|
||||
A: FromLua<'lua>,
|
||||
{
|
||||
let name = name.as_ref();
|
||||
let func = Self::box_function_mut(name, move |lua, (data, val)| function(lua, data, val));
|
||||
self.field_setters.push((name.into(), func));
|
||||
}
|
||||
|
||||
fn add_meta_field<V>(&mut self, name: impl AsRef<str>, value: V)
|
||||
where
|
||||
V: IntoLua<'lua> + Clone + 'static,
|
||||
{
|
||||
let name = name.as_ref().to_string();
|
||||
let name2 = name.clone();
|
||||
self.meta_fields.push((
|
||||
name,
|
||||
Box::new(move |lua, _| unsafe {
|
||||
Self::check_meta_field(lua, &name2, value.clone())?.push_into_stack_multi(lua)
|
||||
}),
|
||||
));
|
||||
}
|
||||
|
||||
fn add_meta_field_with<F, R>(&mut self, name: impl AsRef<str>, f: F)
|
||||
where
|
||||
F: Fn(&'lua Lua) -> Result<R> + MaybeSend + 'static,
|
||||
R: IntoLua<'lua>,
|
||||
{
|
||||
let name = name.as_ref().to_string();
|
||||
let name2 = name.clone();
|
||||
self.meta_fields.push((
|
||||
name,
|
||||
Box::new(move |lua, _| unsafe {
|
||||
Self::check_meta_field(lua, &name2, f(lua)?)?.push_into_stack_multi(lua)
|
||||
}),
|
||||
));
|
||||
}
|
||||
|
||||
// Below are internal methods
|
||||
|
||||
fn append_fields_from<S>(&mut self, other: UserDataRegistry<'lua, S>) {
|
||||
self.fields.extend(other.fields);
|
||||
self.field_getters.extend(other.field_getters);
|
||||
self.field_setters.extend(other.field_setters);
|
||||
self.meta_fields.extend(other.meta_fields);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua, T: 'static> UserDataMethods<'lua, T> for UserDataRegistry<'lua, T> {
|
||||
fn add_method<M, A, R>(&mut self, name: impl AsRef<str>, method: M)
|
||||
where
|
||||
M: Fn(&'lua Lua, &T, A) -> Result<R> + MaybeSend + 'static,
|
||||
A: FromLuaMulti<'lua>,
|
||||
R: IntoLuaMulti<'lua>,
|
||||
{
|
||||
let name = name.as_ref();
|
||||
self.methods
|
||||
.push((name.into(), Self::box_method(name, method)));
|
||||
}
|
||||
|
||||
fn add_method_mut<M, A, R>(&mut self, name: impl AsRef<str>, method: M)
|
||||
where
|
||||
M: FnMut(&'lua Lua, &mut T, A) -> Result<R> + MaybeSend + 'static,
|
||||
A: FromLuaMulti<'lua>,
|
||||
R: IntoLuaMulti<'lua>,
|
||||
{
|
||||
let name = name.as_ref();
|
||||
self.methods
|
||||
.push((name.into(), Self::box_method_mut(name, method)));
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
fn add_async_method<'s, M, A, MR, R>(&mut self, name: impl AsRef<str>, method: M)
|
||||
where
|
||||
'lua: 's,
|
||||
T: 'static,
|
||||
M: Fn(&'lua Lua, &'s T, A) -> MR + MaybeSend + 'static,
|
||||
A: FromLuaMulti<'lua>,
|
||||
MR: Future<Output = Result<R>> + 's,
|
||||
R: IntoLuaMulti<'lua>,
|
||||
{
|
||||
let name = name.as_ref();
|
||||
self.async_methods
|
||||
.push((name.into(), Self::box_async_method(name, method)));
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
fn add_async_method_mut<'s, M, A, MR, R>(&mut self, name: impl AsRef<str>, method: M)
|
||||
where
|
||||
'lua: 's,
|
||||
T: 'static,
|
||||
M: Fn(&'lua Lua, &'s mut T, A) -> MR + MaybeSend + 'static,
|
||||
A: FromLuaMulti<'lua>,
|
||||
MR: Future<Output = Result<R>> + 's,
|
||||
R: IntoLuaMulti<'lua>,
|
||||
{
|
||||
let name = name.as_ref();
|
||||
self.async_methods
|
||||
.push((name.into(), Self::box_async_method_mut(name, method)));
|
||||
}
|
||||
|
||||
fn add_function<F, A, R>(&mut self, name: impl AsRef<str>, function: F)
|
||||
where
|
||||
F: Fn(&'lua Lua, A) -> Result<R> + MaybeSend + 'static,
|
||||
A: FromLuaMulti<'lua>,
|
||||
R: IntoLuaMulti<'lua>,
|
||||
{
|
||||
let name = name.as_ref();
|
||||
self.methods
|
||||
.push((name.into(), Self::box_function(name, function)));
|
||||
}
|
||||
|
||||
fn add_function_mut<F, A, R>(&mut self, name: impl AsRef<str>, function: F)
|
||||
where
|
||||
F: FnMut(&'lua Lua, A) -> Result<R> + MaybeSend + 'static,
|
||||
A: FromLuaMulti<'lua>,
|
||||
R: IntoLuaMulti<'lua>,
|
||||
{
|
||||
let name = name.as_ref();
|
||||
self.methods
|
||||
.push((name.into(), Self::box_function_mut(name, function)));
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
fn add_async_function<F, A, FR, R>(&mut self, name: impl AsRef<str>, function: F)
|
||||
where
|
||||
F: Fn(&'lua Lua, A) -> FR + MaybeSend + 'static,
|
||||
A: FromLuaMulti<'lua>,
|
||||
FR: Future<Output = Result<R>> + 'lua,
|
||||
R: IntoLuaMulti<'lua>,
|
||||
{
|
||||
let name = name.as_ref();
|
||||
self.async_methods
|
||||
.push((name.into(), Self::box_async_function(name, function)));
|
||||
}
|
||||
|
||||
fn add_meta_method<M, A, R>(&mut self, name: impl AsRef<str>, method: M)
|
||||
where
|
||||
M: Fn(&'lua Lua, &T, A) -> Result<R> + MaybeSend + 'static,
|
||||
A: FromLuaMulti<'lua>,
|
||||
R: IntoLuaMulti<'lua>,
|
||||
{
|
||||
let name = name.as_ref();
|
||||
self.meta_methods
|
||||
.push((name.into(), Self::box_method(name, method)));
|
||||
}
|
||||
|
||||
fn add_meta_method_mut<M, A, R>(&mut self, name: impl AsRef<str>, method: M)
|
||||
where
|
||||
M: FnMut(&'lua Lua, &mut T, A) -> Result<R> + MaybeSend + 'static,
|
||||
A: FromLuaMulti<'lua>,
|
||||
R: IntoLuaMulti<'lua>,
|
||||
{
|
||||
let name = name.as_ref();
|
||||
self.meta_methods
|
||||
.push((name.into(), Self::box_method_mut(name, method)));
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "async", not(any(feature = "lua51", feature = "luau"))))]
|
||||
fn add_async_meta_method<'s, M, A, MR, R>(&mut self, name: impl AsRef<str>, method: M)
|
||||
where
|
||||
'lua: 's,
|
||||
T: 'static,
|
||||
M: Fn(&'lua Lua, &'s T, A) -> MR + MaybeSend + 'static,
|
||||
A: FromLuaMulti<'lua>,
|
||||
MR: Future<Output = Result<R>> + 's,
|
||||
R: IntoLuaMulti<'lua>,
|
||||
{
|
||||
let name = name.as_ref();
|
||||
self.async_meta_methods
|
||||
.push((name.into(), Self::box_async_method(name, method)));
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "async", not(any(feature = "lua51", feature = "luau"))))]
|
||||
fn add_async_meta_method_mut<'s, M, A, MR, R>(&mut self, name: impl AsRef<str>, method: M)
|
||||
where
|
||||
'lua: 's,
|
||||
T: 'static,
|
||||
M: Fn(&'lua Lua, &'s mut T, A) -> MR + MaybeSend + 'static,
|
||||
A: FromLuaMulti<'lua>,
|
||||
MR: Future<Output = Result<R>> + 's,
|
||||
R: IntoLuaMulti<'lua>,
|
||||
{
|
||||
let name = name.as_ref();
|
||||
self.async_meta_methods
|
||||
.push((name.into(), Self::box_async_method_mut(name, method)));
|
||||
}
|
||||
|
||||
fn add_meta_function<F, A, R>(&mut self, name: impl AsRef<str>, function: F)
|
||||
where
|
||||
F: Fn(&'lua Lua, A) -> Result<R> + MaybeSend + 'static,
|
||||
A: FromLuaMulti<'lua>,
|
||||
R: IntoLuaMulti<'lua>,
|
||||
{
|
||||
let name = name.as_ref();
|
||||
self.meta_methods
|
||||
.push((name.into(), Self::box_function(name, function)));
|
||||
}
|
||||
|
||||
fn add_meta_function_mut<F, A, R>(&mut self, name: impl AsRef<str>, function: F)
|
||||
where
|
||||
F: FnMut(&'lua Lua, A) -> Result<R> + MaybeSend + 'static,
|
||||
A: FromLuaMulti<'lua>,
|
||||
R: IntoLuaMulti<'lua>,
|
||||
{
|
||||
let name = name.as_ref();
|
||||
self.meta_methods
|
||||
.push((name.into(), Self::box_function_mut(name, function)));
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "async", not(any(feature = "lua51", feature = "luau"))))]
|
||||
fn add_async_meta_function<F, A, FR, R>(&mut self, name: impl AsRef<str>, function: F)
|
||||
where
|
||||
F: Fn(&'lua Lua, A) -> FR + MaybeSend + 'static,
|
||||
A: FromLuaMulti<'lua>,
|
||||
FR: Future<Output = Result<R>> + 'lua,
|
||||
R: IntoLuaMulti<'lua>,
|
||||
{
|
||||
let name = name.as_ref();
|
||||
self.async_meta_methods
|
||||
.push((name.into(), Self::box_async_function(name, function)));
|
||||
}
|
||||
|
||||
// Below are internal methods used in generated code
|
||||
|
||||
fn append_methods_from<S>(&mut self, other: UserDataRegistry<'lua, S>) {
|
||||
self.methods.extend(other.methods);
|
||||
#[cfg(feature = "async")]
|
||||
self.async_methods.extend(other.async_methods);
|
||||
self.meta_methods.extend(other.meta_methods);
|
||||
#[cfg(feature = "async")]
|
||||
self.async_meta_methods.extend(other.async_meta_methods);
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn get_userdata_ref<'a, T>(state: *mut ffi::lua_State, index: c_int) -> Result<Ref<'a, T>> {
|
||||
(*get_userdata::<UserDataCell<T>>(state, index)).try_borrow()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn get_userdata_mut<'a, T>(
|
||||
state: *mut ffi::lua_State,
|
||||
index: c_int,
|
||||
) -> Result<RefMut<'a, T>> {
|
||||
(*get_userdata::<UserDataCell<T>>(state, index)).try_borrow_mut()
|
||||
}
|
||||
|
||||
macro_rules! lua_userdata_impl {
|
||||
($type:ty) => {
|
||||
impl<T: UserData + 'static> UserData for $type {
|
||||
fn add_fields<'lua, F: UserDataFields<'lua, Self>>(fields: &mut F) {
|
||||
let mut orig_fields = UserDataRegistry::new();
|
||||
T::add_fields(&mut orig_fields);
|
||||
fields.append_fields_from(orig_fields);
|
||||
}
|
||||
|
||||
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
|
||||
let mut orig_methods = UserDataRegistry::new();
|
||||
T::add_methods(&mut orig_methods);
|
||||
methods.append_methods_from(orig_methods);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "send"))]
|
||||
lua_userdata_impl!(Rc<T>);
|
||||
#[cfg(not(feature = "send"))]
|
||||
lua_userdata_impl!(Rc<RefCell<T>>);
|
||||
|
||||
lua_userdata_impl!(Arc<T>);
|
||||
lua_userdata_impl!(Arc<Mutex<T>>);
|
||||
lua_userdata_impl!(Arc<RwLock<T>>);
|
||||
#[cfg(feature = "parking_lot")]
|
||||
lua_userdata_impl!(Arc<parking_lot::Mutex<T>>);
|
||||
#[cfg(feature = "parking_lot")]
|
||||
lua_userdata_impl!(Arc<parking_lot::RwLock<T>>);
|
||||
|
||||
// A special proxy object for UserData
|
||||
pub(crate) struct UserDataProxy<T>(pub(crate) PhantomData<T>);
|
||||
|
||||
lua_userdata_impl!(UserDataProxy<T>);
|
||||
@@ -0,0 +1,432 @@
|
||||
use std::any::Any;
|
||||
use std::fmt::Write as _;
|
||||
use std::mem::MaybeUninit;
|
||||
use std::os::raw::{c_int, c_void};
|
||||
use std::panic::{catch_unwind, resume_unwind, AssertUnwindSafe};
|
||||
use std::ptr;
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
use crate::memory::MemoryState;
|
||||
use crate::util::{
|
||||
check_stack, get_internal_metatable, get_internal_userdata, init_internal_metatable,
|
||||
push_internal_userdata, push_string, push_table, rawset_field, to_string, TypeKey,
|
||||
DESTRUCTED_USERDATA_METATABLE,
|
||||
};
|
||||
|
||||
static WRAPPED_FAILURE_TYPE_KEY: u8 = 0;
|
||||
|
||||
pub(crate) enum WrappedFailure {
|
||||
None,
|
||||
Error(Error),
|
||||
Panic(Option<Box<dyn Any + Send + 'static>>),
|
||||
}
|
||||
|
||||
impl TypeKey for WrappedFailure {
|
||||
#[inline(always)]
|
||||
fn type_key() -> *const c_void {
|
||||
&WRAPPED_FAILURE_TYPE_KEY as *const u8 as *const c_void
|
||||
}
|
||||
}
|
||||
|
||||
impl WrappedFailure {
|
||||
pub(crate) unsafe fn new_userdata(state: *mut ffi::lua_State) -> *mut Self {
|
||||
#[cfg(feature = "luau")]
|
||||
let ud = ffi::lua_newuserdata_t::<Self>(state);
|
||||
#[cfg(not(feature = "luau"))]
|
||||
let ud = ffi::lua_newuserdata(state, std::mem::size_of::<Self>()) as *mut Self;
|
||||
ptr::write(ud, WrappedFailure::None);
|
||||
ud
|
||||
}
|
||||
}
|
||||
|
||||
// In the context of a lua callback, this will call the given function and if the given function
|
||||
// returns an error, *or if the given function panics*, this will result in a call to `lua_error` (a
|
||||
// longjmp). The error or panic is wrapped in such a way that when calling `pop_error` back on
|
||||
// the Rust side, it will resume the panic.
|
||||
//
|
||||
// This function assumes the structure of the stack at the beginning of a callback, that the only
|
||||
// elements on the stack are the arguments to the callback.
|
||||
//
|
||||
// This function uses some of the bottom of the stack for error handling, the given callback will be
|
||||
// given the number of arguments available as an argument, and should return the number of returns
|
||||
// as normal, but cannot assume that the arguments available start at 0.
|
||||
unsafe fn callback_error<F, R>(state: *mut ffi::lua_State, f: F) -> R
|
||||
where
|
||||
F: FnOnce(c_int) -> Result<R>,
|
||||
{
|
||||
let nargs = ffi::lua_gettop(state);
|
||||
|
||||
// We need 2 extra stack spaces to store preallocated memory and error/panic metatable
|
||||
let extra_stack = if nargs < 2 { 2 - nargs } else { 1 };
|
||||
ffi::luaL_checkstack(
|
||||
state,
|
||||
extra_stack,
|
||||
cstr!("not enough stack space for callback error handling"),
|
||||
);
|
||||
|
||||
// We cannot shadow Rust errors with Lua ones, we pre-allocate enough memory
|
||||
// to store a wrapped error or panic *before* we proceed.
|
||||
let ud = WrappedFailure::new_userdata(state);
|
||||
ffi::lua_rotate(state, 1, 1);
|
||||
|
||||
match catch_unwind(AssertUnwindSafe(|| f(nargs))) {
|
||||
Ok(Ok(r)) => {
|
||||
ffi::lua_remove(state, 1);
|
||||
r
|
||||
}
|
||||
Ok(Err(err)) => {
|
||||
ffi::lua_settop(state, 1);
|
||||
|
||||
// Build `CallbackError` with traceback
|
||||
let traceback = if ffi::lua_checkstack(state, ffi::LUA_TRACEBACK_STACK) != 0 {
|
||||
ffi::luaL_traceback(state, state, ptr::null(), 0);
|
||||
let traceback = to_string(state, -1);
|
||||
ffi::lua_pop(state, 1);
|
||||
traceback
|
||||
} else {
|
||||
"<not enough stack space for traceback>".to_string()
|
||||
};
|
||||
let cause = Arc::new(err);
|
||||
let wrapped_error = WrappedFailure::Error(Error::CallbackError { traceback, cause });
|
||||
ptr::write(ud, wrapped_error);
|
||||
get_internal_metatable::<WrappedFailure>(state);
|
||||
ffi::lua_setmetatable(state, -2);
|
||||
|
||||
ffi::lua_error(state)
|
||||
}
|
||||
Err(p) => {
|
||||
ffi::lua_settop(state, 1);
|
||||
ptr::write(ud, WrappedFailure::Panic(Some(p)));
|
||||
get_internal_metatable::<WrappedFailure>(state);
|
||||
ffi::lua_setmetatable(state, -2);
|
||||
ffi::lua_error(state)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Pops an error off of the stack and returns it. The specific behavior depends on the type of the
|
||||
// error at the top of the stack:
|
||||
// 1) If the error is actually a panic, this will continue the panic.
|
||||
// 2) If the error on the top of the stack is actually an error, just returns it.
|
||||
// 3) Otherwise, interprets the error as the appropriate lua error.
|
||||
// Uses 2 stack spaces, does not call checkstack.
|
||||
pub(crate) unsafe fn pop_error(state: *mut ffi::lua_State, err_code: c_int) -> Error {
|
||||
mlua_debug_assert!(
|
||||
err_code != ffi::LUA_OK && err_code != ffi::LUA_YIELD,
|
||||
"pop_error called with non-error return code"
|
||||
);
|
||||
|
||||
match get_internal_userdata::<WrappedFailure>(state, -1, ptr::null()).as_mut() {
|
||||
Some(WrappedFailure::Error(err)) => {
|
||||
ffi::lua_pop(state, 1);
|
||||
err.clone()
|
||||
}
|
||||
Some(WrappedFailure::Panic(panic)) => {
|
||||
if let Some(p) = panic.take() {
|
||||
resume_unwind(p);
|
||||
} else {
|
||||
Error::PreviouslyResumedPanic
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
let err_string = to_string(state, -1);
|
||||
ffi::lua_pop(state, 1);
|
||||
|
||||
match err_code {
|
||||
ffi::LUA_ERRRUN => Error::RuntimeError(err_string),
|
||||
ffi::LUA_ERRSYNTAX => {
|
||||
Error::SyntaxError {
|
||||
// This seems terrible, but as far as I can tell, this is exactly what the
|
||||
// stock Lua REPL does.
|
||||
incomplete_input: err_string.ends_with("<eof>") || err_string.ends_with("'<eof>'"),
|
||||
message: err_string,
|
||||
}
|
||||
}
|
||||
ffi::LUA_ERRERR => {
|
||||
// This error is raised when the error handler raises an error too many times
|
||||
// recursively, and continuing to trigger the error handler would cause a stack
|
||||
// overflow. It is not very useful to differentiate between this and "ordinary"
|
||||
// runtime errors, so we handle them the same way.
|
||||
Error::RuntimeError(err_string)
|
||||
}
|
||||
ffi::LUA_ERRMEM => Error::MemoryError(err_string),
|
||||
#[cfg(any(feature = "lua53", feature = "lua52"))]
|
||||
ffi::LUA_ERRGCMM => Error::GarbageCollectorError(err_string),
|
||||
_ => mlua_panic!("unrecognized lua error code"),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Call a function that calls into the Lua API and may trigger a Lua error (longjmp) in a safe way.
|
||||
// Wraps the inner function in a call to `lua_pcall`, so the inner function only has access to a
|
||||
// limited lua stack. `nargs` is the same as the the parameter to `lua_pcall`, and `nresults` is
|
||||
// always `LUA_MULTRET`. Provided function must *not* panic, and since it will generally be
|
||||
// longjmping, should not contain any values that implements Drop.
|
||||
// Internally uses 2 extra stack spaces, and does not call checkstack.
|
||||
pub(crate) unsafe fn protect_lua_call(
|
||||
state: *mut ffi::lua_State,
|
||||
nargs: c_int,
|
||||
f: unsafe extern "C-unwind" fn(*mut ffi::lua_State) -> c_int,
|
||||
) -> Result<()> {
|
||||
let stack_start = ffi::lua_gettop(state) - nargs;
|
||||
|
||||
MemoryState::relax_limit_with(state, || {
|
||||
ffi::lua_pushcfunction(state, error_traceback);
|
||||
ffi::lua_pushcfunction(state, f);
|
||||
});
|
||||
if nargs > 0 {
|
||||
ffi::lua_rotate(state, stack_start + 1, 2);
|
||||
}
|
||||
|
||||
let ret = ffi::lua_pcall(state, nargs, ffi::LUA_MULTRET, stack_start + 1);
|
||||
ffi::lua_remove(state, stack_start + 1);
|
||||
|
||||
if ret == ffi::LUA_OK {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(pop_error(state, ret))
|
||||
}
|
||||
}
|
||||
|
||||
// Call a function that calls into the Lua API and may trigger a Lua error (longjmp) in a safe way.
|
||||
// Wraps the inner function in a call to `lua_pcall`, so the inner function only has access to a
|
||||
// limited lua stack. `nargs` and `nresults` are similar to the parameters of `lua_pcall`, but the
|
||||
// given function return type is not the return value count, instead the inner function return
|
||||
// values are assumed to match the `nresults` param. Provided function must *not* panic, and since
|
||||
// it will generally be longjmping, should not contain any values that implements Drop.
|
||||
// Internally uses 3 extra stack spaces, and does not call checkstack.
|
||||
pub(crate) unsafe fn protect_lua_closure<F, R>(
|
||||
state: *mut ffi::lua_State,
|
||||
nargs: c_int,
|
||||
nresults: c_int,
|
||||
f: F,
|
||||
) -> Result<R>
|
||||
where
|
||||
F: Fn(*mut ffi::lua_State) -> R,
|
||||
R: Copy,
|
||||
{
|
||||
struct Params<F, R: Copy> {
|
||||
function: F,
|
||||
result: MaybeUninit<R>,
|
||||
nresults: c_int,
|
||||
}
|
||||
|
||||
unsafe extern "C-unwind" fn do_call<F, R>(state: *mut ffi::lua_State) -> c_int
|
||||
where
|
||||
F: Fn(*mut ffi::lua_State) -> R,
|
||||
R: Copy,
|
||||
{
|
||||
let params = ffi::lua_touserdata(state, -1) as *mut Params<F, R>;
|
||||
ffi::lua_pop(state, 1);
|
||||
|
||||
(*params).result.write(((*params).function)(state));
|
||||
|
||||
if (*params).nresults == ffi::LUA_MULTRET {
|
||||
ffi::lua_gettop(state)
|
||||
} else {
|
||||
(*params).nresults
|
||||
}
|
||||
}
|
||||
|
||||
let stack_start = ffi::lua_gettop(state) - nargs;
|
||||
|
||||
MemoryState::relax_limit_with(state, || {
|
||||
ffi::lua_pushcfunction(state, error_traceback);
|
||||
ffi::lua_pushcfunction(state, do_call::<F, R>);
|
||||
});
|
||||
if nargs > 0 {
|
||||
ffi::lua_rotate(state, stack_start + 1, 2);
|
||||
}
|
||||
|
||||
let mut params = Params {
|
||||
function: f,
|
||||
result: MaybeUninit::uninit(),
|
||||
nresults,
|
||||
};
|
||||
|
||||
ffi::lua_pushlightuserdata(state, &mut params as *mut Params<F, R> as *mut c_void);
|
||||
let ret = ffi::lua_pcall(state, nargs + 1, nresults, stack_start + 1);
|
||||
ffi::lua_remove(state, stack_start + 1);
|
||||
|
||||
if ret == ffi::LUA_OK {
|
||||
// `LUA_OK` is only returned when the `do_call` function has completed successfully, so
|
||||
// `params.result` is definitely initialized.
|
||||
Ok(params.result.assume_init())
|
||||
} else {
|
||||
Err(pop_error(state, ret))
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) unsafe extern "C-unwind" fn error_traceback(state: *mut ffi::lua_State) -> c_int {
|
||||
// Luau calls error handler for memory allocation errors, skip it
|
||||
// See https://github.com/Roblox/luau/issues/880
|
||||
#[cfg(feature = "luau")]
|
||||
if MemoryState::limit_reached(state) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ffi::lua_checkstack(state, 2) == 0 {
|
||||
// If we don't have enough stack space to even check the error type, do
|
||||
// nothing so we don't risk shadowing a rust panic.
|
||||
return 1;
|
||||
}
|
||||
|
||||
if get_internal_userdata::<WrappedFailure>(state, -1, ptr::null()).is_null() {
|
||||
let s = ffi::luaL_tolstring(state, -1, ptr::null_mut());
|
||||
if ffi::lua_checkstack(state, ffi::LUA_TRACEBACK_STACK) != 0 {
|
||||
ffi::luaL_traceback(state, state, s, 0);
|
||||
ffi::lua_remove(state, -2);
|
||||
}
|
||||
}
|
||||
|
||||
1
|
||||
}
|
||||
|
||||
// A variant of `error_traceback` that can safely inspect another (yielded) thread stack
|
||||
pub(crate) unsafe fn error_traceback_thread(state: *mut ffi::lua_State, thread: *mut ffi::lua_State) {
|
||||
// Move error object to the main thread to safely call `__tostring` metamethod if present
|
||||
ffi::lua_xmove(thread, state, 1);
|
||||
|
||||
if get_internal_userdata::<WrappedFailure>(state, -1, ptr::null()).is_null() {
|
||||
let s = ffi::luaL_tolstring(state, -1, ptr::null_mut());
|
||||
if ffi::lua_checkstack(state, ffi::LUA_TRACEBACK_STACK) != 0 {
|
||||
ffi::luaL_traceback(state, thread, s, 0);
|
||||
ffi::lua_remove(state, -2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize the error, panic, and destructed userdata metatables.
|
||||
pub(crate) unsafe fn init_error_registry(state: *mut ffi::lua_State) -> Result<()> {
|
||||
check_stack(state, 7)?;
|
||||
|
||||
// Create error and panic metatables
|
||||
|
||||
static ERROR_PRINT_BUFFER_KEY: u8 = 0;
|
||||
|
||||
unsafe extern "C-unwind" fn error_tostring(state: *mut ffi::lua_State) -> c_int {
|
||||
callback_error(state, |_| {
|
||||
check_stack(state, 3)?;
|
||||
|
||||
let err_buf = match get_internal_userdata::<WrappedFailure>(state, -1, ptr::null()).as_ref() {
|
||||
Some(WrappedFailure::Error(error)) => {
|
||||
let err_buf_key = &ERROR_PRINT_BUFFER_KEY as *const u8 as *const c_void;
|
||||
ffi::lua_rawgetp(state, ffi::LUA_REGISTRYINDEX, err_buf_key);
|
||||
let err_buf = ffi::lua_touserdata(state, -1) as *mut String;
|
||||
ffi::lua_pop(state, 2);
|
||||
|
||||
(*err_buf).clear();
|
||||
// Depending on how the API is used and what error types scripts are given, it may
|
||||
// be possible to make this consume arbitrary amounts of memory (for example, some
|
||||
// kind of recursive error structure?)
|
||||
let _ = write!(&mut (*err_buf), "{error}");
|
||||
Ok(err_buf)
|
||||
}
|
||||
Some(WrappedFailure::Panic(Some(ref panic))) => {
|
||||
let err_buf_key = &ERROR_PRINT_BUFFER_KEY as *const u8 as *const c_void;
|
||||
ffi::lua_rawgetp(state, ffi::LUA_REGISTRYINDEX, err_buf_key);
|
||||
let err_buf = ffi::lua_touserdata(state, -1) as *mut String;
|
||||
(*err_buf).clear();
|
||||
ffi::lua_pop(state, 2);
|
||||
|
||||
if let Some(msg) = panic.downcast_ref::<&str>() {
|
||||
let _ = write!(&mut (*err_buf), "{msg}");
|
||||
} else if let Some(msg) = panic.downcast_ref::<String>() {
|
||||
let _ = write!(&mut (*err_buf), "{msg}");
|
||||
} else {
|
||||
let _ = write!(&mut (*err_buf), "<panic>");
|
||||
};
|
||||
Ok(err_buf)
|
||||
}
|
||||
Some(WrappedFailure::Panic(None)) => Err(Error::PreviouslyResumedPanic),
|
||||
_ => {
|
||||
// I'm not sure whether this is possible to trigger without bugs in mlua?
|
||||
Err(Error::UserDataTypeMismatch)
|
||||
}
|
||||
}?;
|
||||
|
||||
push_string(state, (*err_buf).as_bytes(), true)?;
|
||||
(*err_buf).clear();
|
||||
|
||||
Ok(1)
|
||||
})
|
||||
}
|
||||
|
||||
init_internal_metatable::<WrappedFailure>(
|
||||
state,
|
||||
Some(|state| {
|
||||
ffi::lua_pushcfunction(state, error_tostring);
|
||||
rawset_field(state, -2, "__tostring")
|
||||
}),
|
||||
)?;
|
||||
|
||||
// Create destructed userdata metatable
|
||||
|
||||
unsafe extern "C-unwind" fn destructed_error(state: *mut ffi::lua_State) -> c_int {
|
||||
callback_error(state, |_| Err(Error::CallbackDestructed))
|
||||
}
|
||||
|
||||
push_table(state, 0, 26, true)?;
|
||||
ffi::lua_pushcfunction(state, destructed_error);
|
||||
for &method in &[
|
||||
"__add",
|
||||
"__sub",
|
||||
"__mul",
|
||||
"__div",
|
||||
"__mod",
|
||||
"__pow",
|
||||
"__unm",
|
||||
#[cfg(any(feature = "lua54", feature = "lua53", feature = "luau"))]
|
||||
"__idiv",
|
||||
#[cfg(any(feature = "lua54", feature = "lua53"))]
|
||||
"__band",
|
||||
#[cfg(any(feature = "lua54", feature = "lua53"))]
|
||||
"__bor",
|
||||
#[cfg(any(feature = "lua54", feature = "lua53"))]
|
||||
"__bxor",
|
||||
#[cfg(any(feature = "lua54", feature = "lua53"))]
|
||||
"__bnot",
|
||||
#[cfg(any(feature = "lua54", feature = "lua53"))]
|
||||
"__shl",
|
||||
#[cfg(any(feature = "lua54", feature = "lua53"))]
|
||||
"__shr",
|
||||
"__concat",
|
||||
"__len",
|
||||
"__eq",
|
||||
"__lt",
|
||||
"__le",
|
||||
"__index",
|
||||
"__newindex",
|
||||
"__call",
|
||||
"__tostring",
|
||||
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52", feature = "luajit52"))]
|
||||
"__pairs",
|
||||
#[cfg(any(feature = "lua53", feature = "lua52", feature = "luajit52"))]
|
||||
"__ipairs",
|
||||
#[cfg(feature = "luau")]
|
||||
"__iter",
|
||||
#[cfg(feature = "lua54")]
|
||||
"__close",
|
||||
] {
|
||||
ffi::lua_pushvalue(state, -1);
|
||||
rawset_field(state, -3, method)?;
|
||||
}
|
||||
ffi::lua_pop(state, 1);
|
||||
|
||||
protect_lua!(state, 1, 0, fn(state) {
|
||||
let destructed_mt_key = &DESTRUCTED_USERDATA_METATABLE as *const u8 as *const c_void;
|
||||
ffi::lua_rawsetp(state, ffi::LUA_REGISTRYINDEX, destructed_mt_key);
|
||||
})?;
|
||||
|
||||
// Create error print buffer
|
||||
init_internal_metatable::<String>(state, None)?;
|
||||
push_internal_userdata(state, String::new(), true)?;
|
||||
protect_lua!(state, 1, 0, fn(state) {
|
||||
let err_buf_key = &ERROR_PRINT_BUFFER_KEY as *const u8 as *const c_void;
|
||||
ffi::lua_rawsetp(state, ffi::LUA_REGISTRYINDEX, err_buf_key);
|
||||
})?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
+41
-832
@@ -1,45 +1,43 @@
|
||||
use std::any::{Any, TypeId};
|
||||
use std::borrow::Cow;
|
||||
use std::ffi::CStr;
|
||||
use std::fmt::Write;
|
||||
use std::mem::MaybeUninit;
|
||||
use std::os::raw::{c_char, c_int, c_void};
|
||||
use std::panic::{catch_unwind, resume_unwind, AssertUnwindSafe};
|
||||
use std::sync::Arc;
|
||||
use std::os::raw::{c_char, c_int};
|
||||
use std::{ptr, slice, str};
|
||||
|
||||
use once_cell::sync::Lazy;
|
||||
use rustc_hash::FxHashMap;
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
use crate::memory::MemoryState;
|
||||
|
||||
pub(crate) use error::{
|
||||
error_traceback, error_traceback_thread, init_error_registry, pop_error, protect_lua_call,
|
||||
protect_lua_closure, WrappedFailure,
|
||||
};
|
||||
pub(crate) use short_names::short_type_name;
|
||||
pub(crate) use types::TypeKey;
|
||||
pub(crate) use userdata::{
|
||||
get_destructed_userdata_metatable, get_internal_metatable, get_internal_userdata, get_userdata,
|
||||
init_internal_metatable, init_userdata_metatable, push_internal_userdata, take_userdata,
|
||||
DESTRUCTED_USERDATA_METATABLE,
|
||||
};
|
||||
|
||||
static METATABLE_CACHE: Lazy<FxHashMap<TypeId, u8>> = Lazy::new(|| {
|
||||
let mut map = FxHashMap::with_capacity_and_hasher(32, Default::default());
|
||||
crate::lua::init_metatable_cache(&mut map);
|
||||
map.insert(TypeId::of::<WrappedFailure>(), 0);
|
||||
map.insert(TypeId::of::<String>(), 0);
|
||||
map
|
||||
});
|
||||
#[cfg(not(feature = "lua54"))]
|
||||
pub(crate) use userdata::push_userdata;
|
||||
#[cfg(feature = "lua54")]
|
||||
pub(crate) use userdata::push_userdata_uv;
|
||||
|
||||
#[cfg(not(feature = "luau"))]
|
||||
pub(crate) use userdata::userdata_destructor;
|
||||
|
||||
// Checks that Lua has enough free stack space for future stack operations. On failure, this will
|
||||
// panic with an internal error message.
|
||||
#[inline]
|
||||
pub unsafe fn assert_stack(state: *mut ffi::lua_State, amount: c_int) {
|
||||
pub(crate) unsafe fn assert_stack(state: *mut ffi::lua_State, amount: c_int) {
|
||||
// TODO: This should only be triggered when there is a logic error in `mlua`. In the future,
|
||||
// when there is a way to be confident about stack safety and test it, this could be enabled
|
||||
// only when `cfg!(debug_assertions)` is true.
|
||||
mlua_assert!(
|
||||
ffi::lua_checkstack(state, amount) != 0,
|
||||
"out of stack space"
|
||||
);
|
||||
mlua_assert!(ffi::lua_checkstack(state, amount) != 0, "out of stack space");
|
||||
}
|
||||
|
||||
// Checks that Lua has enough free stack space and returns `Error::StackError` on failure.
|
||||
#[inline]
|
||||
pub unsafe fn check_stack(state: *mut ffi::lua_State, amount: c_int) -> Result<()> {
|
||||
pub(crate) unsafe fn check_stack(state: *mut ffi::lua_State, amount: c_int) -> Result<()> {
|
||||
if ffi::lua_checkstack(state, amount) == 0 {
|
||||
Err(Error::StackError)
|
||||
} else {
|
||||
@@ -47,7 +45,7 @@ pub unsafe fn check_stack(state: *mut ffi::lua_State, amount: c_int) -> Result<(
|
||||
}
|
||||
}
|
||||
|
||||
pub struct StackGuard {
|
||||
pub(crate) struct StackGuard {
|
||||
state: *mut ffi::lua_State,
|
||||
top: c_int,
|
||||
}
|
||||
@@ -57,7 +55,7 @@ impl StackGuard {
|
||||
// stack size and drop any extra elements. If the stack size at the end is *smaller* than at
|
||||
// the beginning, this is considered a fatal logic error and will result in a panic.
|
||||
#[inline]
|
||||
pub unsafe fn new(state: *mut ffi::lua_State) -> StackGuard {
|
||||
pub(crate) unsafe fn new(state: *mut ffi::lua_State) -> StackGuard {
|
||||
StackGuard {
|
||||
state,
|
||||
top: ffi::lua_gettop(state),
|
||||
@@ -65,7 +63,8 @@ impl StackGuard {
|
||||
}
|
||||
|
||||
// Same as `new()`, but allows specifying the expected stack size at the end of the scope.
|
||||
pub const fn with_top(state: *mut ffi::lua_State, top: c_int) -> StackGuard {
|
||||
#[inline]
|
||||
pub(crate) fn with_top(state: *mut ffi::lua_State, top: c_int) -> StackGuard {
|
||||
StackGuard { state, top }
|
||||
}
|
||||
}
|
||||
@@ -84,164 +83,9 @@ impl Drop for StackGuard {
|
||||
}
|
||||
}
|
||||
|
||||
// Call a function that calls into the Lua API and may trigger a Lua error (longjmp) in a safe way.
|
||||
// Wraps the inner function in a call to `lua_pcall`, so the inner function only has access to a
|
||||
// limited lua stack. `nargs` is the same as the the parameter to `lua_pcall`, and `nresults` is
|
||||
// always `LUA_MULTRET`. Provided function must *not* panic, and since it will generally be lonjmping,
|
||||
// should not contain any values that implements Drop.
|
||||
// Internally uses 2 extra stack spaces, and does not call checkstack.
|
||||
pub unsafe fn protect_lua_call(
|
||||
state: *mut ffi::lua_State,
|
||||
nargs: c_int,
|
||||
f: unsafe extern "C-unwind" fn(*mut ffi::lua_State) -> c_int,
|
||||
) -> Result<()> {
|
||||
let stack_start = ffi::lua_gettop(state) - nargs;
|
||||
|
||||
MemoryState::relax_limit_with(state, || {
|
||||
ffi::lua_pushcfunction(state, error_traceback);
|
||||
ffi::lua_pushcfunction(state, f);
|
||||
});
|
||||
if nargs > 0 {
|
||||
ffi::lua_rotate(state, stack_start + 1, 2);
|
||||
}
|
||||
|
||||
let ret = ffi::lua_pcall(state, nargs, ffi::LUA_MULTRET, stack_start + 1);
|
||||
ffi::lua_remove(state, stack_start + 1);
|
||||
|
||||
if ret == ffi::LUA_OK {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(pop_error(state, ret))
|
||||
}
|
||||
}
|
||||
|
||||
// Call a function that calls into the Lua API and may trigger a Lua error (longjmp) in a safe way.
|
||||
// Wraps the inner function in a call to `lua_pcall`, so the inner function only has access to a
|
||||
// limited lua stack. `nargs` and `nresults` are similar to the parameters of `lua_pcall`, but the
|
||||
// given function return type is not the return value count, instead the inner function return
|
||||
// values are assumed to match the `nresults` param. Provided function must *not* panic, and since it
|
||||
// will generally be lonjmping, should not contain any values that implements Drop.
|
||||
// Internally uses 3 extra stack spaces, and does not call checkstack.
|
||||
pub unsafe fn protect_lua_closure<F, R>(
|
||||
state: *mut ffi::lua_State,
|
||||
nargs: c_int,
|
||||
nresults: c_int,
|
||||
f: F,
|
||||
) -> Result<R>
|
||||
where
|
||||
F: Fn(*mut ffi::lua_State) -> R,
|
||||
R: Copy,
|
||||
{
|
||||
struct Params<F, R: Copy> {
|
||||
function: F,
|
||||
result: MaybeUninit<R>,
|
||||
nresults: c_int,
|
||||
}
|
||||
|
||||
unsafe extern "C-unwind" fn do_call<F, R>(state: *mut ffi::lua_State) -> c_int
|
||||
where
|
||||
F: Fn(*mut ffi::lua_State) -> R,
|
||||
R: Copy,
|
||||
{
|
||||
let params = ffi::lua_touserdata(state, -1) as *mut Params<F, R>;
|
||||
ffi::lua_pop(state, 1);
|
||||
|
||||
(*params).result.write(((*params).function)(state));
|
||||
|
||||
if (*params).nresults == ffi::LUA_MULTRET {
|
||||
ffi::lua_gettop(state)
|
||||
} else {
|
||||
(*params).nresults
|
||||
}
|
||||
}
|
||||
|
||||
let stack_start = ffi::lua_gettop(state) - nargs;
|
||||
|
||||
MemoryState::relax_limit_with(state, || {
|
||||
ffi::lua_pushcfunction(state, error_traceback);
|
||||
ffi::lua_pushcfunction(state, do_call::<F, R>);
|
||||
});
|
||||
if nargs > 0 {
|
||||
ffi::lua_rotate(state, stack_start + 1, 2);
|
||||
}
|
||||
|
||||
let mut params = Params {
|
||||
function: f,
|
||||
result: MaybeUninit::uninit(),
|
||||
nresults,
|
||||
};
|
||||
|
||||
ffi::lua_pushlightuserdata(state, &mut params as *mut Params<F, R> as *mut c_void);
|
||||
let ret = ffi::lua_pcall(state, nargs + 1, nresults, stack_start + 1);
|
||||
ffi::lua_remove(state, stack_start + 1);
|
||||
|
||||
if ret == ffi::LUA_OK {
|
||||
// `LUA_OK` is only returned when the `do_call` function has completed successfully, so
|
||||
// `params.result` is definitely initialized.
|
||||
Ok(params.result.assume_init())
|
||||
} else {
|
||||
Err(pop_error(state, ret))
|
||||
}
|
||||
}
|
||||
|
||||
// Pops an error off of the stack and returns it. The specific behavior depends on the type of the
|
||||
// error at the top of the stack:
|
||||
// 1) If the error is actually a WrappedPanic, this will continue the panic.
|
||||
// 2) If the error on the top of the stack is actually a WrappedError, just returns it.
|
||||
// 3) Otherwise, interprets the error as the appropriate lua error.
|
||||
// Uses 2 stack spaces, does not call checkstack.
|
||||
pub unsafe fn pop_error(state: *mut ffi::lua_State, err_code: c_int) -> Error {
|
||||
mlua_debug_assert!(
|
||||
err_code != ffi::LUA_OK && err_code != ffi::LUA_YIELD,
|
||||
"pop_error called with non-error return code"
|
||||
);
|
||||
|
||||
match get_gc_userdata::<WrappedFailure>(state, -1, ptr::null()).as_mut() {
|
||||
Some(WrappedFailure::Error(err)) => {
|
||||
ffi::lua_pop(state, 1);
|
||||
err.clone()
|
||||
}
|
||||
Some(WrappedFailure::Panic(panic)) => {
|
||||
if let Some(p) = panic.take() {
|
||||
resume_unwind(p);
|
||||
} else {
|
||||
Error::PreviouslyResumedPanic
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
let err_string = to_string(state, -1);
|
||||
ffi::lua_pop(state, 1);
|
||||
|
||||
match err_code {
|
||||
ffi::LUA_ERRRUN => Error::RuntimeError(err_string),
|
||||
ffi::LUA_ERRSYNTAX => {
|
||||
Error::SyntaxError {
|
||||
// This seems terrible, but as far as I can tell, this is exactly what the
|
||||
// stock Lua REPL does.
|
||||
incomplete_input: err_string.ends_with("<eof>")
|
||||
|| err_string.ends_with("'<eof>'"),
|
||||
message: err_string,
|
||||
}
|
||||
}
|
||||
ffi::LUA_ERRERR => {
|
||||
// This error is raised when the error handler raises an error too many times
|
||||
// recursively, and continuing to trigger the error handler would cause a stack
|
||||
// overflow. It is not very useful to differentiate between this and "ordinary"
|
||||
// runtime errors, so we handle them the same way.
|
||||
Error::RuntimeError(err_string)
|
||||
}
|
||||
ffi::LUA_ERRMEM => Error::MemoryError(err_string),
|
||||
#[cfg(any(feature = "lua53", feature = "lua52"))]
|
||||
ffi::LUA_ERRGCMM => Error::GarbageCollectorError(err_string),
|
||||
_ => mlua_panic!("unrecognized lua error code"),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Uses 3 (or 1 if unprotected) stack spaces, does not call checkstack.
|
||||
#[inline(always)]
|
||||
pub unsafe fn push_string(state: *mut ffi::lua_State, s: &[u8], protect: bool) -> Result<()> {
|
||||
pub(crate) unsafe fn push_string(state: *mut ffi::lua_State, s: &[u8], protect: bool) -> Result<()> {
|
||||
// Always use protected mode if the string is too long
|
||||
if protect || s.len() > (1 << 30) {
|
||||
protect_lua!(state, 0, 1, |state| {
|
||||
@@ -256,7 +100,7 @@ pub unsafe fn push_string(state: *mut ffi::lua_State, s: &[u8], protect: bool) -
|
||||
// Uses 3 stack spaces (when protect), does not call checkstack.
|
||||
#[cfg(feature = "luau")]
|
||||
#[inline(always)]
|
||||
pub unsafe fn push_buffer(state: *mut ffi::lua_State, b: &[u8], protect: bool) -> Result<()> {
|
||||
pub(crate) unsafe fn push_buffer(state: *mut ffi::lua_State, b: &[u8], protect: bool) -> Result<()> {
|
||||
let data = if protect {
|
||||
protect_lua!(state, 0, 1, |state| ffi::lua_newbuffer(state, b.len()))?
|
||||
} else {
|
||||
@@ -269,7 +113,7 @@ pub unsafe fn push_buffer(state: *mut ffi::lua_State, b: &[u8], protect: bool) -
|
||||
|
||||
// Uses 3 stack spaces, does not call checkstack.
|
||||
#[inline]
|
||||
pub unsafe fn push_table(
|
||||
pub(crate) unsafe fn push_table(
|
||||
state: *mut ffi::lua_State,
|
||||
narr: usize,
|
||||
nrec: usize,
|
||||
@@ -286,7 +130,7 @@ pub unsafe fn push_table(
|
||||
}
|
||||
|
||||
// Uses 4 stack spaces, does not call checkstack.
|
||||
pub unsafe fn rawset_field(state: *mut ffi::lua_State, table: c_int, field: &str) -> Result<()> {
|
||||
pub(crate) unsafe fn rawset_field(state: *mut ffi::lua_State, table: c_int, field: &str) -> Result<()> {
|
||||
ffi::lua_pushvalue(state, table);
|
||||
protect_lua!(state, 2, 0, |state| {
|
||||
ffi::lua_pushlstring(state, field.as_ptr() as *const c_char, field.len());
|
||||
@@ -295,441 +139,8 @@ pub unsafe fn rawset_field(state: *mut ffi::lua_State, table: c_int, field: &str
|
||||
})
|
||||
}
|
||||
|
||||
// Internally uses 3 stack spaces, does not call checkstack.
|
||||
#[inline]
|
||||
pub unsafe fn push_userdata<T>(state: *mut ffi::lua_State, t: T, protect: bool) -> Result<()> {
|
||||
#[cfg(not(feature = "luau"))]
|
||||
let ud = if protect {
|
||||
protect_lua!(state, 0, 1, |state| {
|
||||
ffi::lua_newuserdata(state, std::mem::size_of::<T>()) as *mut T
|
||||
})?
|
||||
} else {
|
||||
ffi::lua_newuserdata(state, std::mem::size_of::<T>()) as *mut T
|
||||
};
|
||||
#[cfg(feature = "luau")]
|
||||
let ud = if protect {
|
||||
protect_lua!(state, 0, 1, |state| { ffi::lua_newuserdata_t::<T>(state) })?
|
||||
} else {
|
||||
ffi::lua_newuserdata_t::<T>(state)
|
||||
};
|
||||
ptr::write(ud, t);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Internally uses 3 stack spaces, does not call checkstack.
|
||||
#[cfg(feature = "lua54")]
|
||||
#[inline]
|
||||
pub unsafe fn push_userdata_uv<T>(
|
||||
state: *mut ffi::lua_State,
|
||||
t: T,
|
||||
nuvalue: c_int,
|
||||
protect: bool,
|
||||
) -> Result<()> {
|
||||
let ud = if protect {
|
||||
protect_lua!(state, 0, 1, |state| {
|
||||
ffi::lua_newuserdatauv(state, std::mem::size_of::<T>(), nuvalue) as *mut T
|
||||
})?
|
||||
} else {
|
||||
ffi::lua_newuserdatauv(state, std::mem::size_of::<T>(), nuvalue) as *mut T
|
||||
};
|
||||
ptr::write(ud, t);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub unsafe fn get_userdata<T>(state: *mut ffi::lua_State, index: c_int) -> *mut T {
|
||||
let ud = ffi::lua_touserdata(state, index) as *mut T;
|
||||
mlua_debug_assert!(!ud.is_null(), "userdata pointer is null");
|
||||
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 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.
|
||||
get_destructed_userdata_metatable(state);
|
||||
ffi::lua_setmetatable(state, -2);
|
||||
let ud = get_userdata::<T>(state, -1);
|
||||
|
||||
// Update userdata tag to disable destructor and mark as destructed
|
||||
#[cfg(feature = "luau")]
|
||||
ffi::lua_setuserdatatag(state, -1, 1);
|
||||
|
||||
ffi::lua_pop(state, 1);
|
||||
ptr::read(ud)
|
||||
}
|
||||
|
||||
// Pushes the userdata and attaches a metatable with __gc method.
|
||||
// Internally uses 3 stack spaces, does not call checkstack.
|
||||
pub unsafe fn push_gc_userdata<T: Any>(
|
||||
state: *mut ffi::lua_State,
|
||||
t: T,
|
||||
protect: bool,
|
||||
) -> Result<()> {
|
||||
push_userdata(state, t, protect)?;
|
||||
get_gc_metatable::<T>(state);
|
||||
ffi::lua_setmetatable(state, -2);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Uses 2 stack spaces, does not call checkstack
|
||||
pub unsafe fn get_gc_userdata<T: Any>(
|
||||
state: *mut ffi::lua_State,
|
||||
index: c_int,
|
||||
mt_ptr: *const c_void,
|
||||
) -> *mut T {
|
||||
let ud = ffi::lua_touserdata(state, index) as *mut T;
|
||||
if ud.is_null() || ffi::lua_getmetatable(state, index) == 0 {
|
||||
return ptr::null_mut();
|
||||
}
|
||||
if !mt_ptr.is_null() {
|
||||
let ud_mt_ptr = ffi::lua_topointer(state, -1);
|
||||
ffi::lua_pop(state, 1);
|
||||
if !ptr::eq(ud_mt_ptr, mt_ptr) {
|
||||
return ptr::null_mut();
|
||||
}
|
||||
} else {
|
||||
get_gc_metatable::<T>(state);
|
||||
let res = ffi::lua_rawequal(state, -1, -2);
|
||||
ffi::lua_pop(state, 2);
|
||||
if res == 0 {
|
||||
return ptr::null_mut();
|
||||
}
|
||||
}
|
||||
ud
|
||||
}
|
||||
|
||||
unsafe extern "C-unwind" fn lua_error_impl(state: *mut ffi::lua_State) -> c_int {
|
||||
ffi::lua_error(state);
|
||||
}
|
||||
|
||||
unsafe extern "C-unwind" fn lua_isfunction_impl(state: *mut ffi::lua_State) -> c_int {
|
||||
ffi::lua_pushboolean(state, ffi::lua_isfunction(state, -1));
|
||||
1
|
||||
}
|
||||
|
||||
unsafe extern "C-unwind" fn lua_istable_impl(state: *mut ffi::lua_State) -> c_int {
|
||||
ffi::lua_pushboolean(state, ffi::lua_istable(state, -1));
|
||||
1
|
||||
}
|
||||
|
||||
unsafe fn init_userdata_metatable_index(state: *mut ffi::lua_State) -> Result<()> {
|
||||
let index_key = &USERDATA_METATABLE_INDEX as *const u8 as *const _;
|
||||
if ffi::lua_rawgetp(state, ffi::LUA_REGISTRYINDEX, index_key) == ffi::LUA_TFUNCTION {
|
||||
return Ok(());
|
||||
}
|
||||
ffi::lua_pop(state, 1);
|
||||
|
||||
// Create and cache `__index` generator
|
||||
let code = cstr!(
|
||||
r#"
|
||||
local error, isfunction, istable = ...
|
||||
return function (__index, field_getters, methods)
|
||||
-- Common case: has field getters and index is a table
|
||||
if field_getters ~= nil and methods == nil and istable(__index) then
|
||||
return function (self, key)
|
||||
local field_getter = field_getters[key]
|
||||
if field_getter ~= nil then
|
||||
return field_getter(self)
|
||||
end
|
||||
return __index[key]
|
||||
end
|
||||
end
|
||||
|
||||
return function (self, key)
|
||||
if field_getters ~= nil then
|
||||
local field_getter = field_getters[key]
|
||||
if field_getter ~= nil then
|
||||
return field_getter(self)
|
||||
end
|
||||
end
|
||||
|
||||
if methods ~= nil then
|
||||
local method = methods[key]
|
||||
if method ~= nil then
|
||||
return method
|
||||
end
|
||||
end
|
||||
|
||||
if isfunction(__index) then
|
||||
return __index(self, key)
|
||||
elseif __index == nil then
|
||||
error("attempt to get an unknown field '"..key.."'")
|
||||
else
|
||||
return __index[key]
|
||||
end
|
||||
end
|
||||
end
|
||||
"#
|
||||
);
|
||||
let code_len = CStr::from_ptr(code).to_bytes().len();
|
||||
protect_lua!(state, 0, 1, |state| {
|
||||
let ret = ffi::luaL_loadbuffer(state, code, code_len, cstr!("__mlua_index"));
|
||||
if ret != ffi::LUA_OK {
|
||||
ffi::lua_error(state);
|
||||
}
|
||||
ffi::lua_pushcfunction(state, lua_error_impl);
|
||||
ffi::lua_pushcfunction(state, lua_isfunction_impl);
|
||||
ffi::lua_pushcfunction(state, lua_istable_impl);
|
||||
ffi::lua_call(state, 3, 1);
|
||||
|
||||
#[cfg(feature = "luau-jit")]
|
||||
if ffi::luau_codegen_supported() != 0 {
|
||||
ffi::luau_codegen_compile(state, -1);
|
||||
}
|
||||
|
||||
// Store in the registry
|
||||
ffi::lua_pushvalue(state, -1);
|
||||
ffi::lua_rawsetp(state, ffi::LUA_REGISTRYINDEX, index_key);
|
||||
})
|
||||
}
|
||||
|
||||
pub unsafe fn init_userdata_metatable_newindex(state: *mut ffi::lua_State) -> Result<()> {
|
||||
let newindex_key = &USERDATA_METATABLE_NEWINDEX as *const u8 as *const _;
|
||||
if ffi::lua_rawgetp(state, ffi::LUA_REGISTRYINDEX, newindex_key) == ffi::LUA_TFUNCTION {
|
||||
return Ok(());
|
||||
}
|
||||
ffi::lua_pop(state, 1);
|
||||
|
||||
// Create and cache `__newindex` generator
|
||||
let code = cstr!(
|
||||
r#"
|
||||
local error, isfunction = ...
|
||||
return function (__newindex, field_setters)
|
||||
return function (self, key, value)
|
||||
if field_setters ~= nil then
|
||||
local field_setter = field_setters[key]
|
||||
if field_setter ~= nil then
|
||||
field_setter(self, value)
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
if isfunction(__newindex) then
|
||||
__newindex(self, key, value)
|
||||
elseif __newindex == nil then
|
||||
error("attempt to set an unknown field '"..key.."'")
|
||||
else
|
||||
__newindex[key] = value
|
||||
end
|
||||
end
|
||||
end
|
||||
"#
|
||||
);
|
||||
let code_len = CStr::from_ptr(code).to_bytes().len();
|
||||
protect_lua!(state, 0, 1, |state| {
|
||||
let ret = ffi::luaL_loadbuffer(state, code, code_len, cstr!("__mlua_newindex"));
|
||||
if ret != ffi::LUA_OK {
|
||||
ffi::lua_error(state);
|
||||
}
|
||||
ffi::lua_pushcfunction(state, lua_error_impl);
|
||||
ffi::lua_pushcfunction(state, lua_isfunction_impl);
|
||||
ffi::lua_call(state, 2, 1);
|
||||
|
||||
#[cfg(feature = "luau-jit")]
|
||||
if ffi::luau_codegen_supported() != 0 {
|
||||
ffi::luau_codegen_compile(state, -1);
|
||||
}
|
||||
|
||||
// Store in the registry
|
||||
ffi::lua_pushvalue(state, -1);
|
||||
ffi::lua_rawsetp(state, ffi::LUA_REGISTRYINDEX, newindex_key);
|
||||
})
|
||||
}
|
||||
|
||||
// Populates the given table with the appropriate members to be a userdata metatable for the given type.
|
||||
// This function takes the given table at the `metatable` index, and adds an appropriate `__gc` member
|
||||
// to it for the given type and a `__metatable` entry to protect the table from script access.
|
||||
// The function also, if given a `field_getters` or `methods` tables, will create an `__index` metamethod
|
||||
// (capturing previous one) to lookup in `field_getters` first, then `methods` and falling back to the
|
||||
// captured `__index` if no matches found.
|
||||
// The same is also applicable for `__newindex` metamethod and `field_setters` table.
|
||||
// Internally uses 9 stack spaces and does not call checkstack.
|
||||
pub unsafe fn init_userdata_metatable(
|
||||
state: *mut ffi::lua_State,
|
||||
metatable: c_int,
|
||||
field_getters: Option<c_int>,
|
||||
field_setters: Option<c_int>,
|
||||
methods: Option<c_int>,
|
||||
extra_init: Option<fn(*mut ffi::lua_State) -> Result<()>>,
|
||||
) -> Result<()> {
|
||||
ffi::lua_pushvalue(state, metatable);
|
||||
|
||||
if field_getters.is_some() || methods.is_some() {
|
||||
// Push `__index` generator function
|
||||
init_userdata_metatable_index(state)?;
|
||||
|
||||
push_string(state, b"__index", true)?;
|
||||
let index_type = ffi::lua_rawget(state, -3);
|
||||
match index_type {
|
||||
ffi::LUA_TNIL | ffi::LUA_TTABLE | ffi::LUA_TFUNCTION => {
|
||||
for &idx in &[field_getters, methods] {
|
||||
if let Some(idx) = idx {
|
||||
ffi::lua_pushvalue(state, idx);
|
||||
} else {
|
||||
ffi::lua_pushnil(state);
|
||||
}
|
||||
}
|
||||
|
||||
// Generate `__index`
|
||||
protect_lua!(state, 4, 1, fn(state) ffi::lua_call(state, 3, 1))?;
|
||||
}
|
||||
_ => mlua_panic!("improper __index type {}", index_type),
|
||||
}
|
||||
|
||||
rawset_field(state, -2, "__index")?;
|
||||
}
|
||||
|
||||
if let Some(field_setters) = field_setters {
|
||||
// Push `__newindex` generator function
|
||||
init_userdata_metatable_newindex(state)?;
|
||||
|
||||
push_string(state, b"__newindex", true)?;
|
||||
let newindex_type = ffi::lua_rawget(state, -3);
|
||||
match newindex_type {
|
||||
ffi::LUA_TNIL | ffi::LUA_TTABLE | ffi::LUA_TFUNCTION => {
|
||||
ffi::lua_pushvalue(state, field_setters);
|
||||
// Generate `__newindex`
|
||||
protect_lua!(state, 3, 1, fn(state) ffi::lua_call(state, 2, 1))?;
|
||||
}
|
||||
_ => mlua_panic!("improper __newindex type {}", newindex_type),
|
||||
}
|
||||
|
||||
rawset_field(state, -2, "__newindex")?;
|
||||
}
|
||||
|
||||
// Additional initialization
|
||||
if let Some(extra_init) = extra_init {
|
||||
extra_init(state)?;
|
||||
}
|
||||
|
||||
ffi::lua_pushboolean(state, 0);
|
||||
rawset_field(state, -2, "__metatable")?;
|
||||
|
||||
ffi::lua_pop(state, 1);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "luau"))]
|
||||
pub unsafe extern "C-unwind" fn userdata_destructor<T>(state: *mut ffi::lua_State) -> c_int {
|
||||
// It's probably NOT a good idea to catch Rust panics in finalizer
|
||||
// Lua 5.4 ignores it, other versions generates `LUA_ERRGCMM` without calling message handler
|
||||
take_userdata::<T>(state);
|
||||
0
|
||||
}
|
||||
|
||||
// In the context of a lua callback, this will call the given function and if the given function
|
||||
// returns an error, *or if the given function panics*, this will result in a call to `lua_error` (a
|
||||
// longjmp). The error or panic is wrapped in such a way that when calling `pop_error` back on
|
||||
// the Rust side, it will resume the panic.
|
||||
//
|
||||
// This function assumes the structure of the stack at the beginning of a callback, that the only
|
||||
// elements on the stack are the arguments to the callback.
|
||||
//
|
||||
// This function uses some of the bottom of the stack for error handling, the given callback will be
|
||||
// given the number of arguments available as an argument, and should return the number of returns
|
||||
// as normal, but cannot assume that the arguments available start at 0.
|
||||
pub unsafe fn callback_error<F, R>(state: *mut ffi::lua_State, f: F) -> R
|
||||
where
|
||||
F: FnOnce(c_int) -> Result<R>,
|
||||
{
|
||||
let nargs = ffi::lua_gettop(state);
|
||||
|
||||
// We need 2 extra stack spaces to store preallocated memory and error/panic metatable
|
||||
let extra_stack = if nargs < 2 { 2 - nargs } else { 1 };
|
||||
ffi::luaL_checkstack(
|
||||
state,
|
||||
extra_stack,
|
||||
cstr!("not enough stack space for callback error handling"),
|
||||
);
|
||||
|
||||
// We cannot shadow Rust errors with Lua ones, we pre-allocate enough memory
|
||||
// to store a wrapped error or panic *before* we proceed.
|
||||
let ud = WrappedFailure::new_userdata(state);
|
||||
ffi::lua_rotate(state, 1, 1);
|
||||
|
||||
match catch_unwind(AssertUnwindSafe(|| f(nargs))) {
|
||||
Ok(Ok(r)) => {
|
||||
ffi::lua_remove(state, 1);
|
||||
r
|
||||
}
|
||||
Ok(Err(err)) => {
|
||||
ffi::lua_settop(state, 1);
|
||||
|
||||
// Build `CallbackError` with traceback
|
||||
let traceback = if ffi::lua_checkstack(state, ffi::LUA_TRACEBACK_STACK) != 0 {
|
||||
ffi::luaL_traceback(state, state, ptr::null(), 0);
|
||||
let traceback = to_string(state, -1);
|
||||
ffi::lua_pop(state, 1);
|
||||
traceback
|
||||
} else {
|
||||
"<not enough stack space for traceback>".to_string()
|
||||
};
|
||||
let cause = Arc::new(err);
|
||||
let wrapped_error = WrappedFailure::Error(Error::CallbackError { traceback, cause });
|
||||
ptr::write(ud, wrapped_error);
|
||||
get_gc_metatable::<WrappedFailure>(state);
|
||||
ffi::lua_setmetatable(state, -2);
|
||||
|
||||
ffi::lua_error(state)
|
||||
}
|
||||
Err(p) => {
|
||||
ffi::lua_settop(state, 1);
|
||||
ptr::write(ud, WrappedFailure::Panic(Some(p)));
|
||||
get_gc_metatable::<WrappedFailure>(state);
|
||||
ffi::lua_setmetatable(state, -2);
|
||||
ffi::lua_error(state)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe extern "C-unwind" fn error_traceback(state: *mut ffi::lua_State) -> c_int {
|
||||
// Luau calls error handler for memory allocation errors, skip it
|
||||
// See https://github.com/Roblox/luau/issues/880
|
||||
#[cfg(feature = "luau")]
|
||||
if MemoryState::limit_reached(state) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ffi::lua_checkstack(state, 2) == 0 {
|
||||
// If we don't have enough stack space to even check the error type, do
|
||||
// nothing so we don't risk shadowing a rust panic.
|
||||
return 1;
|
||||
}
|
||||
|
||||
if get_gc_userdata::<WrappedFailure>(state, -1, ptr::null()).is_null() {
|
||||
let s = ffi::luaL_tolstring(state, -1, ptr::null_mut());
|
||||
if ffi::lua_checkstack(state, ffi::LUA_TRACEBACK_STACK) != 0 {
|
||||
ffi::luaL_traceback(state, state, s, 0);
|
||||
ffi::lua_remove(state, -2);
|
||||
}
|
||||
}
|
||||
|
||||
1
|
||||
}
|
||||
|
||||
// A variant of `error_traceback` that can safely inspect another (yielded) thread stack
|
||||
pub unsafe fn error_traceback_thread(state: *mut ffi::lua_State, thread: *mut ffi::lua_State) {
|
||||
// Move error object to the main thread to safely call `__tostring` metamethod if present
|
||||
ffi::lua_xmove(thread, state, 1);
|
||||
|
||||
if get_gc_userdata::<WrappedFailure>(state, -1, ptr::null()).is_null() {
|
||||
let s = ffi::luaL_tolstring(state, -1, ptr::null_mut());
|
||||
if ffi::lua_checkstack(state, ffi::LUA_TRACEBACK_STACK) != 0 {
|
||||
ffi::luaL_traceback(state, thread, s, 0);
|
||||
ffi::lua_remove(state, -2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// A variant of `pcall` that does not allow Lua to catch Rust panics from `callback_error`.
|
||||
pub unsafe extern "C-unwind" fn safe_pcall(state: *mut ffi::lua_State) -> c_int {
|
||||
pub(crate) unsafe extern "C-unwind" fn safe_pcall(state: *mut ffi::lua_State) -> c_int {
|
||||
ffi::luaL_checkstack(state, 2, ptr::null());
|
||||
|
||||
let top = ffi::lua_gettop(state);
|
||||
@@ -743,9 +154,8 @@ pub unsafe extern "C-unwind" fn safe_pcall(state: *mut ffi::lua_State) -> c_int
|
||||
ffi::lua_insert(state, 1);
|
||||
ffi::lua_gettop(state)
|
||||
} else {
|
||||
if let Some(WrappedFailure::Panic(_)) =
|
||||
get_gc_userdata::<WrappedFailure>(state, -1, ptr::null()).as_ref()
|
||||
{
|
||||
let wf_ud = get_internal_userdata::<WrappedFailure>(state, -1, ptr::null());
|
||||
if let Some(WrappedFailure::Panic(_)) = wf_ud.as_ref() {
|
||||
ffi::lua_error(state);
|
||||
}
|
||||
ffi::lua_pushboolean(state, 0);
|
||||
@@ -755,13 +165,12 @@ pub unsafe extern "C-unwind" fn safe_pcall(state: *mut ffi::lua_State) -> c_int
|
||||
}
|
||||
|
||||
// A variant of `xpcall` that does not allow Lua to catch Rust panics from `callback_error`.
|
||||
pub unsafe extern "C-unwind" fn safe_xpcall(state: *mut ffi::lua_State) -> c_int {
|
||||
pub(crate) unsafe extern "C-unwind" fn safe_xpcall(state: *mut ffi::lua_State) -> c_int {
|
||||
unsafe extern "C-unwind" fn xpcall_msgh(state: *mut ffi::lua_State) -> c_int {
|
||||
ffi::luaL_checkstack(state, 2, ptr::null());
|
||||
|
||||
if let Some(WrappedFailure::Panic(_)) =
|
||||
get_gc_userdata::<WrappedFailure>(state, -1, ptr::null()).as_ref()
|
||||
{
|
||||
let wf_ud = get_internal_userdata::<WrappedFailure>(state, -1, ptr::null());
|
||||
if let Some(WrappedFailure::Panic(_)) = wf_ud.as_ref() {
|
||||
1
|
||||
} else {
|
||||
ffi::lua_pushvalue(state, ffi::lua_upvalueindex(1));
|
||||
@@ -789,9 +198,8 @@ pub unsafe extern "C-unwind" fn safe_xpcall(state: *mut ffi::lua_State) -> c_int
|
||||
ffi::lua_insert(state, 2);
|
||||
ffi::lua_gettop(state) - 1
|
||||
} else {
|
||||
if let Some(WrappedFailure::Panic(_)) =
|
||||
get_gc_userdata::<WrappedFailure>(state, -1, ptr::null()).as_ref()
|
||||
{
|
||||
let wf_ud = get_internal_userdata::<WrappedFailure>(state, -1, ptr::null());
|
||||
if let Some(WrappedFailure::Panic(_)) = wf_ud.as_ref() {
|
||||
ffi::lua_error(state);
|
||||
}
|
||||
ffi::lua_pushboolean(state, 0);
|
||||
@@ -802,7 +210,7 @@ pub unsafe extern "C-unwind" fn safe_xpcall(state: *mut ffi::lua_State) -> c_int
|
||||
|
||||
// Returns Lua main thread for Lua >= 5.2 or checks that the passed thread is main for Lua 5.1.
|
||||
// Does not call lua_checkstack, uses 1 stack space.
|
||||
pub unsafe fn get_main_state(state: *mut ffi::lua_State) -> Option<*mut ffi::lua_State> {
|
||||
pub(crate) unsafe fn get_main_state(state: *mut ffi::lua_State) -> Option<*mut ffi::lua_State> {
|
||||
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
|
||||
{
|
||||
ffi::lua_rawgeti(state, ffi::LUA_REGISTRYINDEX, ffi::LUA_RIDX_MAINTHREAD);
|
||||
@@ -825,198 +233,6 @@ pub unsafe fn get_main_state(state: *mut ffi::lua_State) -> Option<*mut ffi::lua
|
||||
Some(ffi::lua_mainthread(state))
|
||||
}
|
||||
|
||||
// Initialize the internal (with __gc method) metatable for a type T.
|
||||
// Uses 6 stack spaces and calls checkstack.
|
||||
pub unsafe fn init_gc_metatable<T: Any>(
|
||||
state: *mut ffi::lua_State,
|
||||
customize_fn: Option<fn(*mut ffi::lua_State) -> Result<()>>,
|
||||
) -> Result<()> {
|
||||
check_stack(state, 6)?;
|
||||
|
||||
push_table(state, 0, 3, true)?;
|
||||
|
||||
#[cfg(not(feature = "luau"))]
|
||||
{
|
||||
ffi::lua_pushcfunction(state, userdata_destructor::<T>);
|
||||
rawset_field(state, -2, "__gc")?;
|
||||
}
|
||||
|
||||
ffi::lua_pushboolean(state, 0);
|
||||
rawset_field(state, -2, "__metatable")?;
|
||||
|
||||
if let Some(f) = customize_fn {
|
||||
f(state)?;
|
||||
}
|
||||
|
||||
let type_id = TypeId::of::<T>();
|
||||
let ref_addr = &METATABLE_CACHE[&type_id] as *const u8;
|
||||
protect_lua!(state, 1, 0, |state| {
|
||||
ffi::lua_rawsetp(state, ffi::LUA_REGISTRYINDEX, ref_addr as *const c_void);
|
||||
})?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub unsafe fn get_gc_metatable<T: Any>(state: *mut ffi::lua_State) {
|
||||
let type_id = TypeId::of::<T>();
|
||||
let ref_addr =
|
||||
mlua_expect!(METATABLE_CACHE.get(&type_id), "gc metatable does not exist") as *const u8;
|
||||
ffi::lua_rawgetp(state, ffi::LUA_REGISTRYINDEX, ref_addr as *const c_void);
|
||||
}
|
||||
|
||||
// Initialize the error, panic, and destructed userdata metatables.
|
||||
pub unsafe fn init_error_registry(state: *mut ffi::lua_State) -> Result<()> {
|
||||
check_stack(state, 7)?;
|
||||
|
||||
// Create error and panic metatables
|
||||
|
||||
unsafe extern "C-unwind" fn error_tostring(state: *mut ffi::lua_State) -> c_int {
|
||||
callback_error(state, |_| {
|
||||
check_stack(state, 3)?;
|
||||
|
||||
let err_buf = match get_gc_userdata::<WrappedFailure>(state, -1, ptr::null()).as_ref() {
|
||||
Some(WrappedFailure::Error(error)) => {
|
||||
let err_buf_key = &ERROR_PRINT_BUFFER_KEY as *const u8 as *const c_void;
|
||||
ffi::lua_rawgetp(state, ffi::LUA_REGISTRYINDEX, err_buf_key);
|
||||
let err_buf = ffi::lua_touserdata(state, -1) as *mut String;
|
||||
ffi::lua_pop(state, 2);
|
||||
|
||||
(*err_buf).clear();
|
||||
// Depending on how the API is used and what error types scripts are given, it may
|
||||
// be possible to make this consume arbitrary amounts of memory (for example, some
|
||||
// kind of recursive error structure?)
|
||||
let _ = write!(&mut (*err_buf), "{error}");
|
||||
Ok(err_buf)
|
||||
}
|
||||
Some(WrappedFailure::Panic(Some(ref panic))) => {
|
||||
let err_buf_key = &ERROR_PRINT_BUFFER_KEY as *const u8 as *const c_void;
|
||||
ffi::lua_rawgetp(state, ffi::LUA_REGISTRYINDEX, err_buf_key);
|
||||
let err_buf = ffi::lua_touserdata(state, -1) as *mut String;
|
||||
(*err_buf).clear();
|
||||
ffi::lua_pop(state, 2);
|
||||
|
||||
if let Some(msg) = panic.downcast_ref::<&str>() {
|
||||
let _ = write!(&mut (*err_buf), "{msg}");
|
||||
} else if let Some(msg) = panic.downcast_ref::<String>() {
|
||||
let _ = write!(&mut (*err_buf), "{msg}");
|
||||
} else {
|
||||
let _ = write!(&mut (*err_buf), "<panic>");
|
||||
};
|
||||
Ok(err_buf)
|
||||
}
|
||||
Some(WrappedFailure::Panic(None)) => Err(Error::PreviouslyResumedPanic),
|
||||
_ => {
|
||||
// I'm not sure whether this is possible to trigger without bugs in mlua?
|
||||
Err(Error::UserDataTypeMismatch)
|
||||
}
|
||||
}?;
|
||||
|
||||
push_string(state, (*err_buf).as_bytes(), true)?;
|
||||
(*err_buf).clear();
|
||||
|
||||
Ok(1)
|
||||
})
|
||||
}
|
||||
|
||||
init_gc_metatable::<WrappedFailure>(
|
||||
state,
|
||||
Some(|state| {
|
||||
ffi::lua_pushcfunction(state, error_tostring);
|
||||
rawset_field(state, -2, "__tostring")
|
||||
}),
|
||||
)?;
|
||||
|
||||
// Create destructed userdata metatable
|
||||
|
||||
unsafe extern "C-unwind" fn destructed_error(state: *mut ffi::lua_State) -> c_int {
|
||||
callback_error(state, |_| Err(Error::CallbackDestructed))
|
||||
}
|
||||
|
||||
push_table(state, 0, 26, true)?;
|
||||
ffi::lua_pushcfunction(state, destructed_error);
|
||||
for &method in &[
|
||||
"__add",
|
||||
"__sub",
|
||||
"__mul",
|
||||
"__div",
|
||||
"__mod",
|
||||
"__pow",
|
||||
"__unm",
|
||||
#[cfg(any(feature = "lua54", feature = "lua53", feature = "luau"))]
|
||||
"__idiv",
|
||||
#[cfg(any(feature = "lua54", feature = "lua53"))]
|
||||
"__band",
|
||||
#[cfg(any(feature = "lua54", feature = "lua53"))]
|
||||
"__bor",
|
||||
#[cfg(any(feature = "lua54", feature = "lua53"))]
|
||||
"__bxor",
|
||||
#[cfg(any(feature = "lua54", feature = "lua53"))]
|
||||
"__bnot",
|
||||
#[cfg(any(feature = "lua54", feature = "lua53"))]
|
||||
"__shl",
|
||||
#[cfg(any(feature = "lua54", feature = "lua53"))]
|
||||
"__shr",
|
||||
"__concat",
|
||||
"__len",
|
||||
"__eq",
|
||||
"__lt",
|
||||
"__le",
|
||||
"__index",
|
||||
"__newindex",
|
||||
"__call",
|
||||
"__tostring",
|
||||
#[cfg(any(
|
||||
feature = "lua54",
|
||||
feature = "lua53",
|
||||
feature = "lua52",
|
||||
feature = "luajit52"
|
||||
))]
|
||||
"__pairs",
|
||||
#[cfg(any(feature = "lua53", feature = "lua52", feature = "luajit52"))]
|
||||
"__ipairs",
|
||||
#[cfg(feature = "luau")]
|
||||
"__iter",
|
||||
#[cfg(feature = "lua54")]
|
||||
"__close",
|
||||
] {
|
||||
ffi::lua_pushvalue(state, -1);
|
||||
rawset_field(state, -3, method)?;
|
||||
}
|
||||
ffi::lua_pop(state, 1);
|
||||
|
||||
protect_lua!(state, 1, 0, fn(state) {
|
||||
let destructed_mt_key = &DESTRUCTED_USERDATA_METATABLE as *const u8 as *const c_void;
|
||||
ffi::lua_rawsetp(state, ffi::LUA_REGISTRYINDEX, destructed_mt_key);
|
||||
})?;
|
||||
|
||||
// Create error print buffer
|
||||
init_gc_metatable::<String>(state, None)?;
|
||||
push_gc_userdata(state, String::new(), true)?;
|
||||
protect_lua!(state, 1, 0, fn(state) {
|
||||
let err_buf_key = &ERROR_PRINT_BUFFER_KEY as *const u8 as *const c_void;
|
||||
ffi::lua_rawsetp(state, ffi::LUA_REGISTRYINDEX, err_buf_key);
|
||||
})?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) enum WrappedFailure {
|
||||
None,
|
||||
Error(Error),
|
||||
Panic(Option<Box<dyn Any + Send + 'static>>),
|
||||
}
|
||||
|
||||
impl WrappedFailure {
|
||||
pub(crate) unsafe fn new_userdata(state: *mut ffi::lua_State) -> *mut Self {
|
||||
#[cfg(feature = "luau")]
|
||||
let ud = ffi::lua_newuserdata_t::<Self>(state);
|
||||
#[cfg(not(feature = "luau"))]
|
||||
let ud = ffi::lua_newuserdata(state, std::mem::size_of::<Self>()) as *mut Self;
|
||||
ptr::write(ud, WrappedFailure::None);
|
||||
ud
|
||||
}
|
||||
}
|
||||
|
||||
// Converts the given lua value to a string in a reasonable format without causing a Lua error or
|
||||
// panicking.
|
||||
pub(crate) unsafe fn to_string(state: *mut ffi::lua_State, index: c_int) -> String {
|
||||
@@ -1065,11 +281,6 @@ pub(crate) unsafe fn to_string(state: *mut ffi::lua_State, index: c_int) -> Stri
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn get_destructed_userdata_metatable(state: *mut ffi::lua_State) {
|
||||
let key = &DESTRUCTED_USERDATA_METATABLE as *const u8 as *const c_void;
|
||||
ffi::lua_rawgetp(state, ffi::LUA_REGISTRYINDEX, key);
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn ptr_to_str<'a>(input: *const c_char) -> Option<&'a str> {
|
||||
if input.is_null() {
|
||||
return None;
|
||||
@@ -1091,9 +302,7 @@ pub(crate) fn linenumber_to_usize(n: c_int) -> Option<usize> {
|
||||
}
|
||||
}
|
||||
|
||||
static DESTRUCTED_USERDATA_METATABLE: u8 = 0;
|
||||
static ERROR_PRINT_BUFFER_KEY: u8 = 0;
|
||||
static USERDATA_METATABLE_INDEX: u8 = 0;
|
||||
static USERDATA_METATABLE_NEWINDEX: u8 = 0;
|
||||
|
||||
mod error;
|
||||
mod short_names;
|
||||
mod types;
|
||||
mod userdata;
|
||||
|
||||
+6
-14
@@ -25,22 +25,17 @@ pub(crate) fn short_type_name<T: ?Sized>() -> String {
|
||||
|
||||
// Collapse everything up to the next special character,
|
||||
// then skip over it
|
||||
if let Some(special_character_index) = rest_of_string
|
||||
.find(|c: char| [' ', '<', '>', '(', ')', '[', ']', ',', ';'].contains(&c))
|
||||
if let Some(special_character_index) =
|
||||
rest_of_string.find(|c: char| [' ', '<', '>', '(', ')', '[', ']', ',', ';'].contains(&c))
|
||||
{
|
||||
let segment_to_collapse = rest_of_string
|
||||
.get(0..special_character_index)
|
||||
.unwrap_or_default();
|
||||
let segment_to_collapse = rest_of_string.get(0..special_character_index).unwrap_or_default();
|
||||
parsed_name += collapse_type_name(segment_to_collapse);
|
||||
// Insert the special character
|
||||
let special_character =
|
||||
&rest_of_string[special_character_index..=special_character_index];
|
||||
let special_character = &rest_of_string[special_character_index..=special_character_index];
|
||||
parsed_name.push_str(special_character);
|
||||
|
||||
match special_character {
|
||||
">" | ")" | "]"
|
||||
if rest_of_string[special_character_index + 1..].starts_with("::") =>
|
||||
{
|
||||
">" | ")" | "]" if rest_of_string[special_character_index + 1..].starts_with("::") => {
|
||||
parsed_name.push_str("::");
|
||||
// Move the index past the "::"
|
||||
index += special_character_index + 3;
|
||||
@@ -77,9 +72,6 @@ mod tests {
|
||||
short_type_name::<HashMap<String, Option<[i32; 3]>>>(),
|
||||
"HashMap<String, Option<[i32; 3]>>"
|
||||
);
|
||||
assert_eq!(
|
||||
short_type_name::<dyn Fn(i32) -> i32>(),
|
||||
"dyn Fn(i32) -> i32"
|
||||
);
|
||||
assert_eq!(short_type_name::<dyn Fn(i32) -> i32>(), "dyn Fn(i32) -> i32");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
use std::any::Any;
|
||||
use std::os::raw::c_void;
|
||||
|
||||
use crate::types::{Callback, CallbackUpvalue};
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
use crate::types::{AsyncCallback, AsyncCallbackUpvalue, AsyncPollUpvalue};
|
||||
|
||||
pub(crate) trait TypeKey: Any {
|
||||
fn type_key() -> *const c_void;
|
||||
}
|
||||
|
||||
static STRING_TYPE_KEY: u8 = 0;
|
||||
|
||||
impl TypeKey for String {
|
||||
#[inline(always)]
|
||||
fn type_key() -> *const c_void {
|
||||
&STRING_TYPE_KEY as *const u8 as *const c_void
|
||||
}
|
||||
}
|
||||
|
||||
static CALLBACK_TYPE_KEY: u8 = 0;
|
||||
|
||||
impl TypeKey for Callback {
|
||||
#[inline(always)]
|
||||
fn type_key() -> *const c_void {
|
||||
&CALLBACK_TYPE_KEY as *const u8 as *const c_void
|
||||
}
|
||||
}
|
||||
|
||||
static CALLBACK_UPVALUE_TYPE_KEY: u8 = 0;
|
||||
|
||||
impl TypeKey for CallbackUpvalue {
|
||||
#[inline(always)]
|
||||
fn type_key() -> *const c_void {
|
||||
&CALLBACK_UPVALUE_TYPE_KEY as *const u8 as *const c_void
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
static ASYNC_CALLBACK_TYPE_KEY: u8 = 0;
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
impl TypeKey for AsyncCallback {
|
||||
#[inline(always)]
|
||||
fn type_key() -> *const c_void {
|
||||
&ASYNC_CALLBACK_TYPE_KEY as *const u8 as *const c_void
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
static ASYNC_CALLBACK_UPVALUE_TYPE_KEY: u8 = 0;
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
impl TypeKey for AsyncCallbackUpvalue {
|
||||
#[inline(always)]
|
||||
fn type_key() -> *const c_void {
|
||||
&ASYNC_CALLBACK_UPVALUE_TYPE_KEY as *const u8 as *const c_void
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
static ASYNC_POLL_UPVALUE_TYPE_KEY: u8 = 0;
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
impl TypeKey for AsyncPollUpvalue {
|
||||
#[inline(always)]
|
||||
fn type_key() -> *const c_void {
|
||||
&ASYNC_POLL_UPVALUE_TYPE_KEY as *const u8 as *const c_void
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
static WAKER_TYPE_KEY: u8 = 0;
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
impl TypeKey for Option<std::task::Waker> {
|
||||
#[inline(always)]
|
||||
fn type_key() -> *const c_void {
|
||||
&WAKER_TYPE_KEY as *const u8 as *const c_void
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,380 @@
|
||||
use std::ffi::CStr;
|
||||
use std::os::raw::{c_int, c_void};
|
||||
use std::{ptr, str};
|
||||
|
||||
use crate::error::Result;
|
||||
use crate::util::{check_stack, push_string, push_table, rawset_field, TypeKey};
|
||||
|
||||
// Pushes the userdata and attaches a metatable with __gc method.
|
||||
// Internally uses 3 stack spaces, does not call checkstack.
|
||||
pub(crate) unsafe fn push_internal_userdata<T: TypeKey>(
|
||||
state: *mut ffi::lua_State,
|
||||
t: T,
|
||||
protect: bool,
|
||||
) -> Result<()> {
|
||||
push_userdata(state, t, protect)?;
|
||||
get_internal_metatable::<T>(state);
|
||||
ffi::lua_setmetatable(state, -2);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
pub(crate) unsafe fn get_internal_metatable<T: TypeKey>(state: *mut ffi::lua_State) {
|
||||
ffi::lua_rawgetp(state, ffi::LUA_REGISTRYINDEX, T::type_key());
|
||||
debug_assert!(ffi::lua_isnil(state, -1) == 0, "internal metatable not found");
|
||||
}
|
||||
|
||||
// Initialize the internal metatable for a type T (with __gc method).
|
||||
// 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<()>>,
|
||||
) -> Result<()> {
|
||||
check_stack(state, 6)?;
|
||||
|
||||
push_table(state, 0, 3, true)?;
|
||||
|
||||
#[cfg(not(feature = "luau"))]
|
||||
{
|
||||
ffi::lua_pushcfunction(state, userdata_destructor::<T>);
|
||||
rawset_field(state, -2, "__gc")?;
|
||||
}
|
||||
|
||||
ffi::lua_pushboolean(state, 0);
|
||||
rawset_field(state, -2, "__metatable")?;
|
||||
|
||||
if let Some(f) = customize_fn {
|
||||
f(state)?;
|
||||
}
|
||||
|
||||
protect_lua!(state, 1, 0, |state| {
|
||||
ffi::lua_rawsetp(state, ffi::LUA_REGISTRYINDEX, T::type_key());
|
||||
})?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Uses 2 stack spaces, does not call checkstack
|
||||
pub(crate) unsafe fn get_internal_userdata<T: TypeKey>(
|
||||
state: *mut ffi::lua_State,
|
||||
index: c_int,
|
||||
type_mt_ptr: *const c_void,
|
||||
) -> *mut T {
|
||||
let ud = ffi::lua_touserdata(state, index) as *mut T;
|
||||
if ud.is_null() || ffi::lua_getmetatable(state, index) == 0 {
|
||||
return ptr::null_mut();
|
||||
}
|
||||
if !type_mt_ptr.is_null() {
|
||||
let ud_mt_ptr = ffi::lua_topointer(state, -1);
|
||||
ffi::lua_pop(state, 1);
|
||||
if ud_mt_ptr != type_mt_ptr {
|
||||
return ptr::null_mut();
|
||||
}
|
||||
} else {
|
||||
get_internal_metatable::<T>(state);
|
||||
let res = ffi::lua_rawequal(state, -1, -2);
|
||||
ffi::lua_pop(state, 2);
|
||||
if res == 0 {
|
||||
return ptr::null_mut();
|
||||
}
|
||||
}
|
||||
ud
|
||||
}
|
||||
|
||||
// Internally uses 3 stack spaces, does not call checkstack.
|
||||
#[inline]
|
||||
pub(crate) unsafe fn push_userdata<T>(state: *mut ffi::lua_State, t: T, protect: bool) -> Result<()> {
|
||||
#[cfg(not(feature = "luau"))]
|
||||
let ud = if protect {
|
||||
protect_lua!(state, 0, 1, |state| {
|
||||
ffi::lua_newuserdata(state, std::mem::size_of::<T>()) as *mut T
|
||||
})?
|
||||
} else {
|
||||
ffi::lua_newuserdata(state, std::mem::size_of::<T>()) as *mut T
|
||||
};
|
||||
#[cfg(feature = "luau")]
|
||||
let ud = if protect {
|
||||
protect_lua!(state, 0, 1, |state| { ffi::lua_newuserdata_t::<T>(state) })?
|
||||
} else {
|
||||
ffi::lua_newuserdata_t::<T>(state)
|
||||
};
|
||||
ptr::write(ud, t);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Internally uses 3 stack spaces, does not call checkstack.
|
||||
#[cfg(feature = "lua54")]
|
||||
#[inline]
|
||||
pub(crate) unsafe fn push_userdata_uv<T>(
|
||||
state: *mut ffi::lua_State,
|
||||
t: T,
|
||||
nuvalue: c_int,
|
||||
protect: bool,
|
||||
) -> Result<()> {
|
||||
let ud = if protect {
|
||||
protect_lua!(state, 0, 1, |state| {
|
||||
ffi::lua_newuserdatauv(state, std::mem::size_of::<T>(), nuvalue) as *mut T
|
||||
})?
|
||||
} else {
|
||||
ffi::lua_newuserdatauv(state, std::mem::size_of::<T>(), nuvalue) as *mut T
|
||||
};
|
||||
ptr::write(ud, t);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) unsafe fn get_userdata<T>(state: *mut ffi::lua_State, index: c_int) -> *mut T {
|
||||
let ud = ffi::lua_touserdata(state, index) as *mut T;
|
||||
mlua_debug_assert!(!ud.is_null(), "userdata pointer is null");
|
||||
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.
|
||||
get_destructed_userdata_metatable(state);
|
||||
ffi::lua_setmetatable(state, -2);
|
||||
let ud = get_userdata::<T>(state, -1);
|
||||
|
||||
// Update userdata tag to disable destructor and mark as destructed
|
||||
#[cfg(feature = "luau")]
|
||||
ffi::lua_setuserdatatag(state, -1, 1);
|
||||
|
||||
ffi::lua_pop(state, 1);
|
||||
ptr::read(ud)
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn get_destructed_userdata_metatable(state: *mut ffi::lua_State) {
|
||||
let key = &DESTRUCTED_USERDATA_METATABLE as *const u8 as *const c_void;
|
||||
ffi::lua_rawgetp(state, ffi::LUA_REGISTRYINDEX, key);
|
||||
}
|
||||
|
||||
// Populates the given table with the appropriate members to be a userdata metatable for the given
|
||||
// type. This function takes the given table at the `metatable` index, and adds an appropriate
|
||||
// `__gc` member to it for the given type and a `__metatable` entry to protect the table from script
|
||||
// access. The function also, if given a `field_getters` or `methods` tables, will create an
|
||||
// `__index` metamethod (capturing previous one) to lookup in `field_getters` first, then `methods`
|
||||
// and falling back to the captured `__index` if no matches found.
|
||||
// The same is also applicable for `__newindex` metamethod and `field_setters` table.
|
||||
// Internally uses 9 stack spaces and does not call checkstack.
|
||||
pub(crate) unsafe fn init_userdata_metatable(
|
||||
state: *mut ffi::lua_State,
|
||||
metatable: c_int,
|
||||
field_getters: Option<c_int>,
|
||||
field_setters: Option<c_int>,
|
||||
methods: Option<c_int>,
|
||||
extra_init: Option<fn(*mut ffi::lua_State) -> Result<()>>,
|
||||
) -> Result<()> {
|
||||
ffi::lua_pushvalue(state, metatable);
|
||||
|
||||
if field_getters.is_some() || methods.is_some() {
|
||||
// Push `__index` generator function
|
||||
init_userdata_metatable_index(state)?;
|
||||
|
||||
push_string(state, b"__index", true)?;
|
||||
let index_type = ffi::lua_rawget(state, -3);
|
||||
match index_type {
|
||||
ffi::LUA_TNIL | ffi::LUA_TTABLE | ffi::LUA_TFUNCTION => {
|
||||
for &idx in &[field_getters, methods] {
|
||||
if let Some(idx) = idx {
|
||||
ffi::lua_pushvalue(state, idx);
|
||||
} else {
|
||||
ffi::lua_pushnil(state);
|
||||
}
|
||||
}
|
||||
|
||||
// Generate `__index`
|
||||
protect_lua!(state, 4, 1, fn(state) ffi::lua_call(state, 3, 1))?;
|
||||
}
|
||||
_ => mlua_panic!("improper __index type {}", index_type),
|
||||
}
|
||||
|
||||
rawset_field(state, -2, "__index")?;
|
||||
}
|
||||
|
||||
if let Some(field_setters) = field_setters {
|
||||
// Push `__newindex` generator function
|
||||
init_userdata_metatable_newindex(state)?;
|
||||
|
||||
push_string(state, b"__newindex", true)?;
|
||||
let newindex_type = ffi::lua_rawget(state, -3);
|
||||
match newindex_type {
|
||||
ffi::LUA_TNIL | ffi::LUA_TTABLE | ffi::LUA_TFUNCTION => {
|
||||
ffi::lua_pushvalue(state, field_setters);
|
||||
// Generate `__newindex`
|
||||
protect_lua!(state, 3, 1, fn(state) ffi::lua_call(state, 2, 1))?;
|
||||
}
|
||||
_ => mlua_panic!("improper __newindex type {}", newindex_type),
|
||||
}
|
||||
|
||||
rawset_field(state, -2, "__newindex")?;
|
||||
}
|
||||
|
||||
// Additional initialization
|
||||
if let Some(extra_init) = extra_init {
|
||||
extra_init(state)?;
|
||||
}
|
||||
|
||||
ffi::lua_pushboolean(state, 0);
|
||||
rawset_field(state, -2, "__metatable")?;
|
||||
|
||||
ffi::lua_pop(state, 1);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
unsafe extern "C-unwind" fn lua_error_impl(state: *mut ffi::lua_State) -> c_int {
|
||||
ffi::lua_error(state);
|
||||
}
|
||||
|
||||
unsafe extern "C-unwind" fn lua_isfunction_impl(state: *mut ffi::lua_State) -> c_int {
|
||||
ffi::lua_pushboolean(state, ffi::lua_isfunction(state, -1));
|
||||
1
|
||||
}
|
||||
|
||||
unsafe extern "C-unwind" fn lua_istable_impl(state: *mut ffi::lua_State) -> c_int {
|
||||
ffi::lua_pushboolean(state, ffi::lua_istable(state, -1));
|
||||
1
|
||||
}
|
||||
|
||||
unsafe fn init_userdata_metatable_index(state: *mut ffi::lua_State) -> Result<()> {
|
||||
let index_key = &USERDATA_METATABLE_INDEX as *const u8 as *const _;
|
||||
if ffi::lua_rawgetp(state, ffi::LUA_REGISTRYINDEX, index_key) == ffi::LUA_TFUNCTION {
|
||||
return Ok(());
|
||||
}
|
||||
ffi::lua_pop(state, 1);
|
||||
|
||||
// Create and cache `__index` generator
|
||||
let code = cstr!(
|
||||
r#"
|
||||
local error, isfunction, istable = ...
|
||||
return function (__index, field_getters, methods)
|
||||
-- Common case: has field getters and index is a table
|
||||
if field_getters ~= nil and methods == nil and istable(__index) then
|
||||
return function (self, key)
|
||||
local field_getter = field_getters[key]
|
||||
if field_getter ~= nil then
|
||||
return field_getter(self)
|
||||
end
|
||||
return __index[key]
|
||||
end
|
||||
end
|
||||
|
||||
return function (self, key)
|
||||
if field_getters ~= nil then
|
||||
local field_getter = field_getters[key]
|
||||
if field_getter ~= nil then
|
||||
return field_getter(self)
|
||||
end
|
||||
end
|
||||
|
||||
if methods ~= nil then
|
||||
local method = methods[key]
|
||||
if method ~= nil then
|
||||
return method
|
||||
end
|
||||
end
|
||||
|
||||
if isfunction(__index) then
|
||||
return __index(self, key)
|
||||
elseif __index == nil then
|
||||
error("attempt to get an unknown field '"..key.."'")
|
||||
else
|
||||
return __index[key]
|
||||
end
|
||||
end
|
||||
end
|
||||
"#
|
||||
);
|
||||
let code_len = CStr::from_ptr(code).to_bytes().len();
|
||||
protect_lua!(state, 0, 1, |state| {
|
||||
let ret = ffi::luaL_loadbuffer(state, code, code_len, cstr!("__mlua_index"));
|
||||
if ret != ffi::LUA_OK {
|
||||
ffi::lua_error(state);
|
||||
}
|
||||
ffi::lua_pushcfunction(state, lua_error_impl);
|
||||
ffi::lua_pushcfunction(state, lua_isfunction_impl);
|
||||
ffi::lua_pushcfunction(state, lua_istable_impl);
|
||||
ffi::lua_call(state, 3, 1);
|
||||
|
||||
#[cfg(feature = "luau-jit")]
|
||||
if ffi::luau_codegen_supported() != 0 {
|
||||
ffi::luau_codegen_compile(state, -1);
|
||||
}
|
||||
|
||||
// Store in the registry
|
||||
ffi::lua_pushvalue(state, -1);
|
||||
ffi::lua_rawsetp(state, ffi::LUA_REGISTRYINDEX, index_key);
|
||||
})
|
||||
}
|
||||
|
||||
unsafe fn init_userdata_metatable_newindex(state: *mut ffi::lua_State) -> Result<()> {
|
||||
let newindex_key = &USERDATA_METATABLE_NEWINDEX as *const u8 as *const _;
|
||||
if ffi::lua_rawgetp(state, ffi::LUA_REGISTRYINDEX, newindex_key) == ffi::LUA_TFUNCTION {
|
||||
return Ok(());
|
||||
}
|
||||
ffi::lua_pop(state, 1);
|
||||
|
||||
// Create and cache `__newindex` generator
|
||||
let code = cstr!(
|
||||
r#"
|
||||
local error, isfunction = ...
|
||||
return function (__newindex, field_setters)
|
||||
return function (self, key, value)
|
||||
if field_setters ~= nil then
|
||||
local field_setter = field_setters[key]
|
||||
if field_setter ~= nil then
|
||||
field_setter(self, value)
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
if isfunction(__newindex) then
|
||||
__newindex(self, key, value)
|
||||
elseif __newindex == nil then
|
||||
error("attempt to set an unknown field '"..key.."'")
|
||||
else
|
||||
__newindex[key] = value
|
||||
end
|
||||
end
|
||||
end
|
||||
"#
|
||||
);
|
||||
let code_len = CStr::from_ptr(code).to_bytes().len();
|
||||
protect_lua!(state, 0, 1, |state| {
|
||||
let ret = ffi::luaL_loadbuffer(state, code, code_len, cstr!("__mlua_newindex"));
|
||||
if ret != ffi::LUA_OK {
|
||||
ffi::lua_error(state);
|
||||
}
|
||||
ffi::lua_pushcfunction(state, lua_error_impl);
|
||||
ffi::lua_pushcfunction(state, lua_isfunction_impl);
|
||||
ffi::lua_call(state, 2, 1);
|
||||
|
||||
#[cfg(feature = "luau-jit")]
|
||||
if ffi::luau_codegen_supported() != 0 {
|
||||
ffi::luau_codegen_compile(state, -1);
|
||||
}
|
||||
|
||||
// Store in the registry
|
||||
ffi::lua_pushvalue(state, -1);
|
||||
ffi::lua_rawsetp(state, ffi::LUA_REGISTRYINDEX, newindex_key);
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "luau"))]
|
||||
pub(crate) unsafe extern "C-unwind" fn userdata_destructor<T>(state: *mut ffi::lua_State) -> c_int {
|
||||
// It's probably NOT a good idea to catch Rust panics in finalizer
|
||||
// Lua 5.4 ignores it, other versions generates `LUA_ERRGCMM` without calling message handler
|
||||
take_userdata::<T>(state);
|
||||
0
|
||||
}
|
||||
|
||||
pub(crate) static DESTRUCTED_USERDATA_METATABLE: u8 = 0;
|
||||
static USERDATA_METATABLE_INDEX: u8 = 0;
|
||||
static USERDATA_METATABLE_NEWINDEX: u8 = 0;
|
||||
+130
-229
@@ -1,38 +1,37 @@
|
||||
use std::borrow::Cow;
|
||||
use std::cell::RefCell;
|
||||
use std::cmp::Ordering;
|
||||
use std::collections::HashSet;
|
||||
use std::iter;
|
||||
use std::ops::Index;
|
||||
use std::collections::{vec_deque, HashSet, VecDeque};
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use std::os::raw::{c_int, c_void};
|
||||
use std::string::String as StdString;
|
||||
use std::sync::Arc;
|
||||
use std::{fmt, mem, ptr, slice, str, vec};
|
||||
use std::{fmt, mem, ptr, str};
|
||||
|
||||
use num_traits::FromPrimitive;
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
use {
|
||||
crate::table::SerializableTable,
|
||||
rustc_hash::FxHashSet,
|
||||
serde::ser::{self, Serialize, Serializer},
|
||||
std::{cell::RefCell, rc::Rc, result::Result as StdResult},
|
||||
};
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
use crate::function::Function;
|
||||
use crate::lua::Lua;
|
||||
use crate::string::String;
|
||||
use crate::state::{Lua, RawLua};
|
||||
use crate::string::{BorrowedStr, String};
|
||||
use crate::table::Table;
|
||||
use crate::thread::Thread;
|
||||
use crate::types::{Integer, LightUserData, Number, SubtypeId};
|
||||
use crate::userdata::AnyUserData;
|
||||
use crate::util::{check_stack, StackGuard};
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
use {
|
||||
crate::table::SerializableTable,
|
||||
rustc_hash::FxHashSet,
|
||||
serde::ser::{self, Serialize, Serializer},
|
||||
std::{rc::Rc, result::Result as StdResult},
|
||||
};
|
||||
|
||||
/// A dynamically typed Lua value. The `String`, `Table`, `Function`, `Thread`, and `UserData`
|
||||
/// variants contain handle types into the internal Lua state. It is a logic error to mix handle
|
||||
/// types between separate `Lua` instances, and doing so will result in a panic.
|
||||
#[derive(Clone)]
|
||||
pub enum Value<'lua> {
|
||||
pub enum Value {
|
||||
/// The Lua value `nil`.
|
||||
Nil,
|
||||
/// The Lua value `true` or `false`.
|
||||
@@ -52,27 +51,27 @@ pub enum Value<'lua> {
|
||||
/// An interned string, managed by Lua.
|
||||
///
|
||||
/// Unlike Rust strings, Lua strings may not be valid UTF-8.
|
||||
String(String<'lua>),
|
||||
String(String),
|
||||
/// Reference to a Lua table.
|
||||
Table(Table<'lua>),
|
||||
Table(Table),
|
||||
/// Reference to a Lua function (or closure).
|
||||
Function(Function<'lua>),
|
||||
Function(Function),
|
||||
/// Reference to a Lua thread (or coroutine).
|
||||
Thread(Thread<'lua>),
|
||||
Thread(Thread),
|
||||
/// Reference to a userdata object that holds a custom type which implements `UserData`.
|
||||
/// Special builtin userdata types will be represented as other `Value` variants.
|
||||
UserData(AnyUserData<'lua>),
|
||||
UserData(AnyUserData),
|
||||
/// `Error` is a special builtin userdata type. When received from Lua it is implicitly cloned.
|
||||
Error(Error),
|
||||
Error(Box<Error>),
|
||||
}
|
||||
|
||||
pub use self::Value::Nil;
|
||||
|
||||
impl<'lua> Value<'lua> {
|
||||
impl Value {
|
||||
/// A special value (lightuserdata) to represent null value.
|
||||
///
|
||||
/// It can be used in Lua tables without downsides of `nil`.
|
||||
pub const NULL: Value<'static> = Value::LightUserData(LightUserData(ptr::null_mut()));
|
||||
pub const NULL: Value = Value::LightUserData(LightUserData(ptr::null_mut()));
|
||||
|
||||
/// Returns type name of this value.
|
||||
pub const fn type_name(&self) -> &'static str {
|
||||
@@ -117,8 +116,8 @@ impl<'lua> Value<'lua> {
|
||||
|
||||
/// Converts the value to a generic C pointer.
|
||||
///
|
||||
/// The value can be a userdata, a table, a thread, a string, or a function; otherwise it returns NULL.
|
||||
/// Different objects will give different pointers.
|
||||
/// The value can be a userdata, a table, a thread, a string, or a function; otherwise it
|
||||
/// returns NULL. Different objects will give different pointers.
|
||||
/// There is no way to convert the pointer back to its original value.
|
||||
///
|
||||
/// Typically this function is used only for hashing and debug information.
|
||||
@@ -137,7 +136,8 @@ impl<'lua> Value<'lua> {
|
||||
|
||||
/// Converts the value to a string.
|
||||
///
|
||||
/// If the value has a metatable with a `__tostring` method, then it will be called to get the result.
|
||||
/// If the value has a metatable with a `__tostring` method, then it will be called to get the
|
||||
/// result.
|
||||
pub fn to_string(&self) -> Result<StdString> {
|
||||
match self {
|
||||
Value::Nil => Ok("nil".to_string()),
|
||||
@@ -153,15 +153,16 @@ impl<'lua> Value<'lua> {
|
||||
| Value::Function(Function(r))
|
||||
| Value::Thread(Thread(r, ..))
|
||||
| Value::UserData(AnyUserData(r, ..)) => unsafe {
|
||||
let state = r.lua.state();
|
||||
let lua = r.lua.lock();
|
||||
let state = lua.state();
|
||||
let _guard = StackGuard::new(state);
|
||||
check_stack(state, 3)?;
|
||||
|
||||
r.lua.push_ref(r);
|
||||
lua.push_ref(r);
|
||||
protect_lua!(state, 1, 1, fn(state) {
|
||||
ffi::luaL_tolstring(state, -1, ptr::null_mut());
|
||||
})?;
|
||||
Ok(String(r.lua.pop_ref()).to_str()?.to_string())
|
||||
Ok(String(lua.pop_ref()).to_str()?.to_string())
|
||||
},
|
||||
Value::Error(err) => Ok(err.to_string()),
|
||||
}
|
||||
@@ -329,19 +330,20 @@ impl<'lua> Value<'lua> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Cast the value to [`str`].
|
||||
/// Cast the value to [`BorrowedStr`].
|
||||
///
|
||||
/// If the value is a Lua [`String`], try to convert it to [`str`] or return `None` otherwise.
|
||||
/// If the value is a Lua [`String`], try to convert it to [`BorrowedStr`] or return `None`
|
||||
/// otherwise.
|
||||
#[inline]
|
||||
pub fn as_str(&self) -> Option<&str> {
|
||||
pub fn as_str(&self) -> Option<BorrowedStr> {
|
||||
self.as_string().and_then(|s| s.to_str().ok())
|
||||
}
|
||||
|
||||
/// Cast the value to [`Cow<str>`].
|
||||
/// Cast the value to [`StdString`].
|
||||
///
|
||||
/// If the value is a Lua [`String`], converts it to [`Cow<str>`] or returns `None` otherwise.
|
||||
/// If the value is a Lua [`String`], converts it to [`StdString`] or returns `None` otherwise.
|
||||
#[inline]
|
||||
pub fn as_string_lossy(&self) -> Option<Cow<str>> {
|
||||
pub fn as_string_lossy(&self) -> Option<StdString> {
|
||||
self.as_string().map(|s| s.to_string_lossy())
|
||||
}
|
||||
|
||||
@@ -441,7 +443,7 @@ impl<'lua> Value<'lua> {
|
||||
#[cfg(feature = "serialize")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "serialize")))]
|
||||
#[doc(hidden)]
|
||||
pub fn to_serializable(&self) -> SerializableValue<'_, 'lua> {
|
||||
pub fn to_serializable(&self) -> SerializableValue {
|
||||
SerializableValue::new(self, Default::default(), None)
|
||||
}
|
||||
|
||||
@@ -477,7 +479,7 @@ impl<'lua> Value<'lua> {
|
||||
(Value::Integer(_) | Value::Number(_), _) => Ordering::Less,
|
||||
(_, Value::Integer(_) | Value::Number(_)) => Ordering::Greater,
|
||||
// String
|
||||
(Value::String(a), Value::String(b)) => a.as_bytes().cmp(b.as_bytes()),
|
||||
(Value::String(a), Value::String(b)) => a.as_bytes().cmp(&b.as_bytes()),
|
||||
(Value::String(_), _) => Ordering::Less,
|
||||
(_, Value::String(_)) => Ordering::Greater,
|
||||
// Other variants can be randomly ordered
|
||||
@@ -523,7 +525,7 @@ impl<'lua> Value<'lua> {
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Value<'_> {
|
||||
impl fmt::Debug for Value {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
if fmt.alternate() {
|
||||
return self.fmt_pretty(fmt, true, 0, &mut HashSet::new());
|
||||
@@ -546,7 +548,7 @@ impl fmt::Debug for Value<'_> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> PartialEq for Value<'lua> {
|
||||
impl PartialEq for Value {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
match (self, other) {
|
||||
(Value::Nil, Value::Nil) => true,
|
||||
@@ -568,7 +570,7 @@ impl<'lua> PartialEq for Value<'lua> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> AsRef<Value<'lua>> for Value<'lua> {
|
||||
impl AsRef<Value> for Value {
|
||||
#[inline]
|
||||
fn as_ref(&self) -> &Self {
|
||||
self
|
||||
@@ -578,15 +580,15 @@ impl<'lua> AsRef<Value<'lua>> for Value<'lua> {
|
||||
/// A wrapped [`Value`] with customized serialization behavior.
|
||||
#[cfg(feature = "serialize")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "serialize")))]
|
||||
pub struct SerializableValue<'a, 'lua> {
|
||||
value: &'a Value<'lua>,
|
||||
pub struct SerializableValue<'a> {
|
||||
value: &'a Value,
|
||||
options: crate::serde::de::Options,
|
||||
// In many cases we don't need `visited` map, so don't allocate memory by default
|
||||
visited: Option<Rc<RefCell<FxHashSet<*const c_void>>>>,
|
||||
}
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
impl<'lua> Serialize for Value<'lua> {
|
||||
impl Serialize for Value {
|
||||
#[inline]
|
||||
fn serialize<S: Serializer>(&self, serializer: S) -> StdResult<S::Ok, S::Error> {
|
||||
SerializableValue::new(self, Default::default(), None).serialize(serializer)
|
||||
@@ -594,10 +596,10 @@ impl<'lua> Serialize for Value<'lua> {
|
||||
}
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
impl<'a, 'lua> SerializableValue<'a, 'lua> {
|
||||
impl<'a> SerializableValue<'a> {
|
||||
#[inline]
|
||||
pub(crate) fn new(
|
||||
value: &'a Value<'lua>,
|
||||
value: &'a Value,
|
||||
options: crate::serde::de::Options,
|
||||
visited: Option<&Rc<RefCell<FxHashSet<*const c_void>>>>,
|
||||
) -> Self {
|
||||
@@ -649,7 +651,7 @@ impl<'a, 'lua> SerializableValue<'a, 'lua> {
|
||||
}
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
impl<'a, 'lua> Serialize for SerializableValue<'a, 'lua> {
|
||||
impl<'a> Serialize for SerializableValue<'a> {
|
||||
fn serialize<S>(&self, serializer: S) -> StdResult<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
@@ -688,9 +690,9 @@ impl<'a, 'lua> Serialize for SerializableValue<'a, 'lua> {
|
||||
}
|
||||
|
||||
/// Trait for types convertible to `Value`.
|
||||
pub trait IntoLua<'lua>: Sized {
|
||||
pub trait IntoLua: Sized {
|
||||
/// Performs the conversion.
|
||||
fn into_lua(self, lua: &'lua Lua) -> Result<Value<'lua>>;
|
||||
fn into_lua(self, lua: &Lua) -> Result<Value>;
|
||||
|
||||
/// Pushes the value into the Lua stack.
|
||||
///
|
||||
@@ -698,15 +700,15 @@ pub trait IntoLua<'lua>: Sized {
|
||||
/// This method does not check Lua stack space.
|
||||
#[doc(hidden)]
|
||||
#[inline]
|
||||
unsafe fn push_into_stack(self, lua: &'lua Lua) -> Result<()> {
|
||||
lua.push_value(self.into_lua(lua)?)
|
||||
unsafe fn push_into_stack(self, lua: &RawLua) -> Result<()> {
|
||||
lua.push_value(&self.into_lua(lua.lua())?)
|
||||
}
|
||||
}
|
||||
|
||||
/// Trait for types convertible from `Value`.
|
||||
pub trait FromLua<'lua>: Sized {
|
||||
pub trait FromLua: Sized {
|
||||
/// Performs the conversion.
|
||||
fn from_lua(value: Value<'lua>, lua: &'lua Lua) -> Result<Self>;
|
||||
fn from_lua(value: Value, lua: &Lua) -> Result<Self>;
|
||||
|
||||
/// Performs the conversion for an argument (eg. function argument).
|
||||
///
|
||||
@@ -714,7 +716,7 @@ pub trait FromLua<'lua>: Sized {
|
||||
/// `to` is a function name that received the argument.
|
||||
#[doc(hidden)]
|
||||
#[inline]
|
||||
fn from_lua_arg(arg: Value<'lua>, i: usize, to: Option<&str>, lua: &'lua Lua) -> Result<Self> {
|
||||
fn from_lua_arg(arg: Value, i: usize, to: Option<&str>, lua: &Lua) -> Result<Self> {
|
||||
Self::from_lua(arg, lua).map_err(|err| Error::BadArgument {
|
||||
to: to.map(|s| s.to_string()),
|
||||
pos: i,
|
||||
@@ -726,19 +728,14 @@ pub trait FromLua<'lua>: Sized {
|
||||
/// Performs the conversion for a value in the Lua stack at index `idx`.
|
||||
#[doc(hidden)]
|
||||
#[inline]
|
||||
unsafe fn from_stack(idx: c_int, lua: &'lua Lua) -> Result<Self> {
|
||||
Self::from_lua(lua.stack_value(idx), lua)
|
||||
unsafe fn from_stack(idx: c_int, lua: &RawLua) -> Result<Self> {
|
||||
Self::from_lua(lua.stack_value(idx, None), lua.lua())
|
||||
}
|
||||
|
||||
/// Same as `from_lua_arg` but for a value in the Lua stack at index `idx`.
|
||||
#[doc(hidden)]
|
||||
#[inline]
|
||||
unsafe fn from_stack_arg(
|
||||
idx: c_int,
|
||||
i: usize,
|
||||
to: Option<&str>,
|
||||
lua: &'lua Lua,
|
||||
) -> Result<Self> {
|
||||
unsafe fn from_stack_arg(idx: c_int, i: usize, to: Option<&str>, lua: &RawLua) -> Result<Self> {
|
||||
Self::from_stack(idx, lua).map_err(|err| Error::BadArgument {
|
||||
to: to.map(|s| s.to_string()),
|
||||
pos: i,
|
||||
@@ -749,192 +746,99 @@ pub trait FromLua<'lua>: Sized {
|
||||
}
|
||||
|
||||
/// Multiple Lua values used for both argument passing and also for multiple return values.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct MultiValue<'lua> {
|
||||
vec: Vec<Value<'lua>>,
|
||||
lua: Option<&'lua Lua>,
|
||||
}
|
||||
#[derive(Default, Debug, Clone)]
|
||||
pub struct MultiValue(VecDeque<Value>);
|
||||
|
||||
impl Drop for MultiValue<'_> {
|
||||
fn drop(&mut self) {
|
||||
if let Some(lua) = self.lua {
|
||||
let vec = mem::take(&mut self.vec);
|
||||
lua.push_multivalue_to_pool(vec);
|
||||
}
|
||||
impl Deref for MultiValue {
|
||||
type Target = VecDeque<Value>;
|
||||
|
||||
#[inline]
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> MultiValue<'lua> {
|
||||
impl DerefMut for MultiValue {
|
||||
#[inline]
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl MultiValue {
|
||||
/// Creates an empty `MultiValue` containing no values.
|
||||
pub const fn new() -> MultiValue<'lua> {
|
||||
MultiValue {
|
||||
vec: Vec::new(),
|
||||
lua: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Similar to `new` but can reuse previously used container with allocated capacity.
|
||||
#[inline]
|
||||
pub(crate) fn with_lua_and_capacity(lua: &'lua Lua, capacity: usize) -> MultiValue<'lua> {
|
||||
let vec = lua
|
||||
.pop_multivalue_from_pool()
|
||||
.map(|mut vec| {
|
||||
vec.reserve(capacity);
|
||||
vec
|
||||
})
|
||||
.unwrap_or_else(|| Vec::with_capacity(capacity));
|
||||
MultiValue {
|
||||
vec,
|
||||
lua: Some(lua),
|
||||
#[inline]
|
||||
pub const fn new() -> MultiValue {
|
||||
MultiValue(VecDeque::new())
|
||||
}
|
||||
|
||||
/// Creates an empty `MultiValue` container with space for at least `capacity` elements.
|
||||
pub fn with_capacity(capacity: usize) -> MultiValue {
|
||||
MultiValue(VecDeque::with_capacity(capacity))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn from_lua_iter<T: IntoLua>(lua: &Lua, iter: impl IntoIterator<Item = T>) -> Result<Self> {
|
||||
let iter = iter.into_iter();
|
||||
let mut multi_value = MultiValue::with_capacity(iter.size_hint().0);
|
||||
for value in iter {
|
||||
multi_value.push_back(value.into_lua(lua)?);
|
||||
}
|
||||
Ok(multi_value)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> Default for MultiValue<'lua> {
|
||||
impl FromIterator<Value> for MultiValue {
|
||||
#[inline]
|
||||
fn default() -> MultiValue<'lua> {
|
||||
MultiValue::new()
|
||||
fn from_iter<I: IntoIterator<Item = Value>>(iter: I) -> Self {
|
||||
let mut multi_value = MultiValue::new();
|
||||
multi_value.extend(iter);
|
||||
multi_value
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> FromIterator<Value<'lua>> for MultiValue<'lua> {
|
||||
#[inline]
|
||||
fn from_iter<I: IntoIterator<Item = Value<'lua>>>(iter: I) -> Self {
|
||||
MultiValue::from_vec(Vec::from_iter(iter))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> IntoIterator for MultiValue<'lua> {
|
||||
type Item = Value<'lua>;
|
||||
type IntoIter = iter::Rev<vec::IntoIter<Value<'lua>>>;
|
||||
impl IntoIterator for MultiValue {
|
||||
type Item = Value;
|
||||
type IntoIter = vec_deque::IntoIter<Value>;
|
||||
|
||||
#[inline]
|
||||
fn into_iter(mut self) -> Self::IntoIter {
|
||||
let vec = mem::take(&mut self.vec);
|
||||
let deque = mem::take(&mut self.0);
|
||||
mem::forget(self);
|
||||
vec.into_iter().rev()
|
||||
deque.into_iter()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'lua> IntoIterator for &'a MultiValue<'lua> {
|
||||
type Item = &'a Value<'lua>;
|
||||
type IntoIter = iter::Rev<slice::Iter<'a, Value<'lua>>>;
|
||||
impl<'a> IntoIterator for &'a MultiValue {
|
||||
type Item = &'a Value;
|
||||
type IntoIter = vec_deque::Iter<'a, Value>;
|
||||
|
||||
#[inline]
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.vec.iter().rev()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> Index<usize> for MultiValue<'lua> {
|
||||
type Output = Value<'lua>;
|
||||
|
||||
#[inline]
|
||||
fn index(&self, index: usize) -> &Self::Output {
|
||||
if let Some(result) = self.get(index) {
|
||||
result
|
||||
} else {
|
||||
panic!(
|
||||
"index out of bounds: the len is {} but the index is {}",
|
||||
self.len(),
|
||||
index
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> MultiValue<'lua> {
|
||||
#[inline]
|
||||
pub fn from_vec(mut vec: Vec<Value<'lua>>) -> MultiValue<'lua> {
|
||||
vec.reverse();
|
||||
MultiValue { vec, lua: None }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn into_vec(mut self) -> Vec<Value<'lua>> {
|
||||
let mut vec = mem::take(&mut self.vec);
|
||||
mem::forget(self);
|
||||
vec.reverse();
|
||||
vec
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get(&self, index: usize) -> Option<&Value<'lua>> {
|
||||
if index < self.vec.len() {
|
||||
return self.vec.get(self.vec.len() - index - 1);
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn pop_front(&mut self) -> Option<Value<'lua>> {
|
||||
self.vec.pop()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn push_front(&mut self, value: Value<'lua>) {
|
||||
self.vec.push(value);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn clear(&mut self) {
|
||||
self.vec.clear();
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn len(&self) -> usize {
|
||||
self.vec.len()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.vec.is_empty()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn iter(&self) -> iter::Rev<slice::Iter<Value<'lua>>> {
|
||||
self.vec.iter().rev()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn drain_all(&mut self) -> iter::Rev<vec::Drain<Value<'lua>>> {
|
||||
self.vec.drain(..).rev()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn refill(
|
||||
&mut self,
|
||||
iter: impl IntoIterator<Item = Result<Value<'lua>>>,
|
||||
) -> Result<()> {
|
||||
self.vec.clear();
|
||||
for value in iter {
|
||||
self.vec.push(value?);
|
||||
}
|
||||
self.vec.reverse();
|
||||
Ok(())
|
||||
self.0.iter()
|
||||
}
|
||||
}
|
||||
|
||||
/// Trait for types convertible to any number of Lua values.
|
||||
///
|
||||
/// This is a generalization of `IntoLua`, allowing any number of resulting Lua values instead of just
|
||||
/// one. Any type that implements `IntoLua` will automatically implement this trait.
|
||||
pub trait IntoLuaMulti<'lua>: Sized {
|
||||
/// This is a generalization of `IntoLua`, allowing any number of resulting Lua values instead of
|
||||
/// just one. Any type that implements `IntoLua` will automatically implement this trait.
|
||||
pub trait IntoLuaMulti: Sized {
|
||||
/// Performs the conversion.
|
||||
fn into_lua_multi(self, lua: &'lua Lua) -> Result<MultiValue<'lua>>;
|
||||
fn into_lua_multi(self, lua: &Lua) -> Result<MultiValue>;
|
||||
|
||||
/// Pushes the values into the Lua stack.
|
||||
///
|
||||
/// Returns number of pushed values.
|
||||
#[doc(hidden)]
|
||||
#[inline]
|
||||
unsafe fn push_into_stack_multi(self, lua: &'lua Lua) -> Result<c_int> {
|
||||
let mut values = self.into_lua_multi(lua)?;
|
||||
unsafe fn push_into_stack_multi(self, lua: &RawLua) -> Result<c_int> {
|
||||
let values = self.into_lua_multi(lua.lua())?;
|
||||
let len: c_int = values.len().try_into().unwrap();
|
||||
unsafe {
|
||||
check_stack(lua.state(), len + 1)?;
|
||||
for v in values.drain_all() {
|
||||
lua.push_value(v)?;
|
||||
for val in &values {
|
||||
lua.push_value(val)?;
|
||||
}
|
||||
}
|
||||
Ok(len)
|
||||
@@ -945,14 +849,14 @@ pub trait IntoLuaMulti<'lua>: Sized {
|
||||
///
|
||||
/// This is a generalization of `FromLua`, allowing an arbitrary number of Lua values to participate
|
||||
/// in the conversion. Any type that implements `FromLua` will automatically implement this trait.
|
||||
pub trait FromLuaMulti<'lua>: Sized {
|
||||
pub trait FromLuaMulti: Sized {
|
||||
/// Performs the conversion.
|
||||
///
|
||||
/// In case `values` contains more values than needed to perform the conversion, the excess
|
||||
/// values should be ignored. This reflects the semantics of Lua when calling a function or
|
||||
/// assigning values. Similarly, if not enough values are given, conversions should assume that
|
||||
/// any missing values are nil.
|
||||
fn from_lua_multi(values: MultiValue<'lua>, lua: &'lua Lua) -> Result<Self>;
|
||||
fn from_lua_multi(values: MultiValue, lua: &Lua) -> Result<Self>;
|
||||
|
||||
/// Performs the conversion for a list of arguments.
|
||||
///
|
||||
@@ -960,12 +864,7 @@ pub trait FromLuaMulti<'lua>: Sized {
|
||||
/// `to` is a function name that received the arguments.
|
||||
#[doc(hidden)]
|
||||
#[inline]
|
||||
fn from_lua_args(
|
||||
args: MultiValue<'lua>,
|
||||
i: usize,
|
||||
to: Option<&str>,
|
||||
lua: &'lua Lua,
|
||||
) -> Result<Self> {
|
||||
fn from_lua_args(args: MultiValue, i: usize, to: Option<&str>, lua: &Lua) -> Result<Self> {
|
||||
let _ = (i, to);
|
||||
Self::from_lua_multi(args, lua)
|
||||
}
|
||||
@@ -973,27 +872,22 @@ pub trait FromLuaMulti<'lua>: Sized {
|
||||
/// Performs the conversion for a number of values in the Lua stack.
|
||||
#[doc(hidden)]
|
||||
#[inline]
|
||||
unsafe fn from_stack_multi(nvals: c_int, lua: &'lua Lua) -> Result<Self> {
|
||||
let mut values = MultiValue::with_lua_and_capacity(lua, nvals as usize);
|
||||
for idx in 1..=nvals {
|
||||
values.push_front(lua.stack_value(-idx));
|
||||
unsafe fn from_stack_multi(nvals: c_int, lua: &RawLua) -> Result<Self> {
|
||||
let mut values = MultiValue::with_capacity(nvals as usize);
|
||||
for idx in 0..nvals {
|
||||
values.push_back(lua.stack_value(-nvals + idx, None));
|
||||
}
|
||||
if nvals > 0 {
|
||||
// It's safe to clear the stack as all references moved to ref thread
|
||||
ffi::lua_pop(lua.state(), nvals);
|
||||
}
|
||||
Self::from_lua_multi(values, lua)
|
||||
Self::from_lua_multi(values, lua.lua())
|
||||
}
|
||||
|
||||
/// Same as `from_lua_args` but for a number of values in the Lua stack.
|
||||
#[doc(hidden)]
|
||||
#[inline]
|
||||
unsafe fn from_stack_args(
|
||||
nargs: c_int,
|
||||
i: usize,
|
||||
to: Option<&str>,
|
||||
lua: &'lua Lua,
|
||||
) -> Result<Self> {
|
||||
unsafe fn from_stack_args(nargs: c_int, i: usize, to: Option<&str>, lua: &RawLua) -> Result<Self> {
|
||||
let _ = (i, to);
|
||||
Self::from_stack_multi(nargs, lua)
|
||||
}
|
||||
@@ -1003,6 +897,13 @@ pub trait FromLuaMulti<'lua>: Sized {
|
||||
mod assertions {
|
||||
use super::*;
|
||||
|
||||
#[cfg(not(feature = "send"))]
|
||||
static_assertions::assert_not_impl_any!(Value: Send);
|
||||
#[cfg(not(feature = "send"))]
|
||||
static_assertions::assert_not_impl_any!(MultiValue: Send);
|
||||
|
||||
#[cfg(feature = "send")]
|
||||
static_assertions::assert_impl_all!(Value: Send, Sync);
|
||||
#[cfg(feature = "send")]
|
||||
static_assertions::assert_impl_all!(MultiValue: Send, Sync);
|
||||
}
|
||||
|
||||
+3
-3
@@ -1,8 +1,8 @@
|
||||
[lua54_coverage]
|
||||
features = "lua54,vendored,async,serialize,macros,unstable"
|
||||
features = "lua54,vendored,async,serialize,macros"
|
||||
|
||||
[lua51_coverage]
|
||||
features = "lua51,vendored,async,serialize,macros,unstable"
|
||||
features = "lua51,vendored,async,serialize,macros"
|
||||
|
||||
[luau_coverage]
|
||||
features = "luau,async,serialize,macros,unstable"
|
||||
features = "luau,async,serialize,macros"
|
||||
|
||||
+66
-92
@@ -1,13 +1,14 @@
|
||||
#![cfg(feature = "async")]
|
||||
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
|
||||
use futures_util::stream::TryStreamExt;
|
||||
use tokio::sync::Mutex;
|
||||
|
||||
use mlua::{
|
||||
AnyUserDataExt, Error, Function, Lua, LuaOptions, MultiValue, Result, StdLib, Table, TableExt,
|
||||
UserData, UserDataMethods, Value,
|
||||
Error, Function, Lua, LuaOptions, MultiValue, ObjectLike, Result, StdLib, Table, UserData,
|
||||
UserDataMethods, Value,
|
||||
};
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
@@ -25,8 +26,7 @@ async fn sleep_ms(_ms: u64) {
|
||||
async fn test_async_function() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let f = lua
|
||||
.create_async_function(|_lua, (a, b, c): (i64, i64, i64)| async move { Ok((a + b) * c) })?;
|
||||
let f = lua.create_async_function(|_lua, (a, b, c): (i64, i64, i64)| async move { Ok((a + b) * c) })?;
|
||||
lua.globals().set("f", f)?;
|
||||
|
||||
let res: i64 = lua.load("f(1, 2, 3)").eval_async().await?;
|
||||
@@ -73,18 +73,16 @@ async fn test_async_call() -> Result<()> {
|
||||
Ok(format!("hello, {}!", name))
|
||||
})?;
|
||||
|
||||
match hello.call::<_, ()>("alex") {
|
||||
match hello.call::<()>("alex") {
|
||||
Err(Error::RuntimeError(_)) => {}
|
||||
_ => panic!(
|
||||
"non-async executing async function must fail on the yield stage with RuntimeError"
|
||||
),
|
||||
err => panic!("expected `RuntimeError`, got {err:?}"),
|
||||
};
|
||||
|
||||
assert_eq!(hello.call_async::<_, String>("alex").await?, "hello, alex!");
|
||||
assert_eq!(hello.call_async::<String>("alex").await?, "hello, alex!");
|
||||
|
||||
// Executing non-async functions using async call is allowed
|
||||
let sum = lua.create_function(|_lua, (a, b): (i64, i64)| return Ok(a + b))?;
|
||||
assert_eq!(sum.call_async::<_, i64>((5, 1)).await?, 6);
|
||||
assert_eq!(sum.call_async::<i64>((5, 1)).await?, 6);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -98,7 +96,7 @@ async fn test_async_call_many_returns() -> Result<()> {
|
||||
Ok(("a", "b", "c", 1))
|
||||
})?;
|
||||
|
||||
let vals = hello.call_async::<_, MultiValue>(()).await?;
|
||||
let vals = hello.call_async::<MultiValue>(()).await?;
|
||||
assert_eq!(vals.len(), 4);
|
||||
assert_eq!(vals[0].to_string()?, "a");
|
||||
assert_eq!(vals[1].to_string()?, "b");
|
||||
@@ -161,7 +159,7 @@ async fn test_async_handle_yield() -> Result<()> {
|
||||
"#,
|
||||
)
|
||||
.eval::<Function>()?;
|
||||
assert_eq!(min.call_async::<_, i64>((-1, 1)).await?, -1);
|
||||
assert_eq!(min.call_async::<i64>((-1, 1)).await?, -1);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -230,15 +228,15 @@ async fn test_async_lua54_to_be_closed() -> Result<()> {
|
||||
let f = lua.load(code).into_function()?;
|
||||
|
||||
// Test close using call_async
|
||||
let _ = f.call_async::<_, ()>(()).await;
|
||||
assert_eq!(globals.get::<_, usize>("close_count")?, 1);
|
||||
let _ = f.call_async::<()>(()).await;
|
||||
assert_eq!(globals.get::<usize>("close_count")?, 1);
|
||||
|
||||
// Don't close by default when awaiting async threads
|
||||
let co = lua.create_thread(f.clone())?;
|
||||
let _ = co.clone().into_async::<_, ()>(()).await;
|
||||
assert_eq!(globals.get::<_, usize>("close_count")?, 1);
|
||||
let _ = co.clone().into_async::<()>(()).await;
|
||||
assert_eq!(globals.get::<usize>("close_count")?, 1);
|
||||
let _ = co.reset(f);
|
||||
assert_eq!(globals.get::<_, usize>("close_count")?, 2);
|
||||
assert_eq!(globals.get::<usize>("close_count")?, 2);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -262,7 +260,7 @@ async fn test_async_thread_stream() -> Result<()> {
|
||||
.eval()?,
|
||||
)?;
|
||||
|
||||
let mut stream = thread.into_async::<_, i64>(1);
|
||||
let mut stream = thread.into_async::<i64>(1);
|
||||
let mut sum = 0;
|
||||
while let Some(n) = stream.try_next().await? {
|
||||
sum += n;
|
||||
@@ -310,14 +308,14 @@ fn test_async_thread_capture() -> Result<()> {
|
||||
|
||||
let thread = lua.create_thread(f)?;
|
||||
// After first resume, `v: Value` is captured in the coroutine
|
||||
thread.resume::<_, ()>("abc").unwrap();
|
||||
thread.resume::<()>("abc").unwrap();
|
||||
drop(thread);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_async_table() -> Result<()> {
|
||||
async fn test_async_table_object_like() -> Result<()> {
|
||||
let options = LuaOptions::new().thread_pool_size(4);
|
||||
let lua = Lua::new_with(StdLib::ALL_SAFE, options)?;
|
||||
|
||||
@@ -326,7 +324,7 @@ async fn test_async_table() -> Result<()> {
|
||||
|
||||
let get_value = lua.create_async_function(|_, table: Table| async move {
|
||||
sleep_ms(10).await;
|
||||
table.get::<_, i64>("val")
|
||||
table.get::<i64>("val")
|
||||
})?;
|
||||
table.set("get_value", get_value)?;
|
||||
|
||||
@@ -336,25 +334,20 @@ async fn test_async_table() -> Result<()> {
|
||||
})?;
|
||||
table.set("set_value", set_value)?;
|
||||
|
||||
let sleep = lua.create_async_function(|_, n| async move {
|
||||
sleep_ms(n).await;
|
||||
Ok(format!("elapsed:{}ms", n))
|
||||
})?;
|
||||
table.set("sleep", sleep)?;
|
||||
assert_eq!(table.call_async_method::<i64>("get_value", ()).await?, 10);
|
||||
table.call_async_method::<()>("set_value", 15).await?;
|
||||
assert_eq!(table.call_async_method::<i64>("get_value", ()).await?, 15);
|
||||
|
||||
assert_eq!(
|
||||
table.call_async_method::<_, i64>("get_value", ()).await?,
|
||||
10
|
||||
);
|
||||
table.call_async_method("set_value", 15).await?;
|
||||
assert_eq!(
|
||||
table.call_async_method::<_, i64>("get_value", ()).await?,
|
||||
15
|
||||
);
|
||||
assert_eq!(
|
||||
table.call_async_function::<_, String>("sleep", 7).await?,
|
||||
"elapsed:7ms"
|
||||
);
|
||||
let metatable = lua.create_table()?;
|
||||
metatable.set(
|
||||
"__call",
|
||||
lua.create_async_function(|_, table: Table| async move {
|
||||
sleep_ms(10).await;
|
||||
table.get::<i64>("val")
|
||||
})?,
|
||||
)?;
|
||||
table.set_metatable(Some(metatable));
|
||||
assert_eq!(table.call_async::<i64>(()).await.unwrap(), 15);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -374,9 +367,9 @@ async fn test_async_thread_pool() -> Result<()> {
|
||||
Ok(format!("elapsed:{}ms", n))
|
||||
})?;
|
||||
|
||||
assert!(error_f.call_async::<_, ()>(()).await.is_err());
|
||||
assert!(error_f.call_async::<()>(()).await.is_err());
|
||||
// Next call should use cached thread
|
||||
assert_eq!(sleep.call_async::<_, String>(3).await?, "elapsed:3ms");
|
||||
assert_eq!(sleep.call_async::<String>(3).await?, "elapsed:3ms");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -386,13 +379,13 @@ async fn test_async_userdata() -> Result<()> {
|
||||
struct MyUserData(u64);
|
||||
|
||||
impl UserData for MyUserData {
|
||||
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
|
||||
fn add_methods<M: UserDataMethods<Self>>(methods: &mut M) {
|
||||
methods.add_async_method("get_value", |_, data, ()| async move {
|
||||
sleep_ms(10).await;
|
||||
Ok(data.0)
|
||||
});
|
||||
|
||||
methods.add_async_method_mut("set_value", |_, data, n| async move {
|
||||
methods.add_async_method_mut("set_value", |_, mut data, n| async move {
|
||||
sleep_ms(10).await;
|
||||
data.0 = n;
|
||||
Ok(())
|
||||
@@ -411,22 +404,19 @@ async fn test_async_userdata() -> Result<()> {
|
||||
});
|
||||
|
||||
#[cfg(not(any(feature = "lua51", feature = "luau")))]
|
||||
methods.add_async_meta_method(
|
||||
mlua::MetaMethod::Index,
|
||||
|_, data, key: String| async move {
|
||||
sleep_ms(10).await;
|
||||
match key.as_str() {
|
||||
"ms" => Ok(Some(data.0 as f64)),
|
||||
"s" => Ok(Some((data.0 as f64) / 1000.0)),
|
||||
_ => Ok(None),
|
||||
}
|
||||
},
|
||||
);
|
||||
methods.add_async_meta_method(mlua::MetaMethod::Index, |_, data, key: String| async move {
|
||||
sleep_ms(10).await;
|
||||
match key.as_str() {
|
||||
"ms" => Ok(Some(data.0 as f64)),
|
||||
"s" => Ok(Some((data.0 as f64) / 1000.0)),
|
||||
_ => Ok(None),
|
||||
}
|
||||
});
|
||||
|
||||
#[cfg(not(any(feature = "lua51", feature = "luau")))]
|
||||
methods.add_async_meta_method_mut(
|
||||
mlua::MetaMethod::NewIndex,
|
||||
|_, data, (key, value): (String, f64)| async move {
|
||||
|_, mut data, (key, value): (String, f64)| async move {
|
||||
sleep_ms(10).await;
|
||||
match key.as_str() {
|
||||
"ms" => data.0 = value as u64,
|
||||
@@ -472,13 +462,14 @@ async fn test_async_userdata() -> Result<()> {
|
||||
.exec_async()
|
||||
.await?;
|
||||
|
||||
userdata.call_async_method("set_value", 24).await?;
|
||||
// ObjectLike methods
|
||||
userdata.call_async_method::<()>("set_value", 24).await?;
|
||||
let n: u64 = userdata.call_async_method("get_value", ()).await?;
|
||||
assert_eq!(n, 24);
|
||||
userdata.call_async_function("sleep", 15).await?;
|
||||
userdata.call_async_function::<()>("sleep", 15).await?;
|
||||
|
||||
#[cfg(not(any(feature = "lua51", feature = "luau")))]
|
||||
assert_eq!(userdata.call_async::<_, String>(()).await?, "elapsed:24ms");
|
||||
assert_eq!(userdata.call_async::<String>(()).await?, "elapsed:24ms");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -488,7 +479,7 @@ async fn test_async_thread_error() -> Result<()> {
|
||||
struct MyUserData;
|
||||
|
||||
impl UserData for MyUserData {
|
||||
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
|
||||
fn add_methods<M: UserDataMethods<Self>>(methods: &mut M) {
|
||||
methods.add_meta_method("__tostring", |_, _this, ()| Ok("myuserdata error"))
|
||||
}
|
||||
}
|
||||
@@ -497,7 +488,7 @@ async fn test_async_thread_error() -> Result<()> {
|
||||
let result = lua
|
||||
.load("function x(...) error(...) end x(...)")
|
||||
.set_name("chunk")
|
||||
.call_async::<_, ()>(MyUserData)
|
||||
.call_async::<()>(MyUserData)
|
||||
.await;
|
||||
assert!(
|
||||
matches!(result, Err(Error::RuntimeError(cause)) if cause.contains("myuserdata error")),
|
||||
@@ -507,41 +498,24 @@ async fn test_async_thread_error() -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "unstable", not(feature = "send")))]
|
||||
#[tokio::test]
|
||||
async fn test_owned_async_call() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let hello = lua
|
||||
.create_async_function(|_, name: String| async move {
|
||||
sleep_ms(10).await;
|
||||
Ok(format!("hello, {}!", name))
|
||||
})?
|
||||
.into_owned();
|
||||
drop(lua);
|
||||
|
||||
assert_eq!(hello.call_async::<_, String>("alex").await?, "hello, alex!");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_async_terminate() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let mutex = Arc::new(Mutex::new(0u32));
|
||||
let mutex2 = mutex.clone();
|
||||
let func = lua.create_async_function(move |_, ()| {
|
||||
let mutex = mutex2.clone();
|
||||
async move {
|
||||
let _guard = mutex.lock();
|
||||
sleep_ms(100).await;
|
||||
Ok(())
|
||||
}
|
||||
})?;
|
||||
{
|
||||
let lua = Lua::new();
|
||||
let mutex2 = mutex.clone();
|
||||
let func = lua.create_async_function(move |lua, ()| {
|
||||
let mutex = mutex2.clone();
|
||||
async move {
|
||||
let _guard = mutex.lock().await;
|
||||
sleep_ms(100).await;
|
||||
drop(lua); // Move Lua to the future to test drop
|
||||
Ok(())
|
||||
}
|
||||
})?;
|
||||
|
||||
let _ = tokio::time::timeout(Duration::from_millis(30), func.call_async::<_, ()>(())).await;
|
||||
lua.gc_collect()?;
|
||||
let _ = tokio::time::timeout(Duration::from_millis(30), func.call_async::<()>(())).await;
|
||||
}
|
||||
assert!(mutex.try_lock().is_ok());
|
||||
|
||||
Ok(())
|
||||
|
||||
+24
-24
@@ -22,38 +22,38 @@ fn test_byte_string_round_trip() -> Result<()> {
|
||||
|
||||
let globals = lua.globals();
|
||||
|
||||
let isi = globals.get::<_, BString>("invalid_sequence_identifier")?;
|
||||
let isi = globals.get::<BString>("invalid_sequence_identifier")?;
|
||||
assert_eq!(isi, [0xa0, 0xa1].as_ref());
|
||||
|
||||
let i2os2 = globals.get::<_, BString>("invalid_2_octet_sequence_2nd")?;
|
||||
let i2os2 = globals.get::<BString>("invalid_2_octet_sequence_2nd")?;
|
||||
assert_eq!(i2os2, [0xc3, 0x28].as_ref());
|
||||
|
||||
let i3os2 = globals.get::<_, BString>("invalid_3_octet_sequence_2nd")?;
|
||||
let i3os2 = globals.get::<BString>("invalid_3_octet_sequence_2nd")?;
|
||||
assert_eq!(i3os2, [0xe2, 0x28, 0xa1].as_ref());
|
||||
|
||||
let i3os3 = globals.get::<_, BString>("invalid_3_octet_sequence_3rd")?;
|
||||
let i3os3 = globals.get::<BString>("invalid_3_octet_sequence_3rd")?;
|
||||
assert_eq!(i3os3, [0xe2, 0x82, 0x28].as_ref());
|
||||
|
||||
let i4os2 = globals.get::<_, BString>("invalid_4_octet_sequence_2nd")?;
|
||||
let i4os2 = globals.get::<BString>("invalid_4_octet_sequence_2nd")?;
|
||||
assert_eq!(i4os2, [0xf0, 0x28, 0x8c, 0xbc].as_ref());
|
||||
|
||||
let i4os3 = globals.get::<_, BString>("invalid_4_octet_sequence_3rd")?;
|
||||
let i4os3 = globals.get::<BString>("invalid_4_octet_sequence_3rd")?;
|
||||
assert_eq!(i4os3, [0xf0, 0x90, 0x28, 0xbc].as_ref());
|
||||
|
||||
let i4os4 = globals.get::<_, BString>("invalid_4_octet_sequence_4th")?;
|
||||
let i4os4 = globals.get::<BString>("invalid_4_octet_sequence_4th")?;
|
||||
assert_eq!(i4os4, [0xf0, 0x28, 0x8c, 0x28].as_ref());
|
||||
|
||||
let aas = globals.get::<_, BString>("an_actual_string")?;
|
||||
let aas = globals.get::<BString>("an_actual_string")?;
|
||||
assert_eq!(aas, b"Hello, world!".as_ref());
|
||||
|
||||
globals.set::<_, &BStr>("bstr_invalid_sequence_identifier", isi.as_ref())?;
|
||||
globals.set::<_, &BStr>("bstr_invalid_2_octet_sequence_2nd", i2os2.as_ref())?;
|
||||
globals.set::<_, &BStr>("bstr_invalid_3_octet_sequence_2nd", i3os2.as_ref())?;
|
||||
globals.set::<_, &BStr>("bstr_invalid_3_octet_sequence_3rd", i3os3.as_ref())?;
|
||||
globals.set::<_, &BStr>("bstr_invalid_4_octet_sequence_2nd", i4os2.as_ref())?;
|
||||
globals.set::<_, &BStr>("bstr_invalid_4_octet_sequence_3rd", i4os3.as_ref())?;
|
||||
globals.set::<_, &BStr>("bstr_invalid_4_octet_sequence_4th", i4os4.as_ref())?;
|
||||
globals.set::<_, &BStr>("bstr_an_actual_string", aas.as_ref())?;
|
||||
globals.set("bstr_invalid_sequence_identifier", isi.as_ref() as &BStr)?;
|
||||
globals.set("bstr_invalid_2_octet_sequence_2nd", i2os2.as_ref() as &BStr)?;
|
||||
globals.set("bstr_invalid_3_octet_sequence_2nd", i3os2.as_ref() as &BStr)?;
|
||||
globals.set("bstr_invalid_3_octet_sequence_3rd", i3os3.as_ref() as &BStr)?;
|
||||
globals.set("bstr_invalid_4_octet_sequence_2nd", i4os2.as_ref() as &BStr)?;
|
||||
globals.set("bstr_invalid_4_octet_sequence_3rd", i4os3.as_ref() as &BStr)?;
|
||||
globals.set("bstr_invalid_4_octet_sequence_4th", i4os4.as_ref() as &BStr)?;
|
||||
globals.set("bstr_an_actual_string", aas.as_ref() as &BStr)?;
|
||||
|
||||
lua.load(
|
||||
r#"
|
||||
@@ -69,14 +69,14 @@ fn test_byte_string_round_trip() -> Result<()> {
|
||||
)
|
||||
.exec()?;
|
||||
|
||||
globals.set::<_, BString>("bstring_invalid_sequence_identifier", isi)?;
|
||||
globals.set::<_, BString>("bstring_invalid_2_octet_sequence_2nd", i2os2)?;
|
||||
globals.set::<_, BString>("bstring_invalid_3_octet_sequence_2nd", i3os2)?;
|
||||
globals.set::<_, BString>("bstring_invalid_3_octet_sequence_3rd", i3os3)?;
|
||||
globals.set::<_, BString>("bstring_invalid_4_octet_sequence_2nd", i4os2)?;
|
||||
globals.set::<_, BString>("bstring_invalid_4_octet_sequence_3rd", i4os3)?;
|
||||
globals.set::<_, BString>("bstring_invalid_4_octet_sequence_4th", i4os4)?;
|
||||
globals.set::<_, BString>("bstring_an_actual_string", aas)?;
|
||||
globals.set("bstring_invalid_sequence_identifier", isi)?;
|
||||
globals.set("bstring_invalid_2_octet_sequence_2nd", i2os2)?;
|
||||
globals.set("bstring_invalid_3_octet_sequence_2nd", i3os2)?;
|
||||
globals.set("bstring_invalid_3_octet_sequence_3rd", i3os3)?;
|
||||
globals.set("bstring_invalid_4_octet_sequence_2nd", i4os2)?;
|
||||
globals.set("bstring_invalid_4_octet_sequence_3rd", i4os3)?;
|
||||
globals.set("bstring_invalid_4_octet_sequence_4th", i4os4)?;
|
||||
globals.set("bstring_an_actual_string", aas)?;
|
||||
|
||||
lua.load(
|
||||
r#"
|
||||
|
||||
+2
-3
@@ -1,5 +1,4 @@
|
||||
use std::fs;
|
||||
use std::io;
|
||||
use std::{fs, io};
|
||||
|
||||
use mlua::{Lua, Result};
|
||||
|
||||
@@ -65,7 +64,7 @@ fn test_chunk_macro() -> Result<()> {
|
||||
})
|
||||
.exec()?;
|
||||
|
||||
assert_eq!(lua.globals().get::<_, i32>("s")?, 321);
|
||||
assert_eq!(lua.globals().get::<i32>("s")?, 321);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -12,7 +12,6 @@ fn test_compilation() {
|
||||
t.compile_fail("tests/compile/scope_invariance.rs");
|
||||
t.compile_fail("tests/compile/scope_mutable_aliasing.rs");
|
||||
t.compile_fail("tests/compile/scope_userdata_borrow.rs");
|
||||
t.compile_fail("tests/compile/static_callback_args.rs");
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
{
|
||||
|
||||
@@ -1,10 +1,18 @@
|
||||
error[E0596]: cannot borrow `s` as mutable, as it is a captured variable in a `Fn` closure
|
||||
--> tests/compile/async_any_userdata_method.rs:9:58
|
||||
|
|
||||
9 | reg.add_async_method("t", |_, this: &String, ()| async {
|
||||
| ^^^^^ cannot borrow as mutable
|
||||
10 | s = this;
|
||||
| - mutable borrow occurs due to use of `s` in closure
|
||||
|
||||
error: lifetime may not live long enough
|
||||
--> tests/compile/async_any_userdata_method.rs:9:58
|
||||
|
|
||||
9 | reg.add_async_method("t", |_, this: &String, ()| async {
|
||||
| ___________________________________----------------------_^
|
||||
| | | |
|
||||
| | | return type of closure `{async block@$DIR/tests/compile/async_any_userdata_method.rs:9:58: 12:10}` contains a lifetime `'2`
|
||||
| | | return type of closure `{async block@$DIR/tests/compile/async_any_userdata_method.rs:9:58: 9:63}` contains a lifetime `'2`
|
||||
| | lifetime `'1` represents this closure's body
|
||||
10 | | s = this;
|
||||
11 | | Ok(())
|
||||
@@ -13,17 +21,6 @@ error: lifetime may not live long enough
|
||||
|
|
||||
= note: closure implements `Fn`, so references to captured variables can't escape the closure
|
||||
|
||||
error[E0596]: cannot borrow `s` as mutable, as it is a captured variable in a `Fn` closure
|
||||
--> tests/compile/async_any_userdata_method.rs:9:58
|
||||
|
|
||||
9 | reg.add_async_method("t", |_, this: &String, ()| async {
|
||||
| __________________________________________________________^
|
||||
10 | | s = this;
|
||||
| | - mutable borrow occurs due to use of `s` in closure
|
||||
11 | | Ok(())
|
||||
12 | | });
|
||||
| |_________^ cannot borrow as mutable
|
||||
|
||||
error[E0597]: `s` does not live long enough
|
||||
--> tests/compile/async_any_userdata_method.rs:8:21
|
||||
|
|
||||
|
||||
@@ -1,51 +1,32 @@
|
||||
error[E0277]: the type `UnsafeCell<mlua::lua::ExtraData>` 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::lua::ExtraData>` 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::lua::ExtraData>`
|
||||
note: required because it appears within the type `ArcInner<UnsafeCell<ExtraData>>`
|
||||
--> $RUST/alloc/src/sync.rs
|
||||
= help: within `mlua::types::sync::inner::ReentrantMutex<mlua::state::raw::RawLua>`, the trait `RefUnwindSafe` is not implemented for `UnsafeCell<*mut lua_State>`, 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<*mut lua_State>`
|
||||
--> $RUST/core/src/cell.rs
|
||||
|
|
||||
| struct ArcInner<T: ?Sized> {
|
||||
| ^^^^^^^^
|
||||
note: required because it appears within the type `PhantomData<ArcInner<UnsafeCell<ExtraData>>>`
|
||||
--> $RUST/core/src/marker.rs
|
||||
| pub struct Cell<T: ?Sized> {
|
||||
| ^^^^
|
||||
note: required because it appears within the type `mlua::state::raw::RawLua`
|
||||
--> src/state/raw.rs
|
||||
|
|
||||
| pub struct PhantomData<T: ?Sized>;
|
||||
| ^^^^^^^^^^^
|
||||
note: required because it appears within the type `Arc<UnsafeCell<ExtraData>>`
|
||||
--> $RUST/alloc/src/sync.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 Arc<
|
||||
| ^^^
|
||||
note: required because it appears within the type `LuaInner`
|
||||
--> src/lua.rs
|
||||
|
|
||||
| pub struct LuaInner {
|
||||
| ^^^^^^^^
|
||||
note: required because it appears within the type `ArcInner<LuaInner>`
|
||||
--> $RUST/alloc/src/sync.rs
|
||||
|
|
||||
| struct ArcInner<T: ?Sized> {
|
||||
| ^^^^^^^^
|
||||
note: required because it appears within the type `PhantomData<ArcInner<LuaInner>>`
|
||||
--> $RUST/core/src/marker.rs
|
||||
|
|
||||
| pub struct PhantomData<T: ?Sized>;
|
||||
| ^^^^^^^^^^^
|
||||
note: required because it appears within the type `Arc<LuaInner>`
|
||||
--> $RUST/alloc/src/sync.rs
|
||||
|
|
||||
| pub struct Arc<
|
||||
| ^^^
|
||||
| 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/lua.rs
|
||||
--> src/state.rs
|
||||
|
|
||||
| pub struct Lua(Arc<LuaInner>);
|
||||
| pub struct Lua(XRc<ReentrantMutex<RawLua>>);
|
||||
| ^^^
|
||||
= note: required for `&Lua` to implement `UnwindSafe`
|
||||
note: required because it's used within this closure
|
||||
@@ -53,7 +34,45 @@ note: required because it's used within this closure
|
||||
|
|
||||
7 | catch_unwind(|| lua.create_table().unwrap());
|
||||
| ^^
|
||||
note: required by a bound in `catch_unwind`
|
||||
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/lua_norefunwindsafe.rs:7:18
|
||||
|
|
||||
7 | catch_unwind(|| lua.create_table().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>`, which is required by `{closure@$DIR/tests/compile/lua_norefunwindsafe.rs:7:18: 7:20}: UnwindSafe`
|
||||
= 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 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
|
||||
|
|
||||
| pub struct Lua(XRc<ReentrantMutex<RawLua>>);
|
||||
| ^^^
|
||||
= note: required for `&Lua` to implement `UnwindSafe`
|
||||
note: required because it's used within this closure
|
||||
--> tests/compile/lua_norefunwindsafe.rs:7:18
|
||||
|
|
||||
7 | catch_unwind(|| lua.create_table().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> {
|
||||
|
||||
@@ -11,7 +11,7 @@ fn main() -> Result<()> {
|
||||
lua.create_function(move |_, ()| {
|
||||
Ok(data.get())
|
||||
})?
|
||||
.call::<_, i32>(())?;
|
||||
.call::<i32>(())?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1,69 +1,167 @@
|
||||
error[E0277]: the type `UnsafeCell<mlua::lua::ExtraData>` 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::lua::ExtraData>` 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 `Lua`, the trait `RefUnwindSafe` is not implemented for `UnsafeCell<mlua::lua::ExtraData>`
|
||||
note: required because it appears within the type `ArcInner<UnsafeCell<ExtraData>>`
|
||||
--> $RUST/alloc/src/sync.rs
|
||||
= help: within `rc::RcBox<mlua::types::sync::inner::ReentrantMutex<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>`
|
||||
--> $RUST/core/src/cell.rs
|
||||
|
|
||||
| struct ArcInner<T: ?Sized> {
|
||||
| ^^^^^^^^
|
||||
note: required because it appears within the type `PhantomData<ArcInner<UnsafeCell<ExtraData>>>`
|
||||
--> $RUST/core/src/marker.rs
|
||||
| pub struct Cell<T: ?Sized> {
|
||||
| ^^^^
|
||||
note: required because it appears within the type `rc::RcBox<mlua::types::sync::inner::ReentrantMutex<mlua::state::raw::RawLua>>`
|
||||
--> $RUST/alloc/src/rc.rs
|
||||
|
|
||||
| pub struct PhantomData<T: ?Sized>;
|
||||
| ^^^^^^^^^^^
|
||||
note: required because it appears within the type `Arc<UnsafeCell<ExtraData>>`
|
||||
--> $RUST/alloc/src/sync.rs
|
||||
| struct RcBox<T: ?Sized> {
|
||||
| ^^^^^
|
||||
= note: required for `NonNull<rc::RcBox<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 Arc<
|
||||
| ^^^
|
||||
note: required because it appears within the type `LuaInner`
|
||||
--> src/lua.rs
|
||||
| pub struct Weak<
|
||||
| ^^^^
|
||||
note: required because it appears within the type `mlua::state::WeakLua`
|
||||
--> src/state.rs
|
||||
|
|
||||
| pub struct LuaInner {
|
||||
| ^^^^^^^^
|
||||
note: required because it appears within the type `ArcInner<LuaInner>`
|
||||
--> $RUST/alloc/src/sync.rs
|
||||
|
|
||||
| struct ArcInner<T: ?Sized> {
|
||||
| ^^^^^^^^
|
||||
note: required because it appears within the type `PhantomData<ArcInner<LuaInner>>`
|
||||
--> $RUST/core/src/marker.rs
|
||||
|
|
||||
| pub struct PhantomData<T: ?Sized>;
|
||||
| ^^^^^^^^^^^
|
||||
note: required because it appears within the type `Arc<LuaInner>`
|
||||
--> $RUST/alloc/src/sync.rs
|
||||
|
|
||||
| pub struct Arc<
|
||||
| ^^^
|
||||
note: required because it appears within the type `Lua`
|
||||
--> src/lua.rs
|
||||
|
|
||||
| pub struct Lua(Arc<LuaInner>);
|
||||
| ^^^
|
||||
= note: required for `&Lua` to implement `UnwindSafe`
|
||||
note: required because it appears within the type `LuaRef<'_>`
|
||||
| pub(crate) struct WeakLua(XWeak<ReentrantMutex<RawLua>>);
|
||||
| ^^^^^^^
|
||||
note: required because it appears within the type `mlua::types::ValueRef`
|
||||
--> src/types.rs
|
||||
|
|
||||
| pub(crate) struct LuaRef<'lua> {
|
||||
| ^^^^^^
|
||||
note: required because it appears within the type `Table<'_>`
|
||||
| pub(crate) struct ValueRef {
|
||||
| ^^^^^^^^
|
||||
note: required because it appears within the type `LuaTable`
|
||||
--> src/table.rs
|
||||
|
|
||||
| pub struct Table<'lua>(pub(crate) LuaRef<'lua>);
|
||||
| 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 `catch_unwind`
|
||||
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<*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<*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 `rc::RcBox<mlua::types::sync::inner::ReentrantMutex<mlua::state::raw::RawLua>>`, the trait `RefUnwindSafe` is not implemented for `UnsafeCell<*mut lua_State>`, 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<*mut lua_State>`
|
||||
--> $RUST/core/src/cell.rs
|
||||
|
|
||||
| pub struct Cell<T: ?Sized> {
|
||||
| ^^^^
|
||||
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::RcBox<mlua::types::sync::inner::ReentrantMutex<mlua::state::raw::RawLua>>`
|
||||
--> $RUST/alloc/src/rc.rs
|
||||
|
|
||||
| struct RcBox<T: ?Sized> {
|
||||
| ^^^^^
|
||||
= note: required for `NonNull<rc::RcBox<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`
|
||||
--> 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(crate) 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>`, which is required by `{closure@$DIR/tests/compile/ref_nounwindsafe.rs:8:18: 8:25}: UnwindSafe`
|
||||
= 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::RcBox<mlua::types::sync::inner::ReentrantMutex<mlua::state::raw::RawLua>>`
|
||||
--> $RUST/alloc/src/rc.rs
|
||||
|
|
||||
| struct RcBox<T: ?Sized> {
|
||||
| ^^^^^
|
||||
= note: required for `NonNull<rc::RcBox<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`
|
||||
--> 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(crate) 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> {
|
||||
|
||||
@@ -12,7 +12,7 @@ fn main() {
|
||||
inner = Some(t);
|
||||
Ok(())
|
||||
})?;
|
||||
f.call::<_, ()>(lua.create_table()?)?;
|
||||
f.call::<()>(lua.create_table()?)?;
|
||||
Ok(())
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,26 +1,5 @@
|
||||
warning: unused variable: `old`
|
||||
--> $DIR/scope_callback_capture.rs:9:29
|
||||
error[E0599]: no method named `scope` found for struct `Lua` in the current scope
|
||||
--> tests/compile/scope_callback_capture.rs:5:9
|
||||
|
|
||||
9 | if let Some(old) = inner.take() {
|
||||
| ^^^ help: if this is intentional, prefix it with an underscore: `_old`
|
||||
|
|
||||
= note: `#[warn(unused_variables)]` on by default
|
||||
|
||||
error[E0521]: borrowed data escapes outside of closure
|
||||
--> $DIR/scope_callback_capture.rs:7:17
|
||||
|
|
||||
5 | lua.scope(|scope| {
|
||||
| -----
|
||||
| |
|
||||
| `scope` declared here, outside of the closure body
|
||||
| `scope` is a reference that is only valid in the closure body
|
||||
6 | let mut inner: Option<Table> = None;
|
||||
7 | let f = scope
|
||||
| _________________^
|
||||
8 | | .create_function_mut(move |_, t: Table| {
|
||||
9 | | if let Some(old) = inner.take() {
|
||||
10 | | // Access old callback `Lua`.
|
||||
... |
|
||||
13 | | Ok(())
|
||||
14 | | })?;
|
||||
| |______________^ `scope` escapes the closure body here
|
||||
5 | lua.scope(|scope| {
|
||||
| ----^^^^^ method not found in `Lua`
|
||||
|
||||
@@ -9,7 +9,7 @@ fn main() {
|
||||
inner = Some(t);
|
||||
Ok(())
|
||||
})?;
|
||||
f.call::<_, ()>(lua.create_table()?)?;
|
||||
f.call::<()>(lua.create_table()?)?;
|
||||
Ok(())
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,42 +1,5 @@
|
||||
error[E0521]: borrowed data escapes outside of closure
|
||||
--> tests/compile/scope_callback_inner.rs:7:17
|
||||
|
|
||||
5 | lua.scope(|scope| {
|
||||
| -----
|
||||
| |
|
||||
| `scope` declared here, outside of the closure body
|
||||
| `scope` is a reference that is only valid in the closure body
|
||||
6 | let mut inner: Option<Table> = None;
|
||||
7 | let f = scope
|
||||
| _________________^
|
||||
8 | | .create_function_mut(|_, t: Table| {
|
||||
9 | | inner = Some(t);
|
||||
10 | | Ok(())
|
||||
11 | | })?;
|
||||
| |______________^ `scope` escapes the closure body here
|
||||
|
||||
error[E0373]: closure may outlive the current function, but it borrows `inner`, which is owned by the current function
|
||||
--> tests/compile/scope_callback_inner.rs:8:34
|
||||
|
|
||||
5 | lua.scope(|scope| {
|
||||
| ----- has type `&mlua::Scope<'_, '2>`
|
||||
...
|
||||
8 | .create_function_mut(|_, t: Table| {
|
||||
| ^^^^^^^^^^^^^ may outlive borrowed value `inner`
|
||||
9 | inner = Some(t);
|
||||
| ----- `inner` is borrowed here
|
||||
|
|
||||
note: function requires argument type to outlive `'2`
|
||||
--> tests/compile/scope_callback_inner.rs:7:17
|
||||
|
|
||||
7 | let f = scope
|
||||
| _________________^
|
||||
8 | | .create_function_mut(|_, t: Table| {
|
||||
9 | | inner = Some(t);
|
||||
10 | | Ok(())
|
||||
11 | | })?;
|
||||
| |______________^
|
||||
help: to force the closure to take ownership of `inner` (and any other referenced variables), use the `move` keyword
|
||||
|
|
||||
8 | .create_function_mut(move |_, t: Table| {
|
||||
| ++++
|
||||
error[E0599]: no method named `scope` found for struct `Lua` in the current scope
|
||||
--> tests/compile/scope_callback_inner.rs:5:9
|
||||
|
|
||||
5 | lua.scope(|scope| {
|
||||
| ----^^^^^ method not found in `Lua`
|
||||
|
||||
@@ -9,7 +9,7 @@ fn main() {
|
||||
outer = Some(t);
|
||||
Ok(())
|
||||
})?;
|
||||
f.call::<_, ()>(lua.create_table()?)?;
|
||||
f.call::<()>(lua.create_table()?)?;
|
||||
Ok(())
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,30 +1,5 @@
|
||||
error[E0521]: borrowed data escapes outside of closure
|
||||
--> $DIR/scope_callback_outer.rs:7:17
|
||||
|
|
||||
6 | lua.scope(|scope| {
|
||||
| -----
|
||||
| |
|
||||
| `scope` declared here, outside of the closure body
|
||||
| `scope` is a reference that is only valid in the closure body
|
||||
7 | let f = scope
|
||||
| _________________^
|
||||
8 | | .create_function_mut(|_, t: Table| {
|
||||
9 | | outer = Some(t);
|
||||
10 | | Ok(())
|
||||
11 | | })?;
|
||||
| |______________^ `scope` escapes the closure body here
|
||||
|
||||
error[E0597]: `outer` does not live long enough
|
||||
--> $DIR/scope_callback_outer.rs:9:17
|
||||
|
|
||||
6 | lua.scope(|scope| {
|
||||
| ------- value captured here
|
||||
...
|
||||
9 | outer = Some(t);
|
||||
| ^^^^^ borrowed value does not live long enough
|
||||
...
|
||||
15 | }
|
||||
| -
|
||||
| |
|
||||
| `outer` dropped here while still borrowed
|
||||
| borrow might be used here, when `outer` is dropped and runs the destructor for type `Option<LuaTable<'_>>`
|
||||
error[E0599]: no method named `scope` found for struct `Lua` in the current scope
|
||||
--> tests/compile/scope_callback_outer.rs:6:9
|
||||
|
|
||||
6 | lua.scope(|scope| {
|
||||
| ----^^^^^ method not found in `Lua`
|
||||
|
||||
@@ -18,6 +18,6 @@ fn main() {
|
||||
})?
|
||||
};
|
||||
|
||||
f.call::<_, ()>(())
|
||||
f.call::<()>(())
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,25 +1,5 @@
|
||||
error[E0373]: closure may outlive the current function, but it borrows `test.field`, which is owned by the current function
|
||||
--> tests/compile/scope_invariance.rs:14:38
|
||||
|
|
||||
9 | lua.scope(|scope| {
|
||||
| ----- has type `&mlua::Scope<'_, '1>`
|
||||
...
|
||||
14 | .create_function_mut(|_, ()| {
|
||||
| ^^^^^^^ may outlive borrowed value `test.field`
|
||||
15 | test.field = 42;
|
||||
| ---------- `test.field` is borrowed here
|
||||
|
|
||||
note: function requires argument type to outlive `'1`
|
||||
--> tests/compile/scope_invariance.rs:13:13
|
||||
|
|
||||
13 | / scope
|
||||
14 | | .create_function_mut(|_, ()| {
|
||||
15 | | test.field = 42;
|
||||
16 | | //~^ error: `test` does not live long enough
|
||||
17 | | Ok(())
|
||||
18 | | })?
|
||||
| |__________________^
|
||||
help: to force the closure to take ownership of `test.field` (and any other referenced variables), use the `move` keyword
|
||||
|
|
||||
14 | .create_function_mut(move |_, ()| {
|
||||
| ++++
|
||||
error[E0599]: no method named `scope` found for struct `Lua` in the current scope
|
||||
--> tests/compile/scope_invariance.rs:9:9
|
||||
|
|
||||
9 | lua.scope(|scope| {
|
||||
| ----^^^^^ method not found in `Lua`
|
||||
|
||||
@@ -1,9 +1,5 @@
|
||||
error[E0499]: cannot borrow `i` as mutable more than once at a time
|
||||
--> $DIR/scope_mutable_aliasing.rs:12:61
|
||||
error[E0599]: no method named `scope` found for struct `Lua` in the current scope
|
||||
--> tests/compile/scope_mutable_aliasing.rs:10:9
|
||||
|
|
||||
11 | let _a = scope.create_nonstatic_userdata(MyUserData(&mut i)).unwrap();
|
||||
| ------ first mutable borrow occurs here
|
||||
12 | let _b = scope.create_nonstatic_userdata(MyUserData(&mut i)).unwrap();
|
||||
| ------------------------- ^^^^^^ second mutable borrow occurs here
|
||||
| |
|
||||
| first borrow later used by call
|
||||
10 | lua.scope(|scope| {
|
||||
| ----^^^^^ method not found in `Lua`
|
||||
|
||||
@@ -1,15 +1,5 @@
|
||||
error[E0597]: `ibad` does not live long enough
|
||||
--> tests/compile/scope_userdata_borrow.rs:15:56
|
||||
error[E0599]: no method named `scope` found for struct `Lua` in the current scope
|
||||
--> tests/compile/scope_userdata_borrow.rs:11:9
|
||||
|
|
||||
11 | lua.scope(|scope| {
|
||||
| ----- has type `&mlua::Scope<'_, '1>`
|
||||
...
|
||||
14 | let ibad = 42;
|
||||
| ---- binding `ibad` declared here
|
||||
15 | scope.create_nonstatic_userdata(MyUserData(&ibad)).unwrap();
|
||||
| -------------------------------------------^^^^^--
|
||||
| | |
|
||||
| | borrowed value does not live long enough
|
||||
| argument requires that `ibad` is borrowed for `'1`
|
||||
16 | };
|
||||
| - `ibad` dropped here while still borrowed
|
||||
| ----^^^^^ method not found in `Lua`
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
use std::cell::RefCell;
|
||||
|
||||
use mlua::{Lua, Result, Table};
|
||||
|
||||
fn main() -> Result<()> {
|
||||
thread_local! {
|
||||
static BAD_TIME: RefCell<Option<Table<'static>>> = RefCell::new(None);
|
||||
}
|
||||
|
||||
let lua = Lua::new();
|
||||
|
||||
lua.create_function(|_, table: Table| {
|
||||
BAD_TIME.with(|bt| {
|
||||
*bt.borrow_mut() = Some(table);
|
||||
});
|
||||
Ok(())
|
||||
})?
|
||||
.call::<_, ()>(lua.create_table()?)?;
|
||||
|
||||
// In debug, this will panic with a reference leak before getting to the next part but
|
||||
// it segfaults anyway.
|
||||
drop(lua);
|
||||
|
||||
BAD_TIME.with(|bt| {
|
||||
println!(
|
||||
"you're gonna have a bad time: {}",
|
||||
bt.borrow().as_ref().unwrap().len().unwrap()
|
||||
);
|
||||
});
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
error[E0597]: `lua` does not live long enough
|
||||
--> tests/compile/static_callback_args.rs:12:5
|
||||
|
|
||||
10 | let lua = Lua::new();
|
||||
| --- binding `lua` declared here
|
||||
11 |
|
||||
12 | lua.create_function(|_, table: Table| {
|
||||
| ^^^ borrowed value does not live long enough
|
||||
13 | / BAD_TIME.with(|bt| {
|
||||
14 | | *bt.borrow_mut() = Some(table);
|
||||
15 | | });
|
||||
| |__________- argument requires that `lua` is borrowed for `'static`
|
||||
...
|
||||
32 | }
|
||||
| - `lua` dropped here while still borrowed
|
||||
|
||||
error[E0505]: cannot move out of `lua` because it is borrowed
|
||||
--> tests/compile/static_callback_args.rs:22:10
|
||||
|
|
||||
10 | let lua = Lua::new();
|
||||
| --- binding `lua` declared here
|
||||
11 |
|
||||
12 | lua.create_function(|_, table: Table| {
|
||||
| --- borrow of `lua` occurs here
|
||||
13 | / BAD_TIME.with(|bt| {
|
||||
14 | | *bt.borrow_mut() = Some(table);
|
||||
15 | | });
|
||||
| |__________- argument requires that `lua` is borrowed for `'static`
|
||||
...
|
||||
22 | drop(lua);
|
||||
| ^^^ move out of `lua` occurs here
|
||||
@@ -9,9 +9,9 @@ fn main() -> Result<()> {
|
||||
impl UserData for MyUserData {};
|
||||
let _userdata_ref;
|
||||
{
|
||||
let touter = globals.get::<_, Table>("touter")?;
|
||||
let touter = globals.get::<Table>("touter")?;
|
||||
touter.set("userdata", lua.create_userdata(MyUserData)?)?;
|
||||
let userdata = touter.get::<_, AnyUserData>("userdata")?;
|
||||
let userdata = touter.get::<AnyUserData>("userdata")?;
|
||||
_userdata_ref = userdata.borrow::<MyUserData>();
|
||||
//~^ error: `userdata` does not live long enough
|
||||
}
|
||||
|
||||
+78
-136
@@ -5,8 +5,7 @@ use std::ffi::{CStr, CString};
|
||||
use bstr::BString;
|
||||
use maplit::{btreemap, btreeset, hashmap, hashset};
|
||||
use mlua::{
|
||||
AnyUserData, Error, Function, IntoLua, Lua, RegistryKey, Result, Table, Thread, UserDataRef,
|
||||
Value,
|
||||
AnyUserData, Error, Function, IntoLua, Lua, RegistryKey, Result, Table, Thread, UserDataRef, Value,
|
||||
};
|
||||
|
||||
#[test]
|
||||
@@ -21,7 +20,7 @@ fn test_value_into_lua() -> Result<()> {
|
||||
// Push into stack
|
||||
let table = lua.create_table()?;
|
||||
table.set("v", &v)?;
|
||||
assert_eq!(v, table.get::<_, Value>("v")?);
|
||||
assert_eq!(v, table.get::<Value>("v")?);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -38,36 +37,23 @@ fn test_string_into_lua() -> Result<()> {
|
||||
// Push into stack
|
||||
let table = lua.create_table()?;
|
||||
table.set("s", &s)?;
|
||||
assert_eq!(s, table.get::<_, String>("s")?);
|
||||
assert_eq!(s, table.get::<String>("s")?);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "unstable", not(feature = "send")))]
|
||||
#[test]
|
||||
fn test_owned_string_into_lua() -> Result<()> {
|
||||
fn test_string_from_lua() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
// Direct conversion
|
||||
let s = lua.create_string("hello, world")?.into_owned();
|
||||
let s2 = (&s).into_lua(&lua)?;
|
||||
assert_eq!(s.to_ref(), *s2.as_string().unwrap());
|
||||
// From stack
|
||||
let f = lua.create_function(|_, s: mlua::String| Ok(s))?;
|
||||
let s = f.call::<String>("hello, world!")?;
|
||||
assert_eq!(s, "hello, world!");
|
||||
|
||||
// Push into stack
|
||||
let table = lua.create_table()?;
|
||||
table.set("s", &s)?;
|
||||
assert_eq!(s.to_ref(), table.get::<_, String>("s")?);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "unstable", not(feature = "send")))]
|
||||
#[test]
|
||||
fn test_owned_string_from_lua() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let s = lua.unpack::<mlua::OwnedString>(lua.pack("hello, world")?)?;
|
||||
assert_eq!(s.to_ref(), "hello, world");
|
||||
// Should fallback to default conversion
|
||||
let s = f.call::<String>(42)?;
|
||||
assert_eq!(s, "42");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -83,26 +69,8 @@ fn test_table_into_lua() -> Result<()> {
|
||||
|
||||
// Push into stack
|
||||
let f = lua.create_function(|_, (t, s): (Table, String)| t.set("s", s))?;
|
||||
f.call((&t, "hello"))?;
|
||||
assert_eq!("hello", t.get::<_, String>("s")?);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "unstable", not(feature = "send")))]
|
||||
#[test]
|
||||
fn test_owned_table_into_lua() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
// Direct conversion
|
||||
let t = lua.create_table()?.into_owned();
|
||||
let t2 = (&t).into_lua(&lua)?;
|
||||
assert_eq!(t.to_ref(), *t2.as_table().unwrap());
|
||||
|
||||
// Push into stack
|
||||
let f = lua.create_function(|_, (t, s): (Table, String)| t.set("s", s))?;
|
||||
f.call((&t, "hello"))?;
|
||||
assert_eq!("hello", t.to_ref().get::<_, String>("s")?);
|
||||
f.call::<()>((&t, "hello"))?;
|
||||
assert_eq!("hello", t.get::<String>("s")?);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -119,27 +87,7 @@ fn test_function_into_lua() -> Result<()> {
|
||||
// Push into stack
|
||||
let table = lua.create_table()?;
|
||||
table.set("f", &f)?;
|
||||
assert_eq!(f, table.get::<_, Function>("f")?);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "unstable", not(feature = "send")))]
|
||||
#[test]
|
||||
fn test_owned_function_into_lua() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
// Direct conversion
|
||||
let f = lua
|
||||
.create_function(|_, ()| Ok::<_, Error>(()))?
|
||||
.into_owned();
|
||||
let f2 = (&f).into_lua(&lua)?;
|
||||
assert_eq!(f.to_ref(), *f2.as_function().unwrap());
|
||||
|
||||
// Push into stack
|
||||
let table = lua.create_table()?;
|
||||
table.set("f", &f)?;
|
||||
assert_eq!(f.to_ref(), table.get::<_, Function>("f")?);
|
||||
assert_eq!(f, table.get::<Function>("f")?);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -157,37 +105,7 @@ fn test_thread_into_lua() -> Result<()> {
|
||||
// Push into stack
|
||||
let table = lua.create_table()?;
|
||||
table.set("th", &th)?;
|
||||
assert_eq!(th, table.get::<_, Thread>("th")?);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "unstable", not(feature = "send")))]
|
||||
#[test]
|
||||
fn test_owned_thread_into_lua() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
// Direct conversion
|
||||
let f = lua.create_function(|_, ()| Ok::<_, Error>(()))?;
|
||||
let th = lua.create_thread(f)?.into_owned();
|
||||
let th2 = (&th).into_lua(&lua)?;
|
||||
assert_eq!(&th.to_ref(), th2.as_thread().unwrap());
|
||||
|
||||
// Push into stack
|
||||
let table = lua.create_table()?;
|
||||
table.set("th", &th)?;
|
||||
assert_eq!(th.to_ref(), table.get::<_, Thread>("th")?);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "unstable", not(feature = "send")))]
|
||||
#[test]
|
||||
fn test_owned_thread_from_lua() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let th = lua.unpack::<mlua::OwnedThread>(Value::Thread(lua.current_thread()))?;
|
||||
assert_eq!(th.to_ref(), lua.current_thread());
|
||||
assert_eq!(th, table.get::<Thread>("th")?);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -204,27 +122,8 @@ fn test_anyuserdata_into_lua() -> Result<()> {
|
||||
// Push into stack
|
||||
let table = lua.create_table()?;
|
||||
table.set("ud", &ud)?;
|
||||
assert_eq!(ud, table.get::<_, AnyUserData>("ud")?);
|
||||
assert_eq!("hello", *table.get::<_, UserDataRef<String>>("ud")?);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "unstable", not(feature = "send")))]
|
||||
#[test]
|
||||
fn test_owned_anyuserdata_into_lua() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
// Direct conversion
|
||||
let ud = lua.create_any_userdata(String::from("hello"))?.into_owned();
|
||||
let ud2 = (&ud).into_lua(&lua)?;
|
||||
assert_eq!(ud.to_ref(), *ud2.as_userdata().unwrap());
|
||||
|
||||
// Push into stack
|
||||
let table = lua.create_table()?;
|
||||
table.set("ud", &ud)?;
|
||||
assert_eq!(ud.to_ref(), table.get::<_, AnyUserData>("ud")?);
|
||||
assert_eq!("hello", *table.get::<_, UserDataRef<String>>("ud")?);
|
||||
assert_eq!(ud, table.get::<AnyUserData>("ud")?);
|
||||
assert_eq!("hello", *table.get::<UserDataRef<String>>("ud")?);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -238,30 +137,27 @@ fn test_registry_value_into_lua() -> Result<()> {
|
||||
let r = lua.create_registry_value(&s)?;
|
||||
let value1 = lua.pack(&r)?;
|
||||
let value2 = lua.pack(r)?;
|
||||
assert_eq!(value1.as_str(), Some("hello, world"));
|
||||
assert_eq!(value1.as_str().as_deref(), Some("hello, world"));
|
||||
assert_eq!(value2.to_pointer(), value2.to_pointer());
|
||||
|
||||
// Push into stack
|
||||
let t = lua.create_table()?;
|
||||
let r = lua.create_registry_value(&t)?;
|
||||
let f = lua.create_function(|_, (t, k, v): (Table, Value, Value)| t.set(k, v))?;
|
||||
f.call((&r, "hello", "world"))?;
|
||||
f.call((r, "welcome", "to the jungle"))?;
|
||||
assert_eq!(t.get::<_, String>("hello")?, "world");
|
||||
assert_eq!(t.get::<_, String>("welcome")?, "to the jungle");
|
||||
f.call::<()>((&r, "hello", "world"))?;
|
||||
f.call::<()>((r, "welcome", "to the jungle"))?;
|
||||
assert_eq!(t.get::<String>("hello")?, "world");
|
||||
assert_eq!(t.get::<String>("welcome")?, "to the jungle");
|
||||
|
||||
// Try to set nil registry key
|
||||
let r_nil = lua.create_registry_value(Value::Nil)?;
|
||||
t.set("hello", &r_nil)?;
|
||||
assert_eq!(t.get::<_, Value>("hello")?, Value::Nil);
|
||||
assert_eq!(t.get::<Value>("hello")?, Value::Nil);
|
||||
|
||||
// Check non-owned registry key
|
||||
let lua2 = Lua::new();
|
||||
let r2 = lua2.create_registry_value("abc")?;
|
||||
assert!(matches!(
|
||||
f.call::<_, ()>(&r2),
|
||||
Err(Error::MismatchedRegistryKey)
|
||||
));
|
||||
assert!(matches!(f.call::<()>(&r2), Err(Error::MismatchedRegistryKey)));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -272,7 +168,53 @@ fn test_registry_key_from_lua() -> Result<()> {
|
||||
|
||||
let fkey = lua.load("function() return 1 end").eval::<RegistryKey>()?;
|
||||
let f = lua.registry_value::<Function>(&fkey)?;
|
||||
assert_eq!(f.call::<_, i32>(())?, 1);
|
||||
assert_eq!(f.call::<i32>(())?, 1);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_integer_from_lua() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
// From stack
|
||||
let f = lua.create_function(|_, i: i32| Ok(i))?;
|
||||
assert_eq!(f.call::<i32>(42)?, 42);
|
||||
|
||||
// Out of range
|
||||
match f.call::<i32>(i64::MAX).err() {
|
||||
Some(Error::CallbackError { cause, .. }) => match cause.as_ref() {
|
||||
Error::BadArgument { cause, .. } => match cause.as_ref() {
|
||||
Error::FromLuaConversionError { message, .. } => {
|
||||
assert_eq!(message.as_ref().unwrap(), "out of range");
|
||||
}
|
||||
err => panic!("expected Error::FromLuaConversionError, got {err:?}"),
|
||||
},
|
||||
err => panic!("expected Error::BadArgument, got {err:?}"),
|
||||
},
|
||||
err => panic!("expected Error::CallbackError, got {err:?}"),
|
||||
}
|
||||
|
||||
// Should fallback to default conversion
|
||||
assert_eq!(f.call::<i32>("42")?, 42);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_float_from_lua() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
// From stack
|
||||
let f = lua.create_function(|_, f: f32| Ok(f))?;
|
||||
assert_eq!(f.call::<f32>(42.0)?, 42.0);
|
||||
|
||||
// Out of range (but never fails)
|
||||
let val = f.call::<f32>(f64::MAX)?;
|
||||
assert!(val.is_infinite());
|
||||
|
||||
// Should fallback to default conversion
|
||||
assert_eq!(f.call::<f32>("42.0")?, 42.0);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -405,7 +347,7 @@ fn test_conv_array() -> Result<()> {
|
||||
let v2: [i32; 3] = lua.globals().get("v")?;
|
||||
assert_eq!(v, v2);
|
||||
|
||||
let v2 = lua.globals().get::<_, [i32; 4]>("v");
|
||||
let v2 = lua.globals().get::<[i32; 4]>("v");
|
||||
assert!(matches!(v2, Err(Error::FromLuaConversionError { .. })));
|
||||
|
||||
Ok(())
|
||||
@@ -427,10 +369,10 @@ fn test_bstring_from_lua() -> Result<()> {
|
||||
|
||||
// Test from stack
|
||||
let f = lua.create_function(|_, bstr: BString| Ok(bstr))?;
|
||||
let bstr = f.call::<_, BString>("hello, world")?;
|
||||
let bstr = f.call::<BString>("hello, world")?;
|
||||
assert_eq!(bstr, "hello, world");
|
||||
|
||||
let bstr = f.call::<_, BString>(-43.22)?;
|
||||
let bstr = f.call::<BString>(-43.22)?;
|
||||
assert_eq!(bstr, "-43.22");
|
||||
|
||||
Ok(())
|
||||
@@ -448,7 +390,7 @@ fn test_bstring_from_lua_buffer() -> Result<()> {
|
||||
// Test from stack
|
||||
let f = lua.create_function(|_, bstr: BString| Ok(bstr))?;
|
||||
let buf = lua.create_buffer("hello, world")?;
|
||||
let bstr = f.call::<_, BString>(buf)?;
|
||||
let bstr = f.call::<BString>(buf)?;
|
||||
assert_eq!(bstr, "hello, world");
|
||||
|
||||
Ok(())
|
||||
@@ -465,9 +407,9 @@ fn test_option_into_from_lua() -> Result<()> {
|
||||
|
||||
// Push into stack / get from stack
|
||||
let f = lua.create_function(|_, v: Option<i32>| Ok(v))?;
|
||||
assert_eq!(f.call::<_, Option<i32>>(Some(42))?, Some(42));
|
||||
assert_eq!(f.call::<_, Option<i32>>(Option::<i32>::None)?, None);
|
||||
assert_eq!(f.call::<_, Option<i32>>(())?, None);
|
||||
assert_eq!(f.call::<Option<i32>>(Some(42))?, Some(42));
|
||||
assert_eq!(f.call::<Option<i32>>(Option::<i32>::None)?, None);
|
||||
assert_eq!(f.call::<Option<i32>>(())?, None);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user