mirror of
https://github.com/mlua-rs/mlua
synced 2026-06-08 16:05:43 +00:00
Compare commits
47 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 |
+12
-11
@@ -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,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,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,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,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"
|
||||
cargo test --features "${{ matrix.lua }},vendored,async,serialize,macros,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,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,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,8 +228,7 @@ 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"
|
||||
cargo test --tests --features "${{ matrix.lua }},vendored,async,serialize,macros,unstable"
|
||||
cargo test --tests --features "${{ matrix.lua }},vendored,async,serialize,macros"
|
||||
|
||||
rustfmt:
|
||||
name: Rustfmt
|
||||
@@ -254,4 +255,4 @@ jobs:
|
||||
- uses: giraffate/clippy-action@v1
|
||||
with:
|
||||
reporter: 'github-pr-review'
|
||||
clippy_flags: --features "${{ matrix.lua }},vendored,async,send,serialize,macros,unstable"
|
||||
clippy_flags: --features "${{ matrix.lua }},vendored,async,send,serialize,macros"
|
||||
|
||||
@@ -1,3 +1,20 @@
|
||||
## 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)
|
||||
|
||||
+6
-7
@@ -1,8 +1,8 @@
|
||||
[package]
|
||||
name = "mlua"
|
||||
version = "0.10.0-beta.1" # 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", "unstable"]
|
||||
features = ["lua54", "vendored", "async", "send", "serialize", "macros"]
|
||||
rustdoc-args = ["--cfg", "docsrs"]
|
||||
|
||||
[workspace]
|
||||
@@ -38,13 +38,12 @@ 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 }
|
||||
num-traits = { version = "0.2.14" }
|
||||
rustc-hash = "2.0"
|
||||
@@ -54,7 +53,7 @@ erased-serde = { version = "0.4", optional = true }
|
||||
serde-value = { version = "0.7", 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 }
|
||||
|
||||
@@ -38,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
|
||||
@@ -68,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
|
||||
|
||||
@@ -92,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
|
||||
```
|
||||
|
||||
@@ -196,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 = [
|
||||
|
||||
+12
-18
@@ -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,
|
||||
);
|
||||
@@ -194,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,
|
||||
);
|
||||
@@ -220,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,
|
||||
);
|
||||
@@ -246,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,
|
||||
);
|
||||
@@ -303,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<'a, M: LuaUserDataMethods<'a, Self>>(methods: &mut M) {
|
||||
fn add_methods<M: LuaUserDataMethods<Self>>(methods: &mut M) {
|
||||
methods.add_meta_method(LuaMetaMethod::Index, move |_, _, key: LuaString| Ok(key));
|
||||
}
|
||||
}
|
||||
@@ -319,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,
|
||||
);
|
||||
@@ -329,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<'a, M: LuaUserDataMethods<'a, Self>>(methods: &mut M) {
|
||||
fn add_methods<M: LuaUserDataMethods<Self>>(methods: &mut M) {
|
||||
methods.add_method("add", |_, this, i: i64| Ok(this.0 + i));
|
||||
}
|
||||
}
|
||||
@@ -349,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,
|
||||
);
|
||||
@@ -359,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<'a, M: LuaUserDataMethods<'a, 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)
|
||||
@@ -384,7 +378,7 @@ fn userdata_async_call_method(c: &mut Criterion) {
|
||||
(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();
|
||||
|
||||
@@ -17,7 +17,7 @@ use mlua::{chunk, Error as LuaError, Function, Lua, String as LuaString, Table,
|
||||
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()));
|
||||
@@ -47,13 +47,13 @@ impl hyper::service::Service<Request<Incoming>> for Svc {
|
||||
let handler = self.handler.clone();
|
||||
let lua_req = LuaRequest(self.peer_addr, req);
|
||||
Box::pin(async move {
|
||||
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());
|
||||
@@ -62,7 +62,7 @@ impl hyper::service::Service<Request<Incoming>> for Svc {
|
||||
|
||||
// Set body
|
||||
let body = lua_resp
|
||||
.get::<_, Option<LuaString>>("body")?
|
||||
.get::<Option<LuaString>>("body")?
|
||||
.map(|b| Full::new(Bytes::copy_from_slice(&b.as_bytes())).boxed())
|
||||
.unwrap_or_else(|| Empty::<Bytes>::new().boxed());
|
||||
|
||||
|
||||
@@ -4,27 +4,27 @@ use std::net::SocketAddr;
|
||||
use tokio::io::{AsyncReadExt, AsyncWriteExt};
|
||||
use tokio::net::{TcpListener, TcpStream};
|
||||
|
||||
use mlua::{chunk, Function, Lua, 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) {
|
||||
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(())
|
||||
});
|
||||
@@ -47,7 +47,7 @@ async fn run_server(handler: Function) -> io::Result<()> {
|
||||
let handler = handler.clone();
|
||||
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);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -17,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
|
||||
@@ -32,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);
|
||||
@@ -85,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(),
|
||||
))?;
|
||||
|
||||
@@ -162,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())
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -403,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,
|
||||
|
||||
@@ -247,3 +247,22 @@ pub unsafe fn luaL_requiref(L: *mut lua_State, modname: *const c_char, openf: lu
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
//! 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)]
|
||||
@@ -12,3 +13,22 @@ pub unsafe fn lua_resume(L: *mut lua_State, from: *mut lua_State, narg: c_int, n
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
@@ -169,6 +169,25 @@ pub unsafe fn luaL_loadbuffer(L: *mut lua_State, s: *const c_char, sz: usize, n:
|
||||
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
|
||||
//
|
||||
|
||||
@@ -320,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,17 +346,28 @@ pub unsafe fn luaL_loadbufferx(
|
||||
|
||||
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,
|
||||
@@ -363,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)]
|
||||
|
||||
@@ -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 }
|
||||
|
||||
@@ -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)
|
||||
})
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
+34
-24
@@ -5,11 +5,11 @@ 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::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`]
|
||||
///
|
||||
@@ -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,7 +288,19 @@ 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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -308,13 +322,8 @@ impl<'a> Chunk<'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>(mut self, env: V) -> Self {
|
||||
let lua = self.lua.lock();
|
||||
let lua = lua.lua();
|
||||
self.env = env
|
||||
.into_lua(lua)
|
||||
.and_then(|val| lua.unpack(val))
|
||||
.context("bad environment value");
|
||||
pub fn set_environment(mut self, env: Table) -> Self {
|
||||
self.env = Ok(Some(env));
|
||||
self
|
||||
}
|
||||
|
||||
@@ -343,8 +352,7 @@ impl<'a> Chunk<'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.
|
||||
@@ -404,7 +412,7 @@ impl<'a> Chunk<'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, R: FromLuaMulti>(self, args: A) -> Result<R> {
|
||||
pub fn call<R: FromLuaMulti>(self, args: impl IntoLuaMulti) -> Result<R> {
|
||||
self.into_function()?.call(args)
|
||||
}
|
||||
|
||||
@@ -417,9 +425,8 @@ impl<'a> Chunk<'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,
|
||||
R: FromLuaMulti,
|
||||
{
|
||||
self.into_function()?.call_async(args).await
|
||||
@@ -439,18 +446,17 @@ impl<'a> Chunk<'a> {
|
||||
let name = Self::convert_name(self.name)?;
|
||||
self.lua
|
||||
.lock()
|
||||
.load_chunk(Some(&name), self.env?, self.mode, self.source?.as_ref())
|
||||
.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);
|
||||
}
|
||||
@@ -517,12 +523,16 @@ impl<'a> Chunk<'a> {
|
||||
.compiler
|
||||
.as_ref()
|
||||
.map(|c| c.compile(&source))
|
||||
.transpose()?
|
||||
.unwrap_or(source);
|
||||
|
||||
let name = Self::convert_name(self.name.clone())?;
|
||||
self.lua
|
||||
.lock()
|
||||
.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 {
|
||||
|
||||
+55
-8
@@ -76,6 +76,17 @@ impl FromLua for String {
|
||||
message: Some("expected string or number".to_string()),
|
||||
})
|
||||
}
|
||||
|
||||
unsafe fn from_stack(idx: c_int, lua: &RawLua) -> Result<Self> {
|
||||
let state = lua.state();
|
||||
let type_id = ffi::lua_type(state, idx);
|
||||
if type_id == ffi::LUA_TSTRING {
|
||||
ffi::lua_xpush(state, lua.ref_thread(), idx);
|
||||
return Ok(String(lua.pop_ref_thread()));
|
||||
}
|
||||
// Fallback to default
|
||||
Self::from_lua(lua.stack_value(idx, Some(type_id)), lua.lua())
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoLua for Table {
|
||||
@@ -385,7 +396,8 @@ impl FromLua for StdString {
|
||||
#[inline]
|
||||
unsafe fn from_stack(idx: c_int, lua: &RawLua) -> Result<Self> {
|
||||
let state = lua.state();
|
||||
if ffi::lua_type(state, idx) == ffi::LUA_TSTRING {
|
||||
let type_id = ffi::lua_type(state, idx);
|
||||
if type_id == ffi::LUA_TSTRING {
|
||||
let mut size = 0;
|
||||
let data = ffi::lua_tolstring(state, idx, &mut size);
|
||||
let bytes = slice::from_raw_parts(data as *const u8, size);
|
||||
@@ -398,7 +410,7 @@ impl FromLua for StdString {
|
||||
});
|
||||
}
|
||||
// Fallback to default
|
||||
Self::from_lua(lua.stack_value(idx), lua.lua())
|
||||
Self::from_lua(lua.stack_value(idx, Some(type_id)), lua.lua())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -536,9 +548,9 @@ impl FromLua for BString {
|
||||
mlua_assert!(!buf.is_null(), "invalid Luau buffer");
|
||||
Ok(slice::from_raw_parts(buf as *const u8, size).into())
|
||||
}
|
||||
_ => {
|
||||
type_id => {
|
||||
// Fallback to default
|
||||
Self::from_lua(lua.stack_value(idx), lua.lua())
|
||||
Self::from_lua(lua.stack_value(idx, Some(type_id)), lua.lua())
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -622,6 +634,24 @@ macro_rules! lua_convert_int {
|
||||
message: Some("out of range".to_owned()),
|
||||
})
|
||||
}
|
||||
|
||||
unsafe fn from_stack(idx: c_int, lua: &RawLua) -> Result<Self> {
|
||||
let state = lua.state();
|
||||
let type_id = ffi::lua_type(state, idx);
|
||||
if type_id == ffi::LUA_TNUMBER {
|
||||
let mut ok = 0;
|
||||
let i = ffi::lua_tointegerx(state, idx, &mut ok);
|
||||
if ok != 0 {
|
||||
return cast(i).ok_or_else(|| Error::FromLuaConversionError {
|
||||
from: "integer",
|
||||
to: stringify!($x),
|
||||
message: Some("out of range".to_owned()),
|
||||
});
|
||||
}
|
||||
}
|
||||
// Fallback to default
|
||||
Self::from_lua(lua.stack_value(idx, Some(type_id)), lua.lua())
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -672,6 +702,24 @@ macro_rules! lua_convert_float {
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
unsafe fn from_stack(idx: c_int, lua: &RawLua) -> Result<Self> {
|
||||
let state = lua.state();
|
||||
let type_id = ffi::lua_type(state, idx);
|
||||
if type_id == ffi::LUA_TNUMBER {
|
||||
let mut ok = 0;
|
||||
let i = ffi::lua_tonumberx(state, idx, &mut ok);
|
||||
if ok != 0 {
|
||||
return cast(i).ok_or_else(|| Error::FromLuaConversionError {
|
||||
from: "number",
|
||||
to: stringify!($x),
|
||||
message: Some("out of range".to_owned()),
|
||||
});
|
||||
}
|
||||
}
|
||||
// Fallback to default
|
||||
Self::from_lua(lua.stack_value(idx, Some(type_id)), lua.lua())
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -893,10 +941,9 @@ impl<T: FromLua> FromLua for Option<T> {
|
||||
|
||||
#[inline]
|
||||
unsafe fn from_stack(idx: c_int, lua: &RawLua) -> Result<Self> {
|
||||
if ffi::lua_isnil(lua.state(), idx) != 0 {
|
||||
Ok(None)
|
||||
} else {
|
||||
Ok(Some(T::from_stack(idx, lua)?))
|
||||
match ffi::lua_type(lua.state(), idx) {
|
||||
ffi::LUA_TNIL => Ok(None),
|
||||
_ => Ok(Some(T::from_stack(idx, lua)?)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+5
-5
@@ -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"),
|
||||
|
||||
+16
-18
@@ -75,7 +75,7 @@ impl Function {
|
||||
///
|
||||
/// let tostring: Function = globals.get("tostring")?;
|
||||
///
|
||||
/// assert_eq!(tostring.call::<_, String>(123)?, "123");
|
||||
/// assert_eq!(tostring.call::<String>(123)?, "123");
|
||||
///
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
@@ -94,12 +94,12 @@ impl Function {
|
||||
/// 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, R: FromLuaMulti>(&self, args: A) -> Result<R> {
|
||||
pub fn call<R: FromLuaMulti>(&self, args: impl IntoLuaMulti) -> Result<R> {
|
||||
let lua = self.0.lua.lock();
|
||||
let state = lua.state();
|
||||
unsafe {
|
||||
@@ -153,9 +153,8 @@ impl Function {
|
||||
/// [`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>>
|
||||
pub fn call_async<R>(&self, args: impl IntoLuaMulti) -> impl Future<Output = Result<R>>
|
||||
where
|
||||
A: IntoLuaMulti,
|
||||
R: FromLuaMulti,
|
||||
{
|
||||
let lua = self.0.lua.lock();
|
||||
@@ -188,15 +187,15 @@ impl Function {
|
||||
/// "#).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>(&self, args: A) -> Result<Function> {
|
||||
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;
|
||||
@@ -514,10 +513,10 @@ impl PartialEq for Function {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct WrappedFunction(pub(crate) Callback<'static>);
|
||||
pub(crate) struct WrappedFunction(pub(crate) Callback);
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
pub(crate) struct WrappedAsyncFunction(pub(crate) AsyncCallback<'static>);
|
||||
pub(crate) struct WrappedAsyncFunction(pub(crate) AsyncCallback);
|
||||
|
||||
impl Function {
|
||||
/// Wraps a Rust function or closure, returning an opaque type that implements [`IntoLua`]
|
||||
@@ -559,18 +558,17 @@ impl Function {
|
||||
where
|
||||
A: FromLuaMulti,
|
||||
R: IntoLuaMulti,
|
||||
F: Fn(&Lua, A) -> FR + MaybeSend + 'static,
|
||||
FR: Future<Output = Result<R>> + 'static,
|
||||
F: Fn(Lua, A) -> FR + MaybeSend + 'static,
|
||||
FR: Future<Output = Result<R>> + MaybeSend + 'static,
|
||||
{
|
||||
WrappedAsyncFunction(Box::new(move |rawlua, args| unsafe {
|
||||
let lua = rawlua.lua();
|
||||
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::ready(Err(e))),
|
||||
};
|
||||
let fut = func(lua, args);
|
||||
let weak = rawlua.weak().clone();
|
||||
Box::pin(async move { fut.await?.push_into_stack_multi(&weak.lock()) })
|
||||
let lua = rawlua.lua().clone();
|
||||
let fut = func(lua.clone(), args);
|
||||
Box::pin(async move { fut.await?.push_into_stack_multi(lua.raw_lua()) })
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
+10
-5
@@ -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]
|
||||
@@ -94,6 +93,7 @@ mod stdlib;
|
||||
mod string;
|
||||
mod table;
|
||||
mod thread;
|
||||
mod traits;
|
||||
mod types;
|
||||
mod userdata;
|
||||
mod util;
|
||||
@@ -101,6 +101,7 @@ mod value;
|
||||
|
||||
pub mod prelude;
|
||||
|
||||
pub use bstr::BString;
|
||||
pub use ffi::{self, lua_CFunction, lua_State};
|
||||
|
||||
pub use crate::chunk::{AsChunk, Chunk, ChunkMode};
|
||||
@@ -112,12 +113,13 @@ pub use crate::state::{GCMode, Lua, LuaOptions};
|
||||
// pub use crate::scope::Scope;
|
||||
pub use crate::stdlib::StdLib;
|
||||
pub use crate::string::{BorrowedBytes, BorrowedStr, String};
|
||||
pub use crate::table::{Table, TableExt, TablePairs, TableSequence};
|
||||
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, AnyUserDataExt, MetaMethod, UserData, UserDataFields, UserDataMetatable, UserDataMethods,
|
||||
UserDataRef, UserDataRefMut, UserDataRegistry,
|
||||
AnyUserData, MetaMethod, UserData, UserDataFields, UserDataMetatable, UserDataMethods, UserDataRef,
|
||||
UserDataRefMut, UserDataRegistry,
|
||||
};
|
||||
pub use crate::value::{FromLua, FromLuaMulti, IntoLua, IntoLuaMulti, MultiValue, Nil, Value};
|
||||
|
||||
@@ -257,6 +259,9 @@ pub use mlua_derive::FromLua;
|
||||
#[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::*;
|
||||
|
||||
|
||||
+19
-17
@@ -9,7 +9,6 @@ use crate::chunk::ChunkMode;
|
||||
use crate::error::Result;
|
||||
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)?));
|
||||
|
||||
// Set `package.path`
|
||||
let mut search_path = env::var("LUAU_PATH")
|
||||
@@ -81,9 +76,15 @@ 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)?;
|
||||
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)?])?;
|
||||
@@ -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,10 +221,10 @@ 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()) {
|
||||
|
||||
+6
-8
@@ -32,7 +32,7 @@ impl<E: IntoLua> IntoLuaMulti for StdResult<(), E> {
|
||||
#[inline]
|
||||
fn into_lua_multi(self, lua: &Lua) -> Result<MultiValue> {
|
||||
match self {
|
||||
Ok(_) => Ok(MultiValue::new()),
|
||||
Ok(_) => const { Ok(MultiValue::new()) },
|
||||
Err(err) => (Nil, err).into_lua_multi(lua),
|
||||
}
|
||||
}
|
||||
@@ -49,7 +49,7 @@ impl<E: IntoLua> IntoLuaMulti for StdResult<(), E> {
|
||||
impl<T: IntoLua> IntoLuaMulti for T {
|
||||
#[inline]
|
||||
fn into_lua_multi(self, lua: &Lua) -> Result<MultiValue> {
|
||||
let mut v = MultiValue::with_lua_and_capacity(lua, 1);
|
||||
let mut v = MultiValue::with_capacity(1);
|
||||
v.push_back(self.into_lua(lua)?);
|
||||
Ok(v)
|
||||
}
|
||||
@@ -141,7 +141,7 @@ impl<T> Variadic<T> {
|
||||
|
||||
impl<T> Default for Variadic<T> {
|
||||
fn default() -> Variadic<T> {
|
||||
Variadic::new()
|
||||
const { Variadic::new() }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -177,9 +177,7 @@ impl<T> DerefMut for Variadic<T> {
|
||||
impl<T: IntoLua> IntoLuaMulti for Variadic<T> {
|
||||
#[inline]
|
||||
fn into_lua_multi(self, lua: &Lua) -> Result<MultiValue> {
|
||||
let mut values = MultiValue::with_lua_and_capacity(lua, self.0.len());
|
||||
values.extend_from_values(self.0.into_iter().map(|val| val.into_lua(lua)))?;
|
||||
Ok(values)
|
||||
MultiValue::from_lua_iter(lua, self)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -198,8 +196,8 @@ macro_rules! impl_tuple {
|
||||
() => (
|
||||
impl IntoLuaMulti for () {
|
||||
#[inline]
|
||||
fn into_lua_multi(self, lua: &Lua) -> Result<MultiValue> {
|
||||
Ok(MultiValue::with_lua_and_capacity(lua, 0))
|
||||
fn into_lua_multi(self, _: &Lua) -> Result<MultiValue> {
|
||||
const { Ok(MultiValue::new()) }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
||||
+10
-10
@@ -2,17 +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,
|
||||
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, 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,
|
||||
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"))]
|
||||
|
||||
+39
-17
@@ -51,7 +51,7 @@ pub struct Options {
|
||||
|
||||
impl Default for Options {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
const { Self::new() }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -504,13 +504,8 @@ struct MapDeserializer<'a> {
|
||||
processed: usize,
|
||||
}
|
||||
|
||||
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>,
|
||||
{
|
||||
impl<'a> MapDeserializer<'a> {
|
||||
fn next_key_deserializer(&mut self) -> Result<Option<Deserializer>> {
|
||||
loop {
|
||||
match self.pairs.next() {
|
||||
Some(item) => {
|
||||
@@ -526,23 +521,45 @@ impl<'de> de::MapAccess<'de> for MapDeserializer<'_> {
|
||||
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),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -702,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())),
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -52,7 +52,7 @@ pub struct Options {
|
||||
|
||||
impl Default for Options {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
const { Self::new() }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+80
-87
@@ -1,6 +1,5 @@
|
||||
use std::any::TypeId;
|
||||
use std::cell::RefCell;
|
||||
// use std::collections::VecDeque;
|
||||
use std::marker::PhantomData;
|
||||
use std::ops::Deref;
|
||||
use std::os::raw::{c_int, c_void};
|
||||
@@ -44,11 +43,13 @@ use util::{callback_error_ext, StateGuard};
|
||||
|
||||
/// Top level Lua struct which represents an instance of Lua VM.
|
||||
#[derive(Clone)]
|
||||
#[repr(transparent)]
|
||||
pub struct Lua(XRc<ReentrantMutex<RawLua>>);
|
||||
pub struct Lua {
|
||||
pub(self) raw: XRc<ReentrantMutex<RawLua>>,
|
||||
// Controls whether garbage collection should be run on drop
|
||||
pub(self) collect_garbage: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
#[repr(transparent)]
|
||||
pub(crate) struct WeakLua(XWeak<ReentrantMutex<RawLua>>);
|
||||
|
||||
pub(crate) struct LuaGuard(ArcReentrantMutexGuard<RawLua>);
|
||||
@@ -103,7 +104,7 @@ pub struct LuaOptions {
|
||||
|
||||
impl Default for LuaOptions {
|
||||
fn default() -> Self {
|
||||
LuaOptions::new()
|
||||
const { LuaOptions::new() }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -138,10 +139,11 @@ impl LuaOptions {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "module"))]
|
||||
impl Drop for Lua {
|
||||
fn drop(&mut self) {
|
||||
let _ = self.gc_collect();
|
||||
if self.collect_garbage {
|
||||
let _ = self.gc_collect();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -250,7 +252,10 @@ impl Lua {
|
||||
|
||||
/// Creates a new Lua state with required `libs` and `options`
|
||||
unsafe fn inner_new(libs: StdLib, options: LuaOptions) -> Lua {
|
||||
let lua = Lua(RawLua::new(libs, options));
|
||||
let lua = Lua {
|
||||
raw: RawLua::new(libs, options),
|
||||
collect_garbage: true,
|
||||
};
|
||||
|
||||
#[cfg(feature = "luau")]
|
||||
mlua_expect!(lua.configure_luau(), "Error configuring Luau");
|
||||
@@ -265,7 +270,10 @@ impl Lua {
|
||||
#[allow(clippy::missing_safety_doc)]
|
||||
#[inline]
|
||||
pub unsafe fn init_from_ptr(state: *mut ffi::lua_State) -> Lua {
|
||||
Lua(RawLua::init_from_ptr(state))
|
||||
Lua {
|
||||
raw: RawLua::init_from_ptr(state, false),
|
||||
collect_garbage: true,
|
||||
}
|
||||
}
|
||||
|
||||
/// FIXME: Deprecated load_from_std_lib
|
||||
@@ -344,57 +352,28 @@ impl Lua {
|
||||
loaded.raw_set(modname, Nil)
|
||||
}
|
||||
|
||||
/// Consumes and leaks `Lua` object, returning a static reference `&'static Lua`.
|
||||
///
|
||||
/// This function is useful when the `Lua` object is supposed to live for the remainder
|
||||
/// of the program's life.
|
||||
///
|
||||
/// Dropping the returned reference will cause a memory leak. If this is not acceptable,
|
||||
/// the reference should first be wrapped with the [`Lua::from_static`] function producing a
|
||||
/// `Lua`. This `Lua` object can then be dropped which will properly release the allocated
|
||||
/// memory.
|
||||
///
|
||||
/// [`Lua::from_static`]: #method.from_static
|
||||
///
|
||||
/// FIXME: remove
|
||||
#[doc(hidden)]
|
||||
pub fn into_static(self) -> &'static Self {
|
||||
Box::leak(Box::new(self))
|
||||
}
|
||||
|
||||
/// Constructs a `Lua` from a static reference to it.
|
||||
///
|
||||
/// # Safety
|
||||
/// This function is unsafe because improper use may lead to memory problems or undefined
|
||||
/// behavior.
|
||||
///
|
||||
/// FIXME: remove
|
||||
#[doc(hidden)]
|
||||
pub unsafe fn from_static(lua: &'static Lua) -> Self {
|
||||
*Box::from_raw(lua as *const Lua as *mut Lua)
|
||||
}
|
||||
|
||||
// Executes module entrypoint function, which returns only one Value.
|
||||
// The returned value then pushed onto the stack.
|
||||
#[doc(hidden)]
|
||||
#[cfg(not(tarpaulin_include))]
|
||||
pub unsafe fn entrypoint<F, A, R>(self, state: *mut ffi::lua_State, func: F) -> c_int
|
||||
pub unsafe fn entrypoint<F, A, R>(state: *mut ffi::lua_State, func: F) -> c_int
|
||||
where
|
||||
F: Fn(&Lua, A) -> Result<R> + MaybeSend + 'static,
|
||||
F: FnOnce(&Lua, A) -> Result<R>,
|
||||
A: FromLuaMulti,
|
||||
R: IntoLua,
|
||||
{
|
||||
let extra = self.lock().extra.get();
|
||||
// `self` is no longer needed and must be dropped at this point to avoid possible memory leak
|
||||
// Make sure that Lua is initialized
|
||||
let mut lua = Self::init_from_ptr(state);
|
||||
lua.collect_garbage = false;
|
||||
// `Lua` is no longer needed and must be dropped at this point to avoid possible memory leak
|
||||
// in case of possible longjmp (lua_error) below
|
||||
drop(self);
|
||||
drop(lua);
|
||||
|
||||
callback_error_ext(state, extra, move |nargs| {
|
||||
let lua = (*extra).lua();
|
||||
let rawlua = lua.lock();
|
||||
let _guard = StateGuard::new(&rawlua, state);
|
||||
let args = A::from_stack_args(nargs, 1, None, &rawlua)?;
|
||||
func(lua, args)?.push_into_stack(&rawlua)?;
|
||||
callback_error_ext(state, ptr::null_mut(), move |extra, nargs| {
|
||||
let rawlua = (*extra).raw_lua();
|
||||
let _guard = StateGuard::new(rawlua, state);
|
||||
let args = A::from_stack_args(nargs, 1, None, rawlua)?;
|
||||
func(rawlua.lua(), args)?.push_into_stack(rawlua)?;
|
||||
Ok(1)
|
||||
})
|
||||
}
|
||||
@@ -402,12 +381,12 @@ impl Lua {
|
||||
// A simple module entrypoint without arguments
|
||||
#[doc(hidden)]
|
||||
#[cfg(not(tarpaulin_include))]
|
||||
pub unsafe fn entrypoint1<R, F>(self, state: *mut ffi::lua_State, func: F) -> c_int
|
||||
pub unsafe fn entrypoint1<F, R>(state: *mut ffi::lua_State, func: F) -> c_int
|
||||
where
|
||||
F: FnOnce(&Lua) -> Result<R>,
|
||||
R: IntoLua,
|
||||
F: Fn(&Lua) -> Result<R> + MaybeSend + 'static,
|
||||
{
|
||||
self.entrypoint(state, move |lua, _: ()| func(lua))
|
||||
Self::entrypoint(state, move |lua, _: ()| func(lua))
|
||||
}
|
||||
|
||||
/// Skips memory checks for some operations.
|
||||
@@ -436,11 +415,11 @@ impl Lua {
|
||||
///
|
||||
/// lua.sandbox(true)?;
|
||||
/// lua.load("var = 123").exec()?;
|
||||
/// assert_eq!(lua.globals().get::<_, u32>("var")?, 123);
|
||||
/// assert_eq!(lua.globals().get::<u32>("var")?, 123);
|
||||
///
|
||||
/// // Restore the global environment (clear changes made in sandbox)
|
||||
/// lua.sandbox(false)?;
|
||||
/// assert_eq!(lua.globals().get::<_, Option<u32>>("var")?, None);
|
||||
/// assert_eq!(lua.globals().get::<Option<u32>>("var")?, None);
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
@@ -597,8 +576,7 @@ impl Lua {
|
||||
// We don't support GC interrupts since they cannot survive Lua exceptions
|
||||
return;
|
||||
}
|
||||
let extra = ExtraData::get(state);
|
||||
let result = callback_error_ext(state, extra, move |_| {
|
||||
let result = callback_error_ext(state, ptr::null_mut(), move |extra, _| {
|
||||
let interrupt_cb = (*extra).interrupt_callback.clone();
|
||||
let interrupt_cb = mlua_expect!(interrupt_cb, "no interrupt callback set in interrupt_proc");
|
||||
if Rc::strong_count(&interrupt_cb) > 2 {
|
||||
@@ -651,7 +629,7 @@ impl Lua {
|
||||
|
||||
unsafe extern "C-unwind" fn warn_proc(ud: *mut c_void, msg: *const c_char, tocont: c_int) {
|
||||
let extra = ud as *mut ExtraData;
|
||||
callback_error_ext((*extra).raw_lua().state(), extra, |_| {
|
||||
callback_error_ext((*extra).raw_lua().state(), extra, |extra, _| {
|
||||
let cb = mlua_expect!(
|
||||
(*extra).warn_callback.as_ref(),
|
||||
"no warning callback set in warn_proc"
|
||||
@@ -1149,8 +1127,8 @@ impl Lua {
|
||||
|
||||
/// Wraps a Rust async function or closure, creating a callable Lua function handle to it.
|
||||
///
|
||||
/// While executing the function Rust will poll Future and if the result is not ready, call
|
||||
/// `yield()` passing internal representation of a `Poll::Pending` value.
|
||||
/// While executing the function Rust will poll the Future and if the result is not ready,
|
||||
/// call `yield()` passing internal representation of a `Poll::Pending` value.
|
||||
///
|
||||
/// The function must be called inside Lua coroutine ([`Thread`]) to be able to suspend its
|
||||
/// execution. An executor should be used to poll [`AsyncThread`] and mlua will take a
|
||||
@@ -1169,7 +1147,7 @@ impl Lua {
|
||||
/// use std::time::Duration;
|
||||
/// use mlua::{Lua, Result};
|
||||
///
|
||||
/// async fn sleep(_lua: &Lua, n: u64) -> Result<&'static str> {
|
||||
/// async fn sleep(_lua: Lua, n: u64) -> Result<&'static str> {
|
||||
/// tokio::time::sleep(Duration::from_millis(n)).await;
|
||||
/// Ok("done")
|
||||
/// }
|
||||
@@ -1188,22 +1166,23 @@ impl Lua {
|
||||
/// [`AsyncThread`]: crate::AsyncThread
|
||||
#[cfg(feature = "async")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
|
||||
pub fn create_async_function<'lua, 'a, F, A, FR, R>(&'lua self, func: F) -> Result<Function>
|
||||
pub fn create_async_function<F, A, FR, R>(&self, func: F) -> Result<Function>
|
||||
where
|
||||
'lua: 'a,
|
||||
F: Fn(&'a Lua, A) -> FR + MaybeSend + 'static,
|
||||
F: Fn(Lua, A) -> FR + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
FR: Future<Output = Result<R>> + 'a,
|
||||
FR: Future<Output = Result<R>> + MaybeSend + 'static,
|
||||
R: IntoLuaMulti,
|
||||
{
|
||||
(self.lock()).create_async_callback(Box::new(move |rawlua, args| unsafe {
|
||||
let lua = rawlua.lua();
|
||||
let args = match A::from_lua_args(args, 1, None, lua) {
|
||||
// In future we should switch to async closures when they are stable to capture `&Lua`
|
||||
// See https://rust-lang.github.io/rfcs/3668-async-closures.html
|
||||
(self.lock()).create_async_callback(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::ready(Err(e))),
|
||||
};
|
||||
let fut = func(lua, args);
|
||||
Box::pin(async move { fut.await?.push_into_stack_multi(rawlua) })
|
||||
let lua = rawlua.lua();
|
||||
let fut = func(lua.clone(), args);
|
||||
Box::pin(async move { fut.await?.push_into_stack_multi(lua.raw_lua()) })
|
||||
}))
|
||||
}
|
||||
|
||||
@@ -1272,7 +1251,7 @@ impl Lua {
|
||||
///
|
||||
/// This methods provides a way to add fields or methods to userdata objects of a type `T`.
|
||||
pub fn register_userdata_type<T: 'static>(&self, f: impl FnOnce(&mut UserDataRegistry<T>)) -> Result<()> {
|
||||
let mut registry = UserDataRegistry::new();
|
||||
let mut registry = const { UserDataRegistry::new() };
|
||||
f(&mut registry);
|
||||
|
||||
let lua = self.lock();
|
||||
@@ -1306,11 +1285,11 @@ impl Lua {
|
||||
/// struct MyUserData(i32);
|
||||
///
|
||||
/// impl UserData for MyUserData {
|
||||
/// fn add_fields<'a, F: UserDataFields<'a, Self>>(fields: &mut F) {
|
||||
/// fn add_fields<F: UserDataFields<Self>>(fields: &mut F) {
|
||||
/// fields.add_field_method_get("val", |_, this| Ok(this.0));
|
||||
/// }
|
||||
///
|
||||
/// fn add_methods<'a, M: UserDataMethods<'a, Self>>(methods: &mut M) {
|
||||
/// fn add_methods<M: UserDataMethods<Self>>(methods: &mut M) {
|
||||
/// methods.add_function("new", |_, value: i32| Ok(MyUserData(value)));
|
||||
/// }
|
||||
/// }
|
||||
@@ -1331,12 +1310,12 @@ impl Lua {
|
||||
}
|
||||
|
||||
/// Sets the metatable for a Luau builtin vector type.
|
||||
#[cfg(any(all(feature = "luau", feature = "unstable"), doc))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all(feature = "luau", feature = "unstable"))))]
|
||||
#[cfg(any(feature = "luau", doc))]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
|
||||
pub fn set_vector_metatable(&self, metatable: Option<Table>) {
|
||||
let lua = self.lock();
|
||||
let state = lua.state();
|
||||
unsafe {
|
||||
let state = lua.state();
|
||||
let _sg = StackGuard::new(state);
|
||||
assert_stack(state, 2);
|
||||
|
||||
@@ -1497,7 +1476,7 @@ impl Lua {
|
||||
|
||||
/// Converts a value that implements `IntoLua` into a `Value` instance.
|
||||
#[inline]
|
||||
pub fn pack<T: IntoLua>(&self, t: T) -> Result<Value> {
|
||||
pub fn pack(&self, t: impl IntoLua) -> Result<Value> {
|
||||
t.into_lua(self)
|
||||
}
|
||||
|
||||
@@ -1509,7 +1488,7 @@ impl Lua {
|
||||
|
||||
/// Converts a value that implements `IntoLuaMulti` into a `MultiValue` instance.
|
||||
#[inline]
|
||||
pub fn pack_multi<T: IntoLuaMulti>(&self, t: T) -> Result<MultiValue> {
|
||||
pub fn pack_multi(&self, t: impl IntoLuaMulti) -> Result<MultiValue> {
|
||||
t.into_lua_multi(self)
|
||||
}
|
||||
|
||||
@@ -1523,10 +1502,7 @@ impl Lua {
|
||||
///
|
||||
/// This value will be available to rust from all `Lua` instances which share the same main
|
||||
/// state.
|
||||
pub fn set_named_registry_value<T>(&self, name: &str, t: T) -> Result<()>
|
||||
where
|
||||
T: IntoLua,
|
||||
{
|
||||
pub fn set_named_registry_value(&self, name: &str, t: impl IntoLua) -> Result<()> {
|
||||
let lua = self.lock();
|
||||
let state = lua.state();
|
||||
unsafe {
|
||||
@@ -1575,7 +1551,7 @@ impl Lua {
|
||||
/// Be warned, garbage collection of values held inside the registry is not automatic, see
|
||||
/// [`RegistryKey`] for more details.
|
||||
/// However, dropped [`RegistryKey`]s automatically reused to store new values.
|
||||
pub fn create_registry_value<T: IntoLua>(&self, t: T) -> Result<RegistryKey> {
|
||||
pub fn create_registry_value(&self, t: impl IntoLua) -> Result<RegistryKey> {
|
||||
let lua = self.lock();
|
||||
let state = lua.state();
|
||||
unsafe {
|
||||
@@ -1657,7 +1633,7 @@ impl Lua {
|
||||
/// An identifier used in [`RegistryKey`] may possibly be changed to a new value.
|
||||
///
|
||||
/// See [`Lua::create_registry_value`] for more details.
|
||||
pub fn replace_registry_value<T: IntoLua>(&self, key: &mut RegistryKey, t: T) -> Result<()> {
|
||||
pub fn replace_registry_value(&self, key: &mut RegistryKey, t: impl IntoLua) -> Result<()> {
|
||||
let lua = self.lock();
|
||||
if !lua.owns_registry_value(key) {
|
||||
return Err(Error::MismatchedRegistryKey);
|
||||
@@ -1861,17 +1837,25 @@ impl Lua {
|
||||
|
||||
#[inline(always)]
|
||||
pub(crate) fn lock(&self) -> ReentrantMutexGuard<RawLua> {
|
||||
self.0.lock()
|
||||
self.raw.lock()
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub(crate) fn lock_arc(&self) -> LuaGuard {
|
||||
LuaGuard(self.0.lock_arc())
|
||||
LuaGuard(self.raw.lock_arc())
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub(crate) fn weak(&self) -> WeakLua {
|
||||
WeakLua(XRc::downgrade(&self.0))
|
||||
WeakLua(XRc::downgrade(&self.raw))
|
||||
}
|
||||
|
||||
/// Returns a handle to the unprotected Lua state without any synchronization.
|
||||
///
|
||||
/// This is useful where we know that the lock is already held by the caller.
|
||||
#[inline(always)]
|
||||
pub(crate) unsafe fn raw_lua(&self) -> &RawLua {
|
||||
&*self.raw.data_ptr()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1886,6 +1870,15 @@ impl WeakLua {
|
||||
pub(crate) fn try_lock(&self) -> Option<LuaGuard> {
|
||||
Some(LuaGuard::new(self.0.upgrade()?))
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
#[inline(always)]
|
||||
pub(crate) fn upgrade(&self) -> Lua {
|
||||
Lua {
|
||||
raw: self.0.upgrade().expect("Lua instance is destroyed"),
|
||||
collect_garbage: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for WeakLua {
|
||||
|
||||
+24
-25
@@ -1,10 +1,9 @@
|
||||
use std::any::TypeId;
|
||||
use std::cell::UnsafeCell;
|
||||
use std::rc::Rc;
|
||||
// use std::collections::VecDeque;
|
||||
use std::mem::{self, MaybeUninit};
|
||||
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;
|
||||
@@ -13,7 +12,7 @@ use rustc_hash::FxHashMap;
|
||||
use crate::error::Result;
|
||||
use crate::state::RawLua;
|
||||
use crate::stdlib::StdLib;
|
||||
use crate::types::{AppData, ReentrantMutex, XRc, XWeak};
|
||||
use crate::types::{AppData, ReentrantMutex, XRc};
|
||||
use crate::util::{get_internal_metatable, push_internal_userdata, TypeKey, WrappedFailure};
|
||||
|
||||
#[cfg(any(feature = "luau", doc))]
|
||||
@@ -28,15 +27,13 @@ use super::{Lua, WeakLua};
|
||||
static EXTRA_REGISTRY_KEY: u8 = 0;
|
||||
|
||||
const WRAPPED_FAILURE_POOL_SIZE: usize = 64;
|
||||
// const MULTIVALUE_POOL_SIZE: usize = 64;
|
||||
const REF_STACK_RESERVE: c_int = 1;
|
||||
|
||||
/// Data associated with the Lua state.
|
||||
pub(crate) struct ExtraData {
|
||||
// Same layout as `Lua`
|
||||
pub(super) lua: MaybeUninit<XRc<ReentrantMutex<RawLua>>>,
|
||||
// Same layout as `WeakLua`
|
||||
pub(super) weak: MaybeUninit<XWeak<ReentrantMutex<RawLua>>>,
|
||||
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>>,
|
||||
@@ -50,7 +47,7 @@ pub(crate) struct ExtraData {
|
||||
|
||||
pub(super) safe: bool,
|
||||
pub(super) libs: StdLib,
|
||||
#[cfg(feature = "module")]
|
||||
// Used in module mode
|
||||
pub(super) skip_memory_check: bool,
|
||||
|
||||
// Auxiliary thread to store references
|
||||
@@ -61,8 +58,6 @@ pub(crate) struct ExtraData {
|
||||
|
||||
// Pool of `WrappedFailure` enums in the ref thread (as userdata)
|
||||
pub(super) wrapped_failure_pool: Vec<c_int>,
|
||||
// Pool of `MultiValue` containers
|
||||
// multivalue_pool: Vec<VecDeque<Value>>,
|
||||
// Pool of `Thread`s (coroutines) for async execution
|
||||
#[cfg(feature = "async")]
|
||||
pub(super) thread_pool: Vec<c_int>,
|
||||
@@ -94,8 +89,9 @@ pub(crate) struct ExtraData {
|
||||
impl Drop for ExtraData {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
#[cfg(feature = "module")]
|
||||
self.lua.assume_init_drop();
|
||||
if !self.owned {
|
||||
self.lua.assume_init_drop();
|
||||
}
|
||||
|
||||
self.weak.assume_init_drop();
|
||||
}
|
||||
@@ -117,7 +113,7 @@ impl ExtraData {
|
||||
#[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) -> XRc<UnsafeCell<Self>> {
|
||||
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!(
|
||||
@@ -143,9 +139,11 @@ impl ExtraData {
|
||||
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),
|
||||
@@ -153,7 +151,6 @@ impl ExtraData {
|
||||
app_data: AppData::default(),
|
||||
safe: false,
|
||||
libs: StdLib::NONE,
|
||||
#[cfg(feature = "module")]
|
||||
skip_memory_check: false,
|
||||
ref_thread,
|
||||
// We need some reserved stack space to move values in and out of the ref stack.
|
||||
@@ -161,7 +158,6 @@ impl ExtraData {
|
||||
ref_stack_top: ffi::lua_gettop(ref_thread),
|
||||
ref_free: Vec::new(),
|
||||
wrapped_failure_pool: Vec::with_capacity(WRAPPED_FAILURE_POOL_SIZE),
|
||||
// multivalue_pool: Vec::with_capacity(MULTIVALUE_POOL_SIZE),
|
||||
#[cfg(feature = "async")]
|
||||
thread_pool: Vec::new(),
|
||||
wrapped_failure_mt_ptr,
|
||||
@@ -189,12 +185,15 @@ impl ExtraData {
|
||||
extra
|
||||
}
|
||||
|
||||
pub(super) unsafe fn set_lua(&mut self, lua: &XRc<ReentrantMutex<RawLua>>) {
|
||||
self.lua.write(XRc::clone(lua));
|
||||
if cfg!(not(feature = "module")) {
|
||||
XRc::decrement_strong_count(XRc::as_ptr(lua));
|
||||
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(XRc::downgrade(lua));
|
||||
self.weak.write(WeakLua(XRc::downgrade(raw)));
|
||||
}
|
||||
|
||||
pub(super) unsafe fn get(state: *mut ffi::lua_State) -> *mut Self {
|
||||
@@ -232,16 +231,16 @@ impl ExtraData {
|
||||
|
||||
#[inline(always)]
|
||||
pub(super) unsafe fn lua(&self) -> &Lua {
|
||||
mem::transmute(self.lua.assume_init_ref())
|
||||
self.lua.assume_init_ref()
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub(super) unsafe fn raw_lua(&self) -> &RawLua {
|
||||
&*self.lua.assume_init_ref().data_ptr()
|
||||
&*self.lua.assume_init_ref().raw.data_ptr()
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub(super) unsafe fn weak(&self) -> &WeakLua {
|
||||
mem::transmute(self.weak.assume_init_ref())
|
||||
self.weak.assume_init_ref()
|
||||
}
|
||||
}
|
||||
|
||||
+58
-89
@@ -3,7 +3,6 @@ use std::cell::{Cell, UnsafeCell};
|
||||
use std::ffi::{CStr, CString};
|
||||
use std::os::raw::{c_char, c_int, c_void};
|
||||
use std::panic::resume_unwind;
|
||||
use std::rc::Rc;
|
||||
use std::result::Result as StdResult;
|
||||
use std::sync::Arc;
|
||||
use std::{mem, ptr};
|
||||
@@ -51,10 +50,13 @@ pub struct RawLua {
|
||||
pub(super) extra: XRc<UnsafeCell<ExtraData>>,
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "module"))]
|
||||
impl Drop for RawLua {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
if !(*self.extra.get()).owned {
|
||||
return;
|
||||
}
|
||||
|
||||
let mem_state = MemoryState::get(self.main_state);
|
||||
|
||||
ffi::lua_close(self.main_state);
|
||||
@@ -116,7 +118,7 @@ impl RawLua {
|
||||
ffi::luau_codegen_create(state);
|
||||
}
|
||||
|
||||
let rawlua = Self::init_from_ptr(state);
|
||||
let rawlua = Self::init_from_ptr(state, true);
|
||||
let extra = rawlua.lock().extra.get();
|
||||
|
||||
mlua_expect!(
|
||||
@@ -155,7 +157,7 @@ impl RawLua {
|
||||
rawlua
|
||||
}
|
||||
|
||||
pub(super) unsafe fn init_from_ptr(state: *mut ffi::lua_State) -> XRc<ReentrantMutex<Self>> {
|
||||
pub(super) unsafe fn init_from_ptr(state: *mut ffi::lua_State, owned: bool) -> XRc<ReentrantMutex<Self>> {
|
||||
assert!(!state.is_null(), "Lua state is NULL");
|
||||
if let Some(lua) = Self::try_from_ptr(state) {
|
||||
return lua;
|
||||
@@ -192,7 +194,7 @@ impl RawLua {
|
||||
);
|
||||
|
||||
// Init ExtraData
|
||||
let extra = ExtraData::init(main_state);
|
||||
let extra = ExtraData::init(main_state, owned);
|
||||
|
||||
// Register `DestructedUserdata` type
|
||||
get_destructed_userdata_metatable(main_state);
|
||||
@@ -220,10 +222,10 @@ impl RawLua {
|
||||
rawlua
|
||||
}
|
||||
|
||||
pub(super) unsafe fn try_from_ptr(state: *mut ffi::lua_State) -> Option<XRc<ReentrantMutex<Self>>> {
|
||||
unsafe fn try_from_ptr(state: *mut ffi::lua_State) -> Option<XRc<ReentrantMutex<Self>>> {
|
||||
match ExtraData::get(state) {
|
||||
extra if extra.is_null() => None,
|
||||
extra => Some(XRc::clone(&(*extra).lua().0)),
|
||||
extra => Some(XRc::clone(&(*extra).lua().raw)),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -299,14 +301,14 @@ impl RawLua {
|
||||
pub(crate) fn load_chunk(
|
||||
&self,
|
||||
name: Option<&CStr>,
|
||||
env: Option<Table>,
|
||||
env: Option<&Table>,
|
||||
mode: Option<ChunkMode>,
|
||||
source: &[u8],
|
||||
) -> Result<Function> {
|
||||
let state = self.state();
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(state);
|
||||
check_stack(state, 1)?;
|
||||
check_stack(state, 2)?;
|
||||
|
||||
let mode_str = match mode {
|
||||
Some(ChunkMode::Binary) => cstr!("b"),
|
||||
@@ -314,22 +316,21 @@ impl RawLua {
|
||||
None => cstr!("bt"),
|
||||
};
|
||||
|
||||
match ffi::luaL_loadbufferx(
|
||||
match ffi::luaL_loadbufferenv(
|
||||
state,
|
||||
source.as_ptr() as *const c_char,
|
||||
source.len(),
|
||||
name.map(|n| n.as_ptr()).unwrap_or_else(ptr::null),
|
||||
mode_str,
|
||||
match env {
|
||||
Some(env) => {
|
||||
self.push_ref(&env.0);
|
||||
-1
|
||||
}
|
||||
_ => 0,
|
||||
},
|
||||
) {
|
||||
ffi::LUA_OK => {
|
||||
if let Some(env) = env {
|
||||
self.push_ref(&env.0);
|
||||
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
|
||||
ffi::lua_setupvalue(state, -2, 1);
|
||||
#[cfg(any(feature = "lua51", feature = "luajit", feature = "luau"))]
|
||||
ffi::lua_setfenv(state, -2);
|
||||
}
|
||||
|
||||
#[cfg(feature = "luau-jit")]
|
||||
if (*self.extra.get()).enable_jit && ffi::luau_codegen_supported() != 0 {
|
||||
ffi::luau_codegen_compile(state, -1);
|
||||
@@ -359,10 +360,10 @@ impl RawLua {
|
||||
ffi::lua_sethook(state, None, 0, 0);
|
||||
return;
|
||||
}
|
||||
callback_error_ext(state, extra, move |_| {
|
||||
callback_error_ext(state, extra, move |extra, _| {
|
||||
let hook_cb = (*extra).hook_callback.clone();
|
||||
let hook_cb = mlua_expect!(hook_cb, "no hook callback set in hook_proc");
|
||||
if Rc::strong_count(&hook_cb) > 2 {
|
||||
if std::rc::Rc::strong_count(&hook_cb) > 2 {
|
||||
return Ok(()); // Don't allow recursion
|
||||
}
|
||||
let rawlua = (*extra).raw_lua();
|
||||
@@ -372,7 +373,7 @@ impl RawLua {
|
||||
})
|
||||
}
|
||||
|
||||
(*self.extra.get()).hook_callback = Some(Rc::new(callback));
|
||||
(*self.extra.get()).hook_callback = Some(std::rc::Rc::new(callback));
|
||||
(*self.extra.get()).hook_thread = state; // Mark for what thread the hook is set
|
||||
ffi::lua_sethook(state, Some(hook_proc), triggers.mask(), triggers.count());
|
||||
}
|
||||
@@ -491,31 +492,12 @@ impl RawLua {
|
||||
#[cfg(feature = "luau")]
|
||||
ffi::lua_resetthread(thread_state);
|
||||
extra.thread_pool.push(thread.0.index);
|
||||
thread.0.drop = false;
|
||||
thread.0.drop = false; // Prevent thread from being garbage collected
|
||||
return true;
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
// FIXME
|
||||
// #[inline]
|
||||
// pub(crate) fn pop_multivalue_from_pool(&self) -> Option<VecDeque<Value>> {
|
||||
// let extra = unsafe { &mut *self.extra.get() };
|
||||
// extra.multivalue_pool.pop()
|
||||
// }
|
||||
|
||||
// FIXME
|
||||
// #[inline]
|
||||
// pub(crate) fn push_multivalue_to_pool(&self, mut multivalue: VecDeque<Value>) {
|
||||
// let extra = unsafe { &mut *self.extra.get() };
|
||||
// if extra.multivalue_pool.len() < MULTIVALUE_POOL_SIZE {
|
||||
// multivalue.clear();
|
||||
// extra
|
||||
// .multivalue_pool
|
||||
// .push(unsafe { mem::transmute(multivalue) });
|
||||
// }
|
||||
// }
|
||||
|
||||
/// Pushes a value that implements `IntoLua` onto the Lua stack.
|
||||
///
|
||||
/// Uses 2 stack spaces, does not call checkstack.
|
||||
@@ -560,7 +542,7 @@ impl RawLua {
|
||||
///
|
||||
/// Uses 2 stack spaces, does not call `checkstack`.
|
||||
pub(crate) unsafe fn pop_value(&self) -> Value {
|
||||
let value = self.stack_value(-1);
|
||||
let value = self.stack_value(-1, None);
|
||||
ffi::lua_pop(self.state(), 1);
|
||||
value
|
||||
}
|
||||
@@ -568,9 +550,9 @@ impl RawLua {
|
||||
/// Returns value at given stack index without popping it.
|
||||
///
|
||||
/// Uses 2 stack spaces, does not call checkstack.
|
||||
pub(crate) unsafe fn stack_value(&self, idx: c_int) -> Value {
|
||||
pub(crate) unsafe fn stack_value(&self, idx: c_int, type_hint: Option<c_int>) -> Value {
|
||||
let state = self.state();
|
||||
match ffi::lua_type(state, idx) {
|
||||
match type_hint.unwrap_or_else(|| ffi::lua_type(state, idx)) {
|
||||
ffi::LUA_TNIL => Nil,
|
||||
|
||||
ffi::LUA_TBOOLEAN => Value::Boolean(ffi::lua_toboolean(state, idx) != 0),
|
||||
@@ -725,10 +707,7 @@ impl RawLua {
|
||||
// MemoryInfo is empty in module mode so we cannot predict memory limits
|
||||
match MemoryState::get(self.main_state) {
|
||||
mem_state if !mem_state.is_null() => (*mem_state).memory_limit() == 0,
|
||||
#[cfg(feature = "module")]
|
||||
_ => (*self.extra.get()).skip_memory_check, // Check the special flag (only for module mode)
|
||||
#[cfg(not(feature = "module"))]
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -744,7 +723,7 @@ impl RawLua {
|
||||
}
|
||||
|
||||
// Create a new metatable from `UserData` definition
|
||||
let mut registry = UserDataRegistry::new();
|
||||
let mut registry = const { UserDataRegistry::new() };
|
||||
T::register(&mut registry);
|
||||
|
||||
self.register_userdata_metatable(registry)
|
||||
@@ -763,7 +742,7 @@ impl RawLua {
|
||||
}
|
||||
|
||||
// Create an empty metatable
|
||||
let registry = UserDataRegistry::new();
|
||||
let registry = const { UserDataRegistry::new() };
|
||||
self.register_userdata_metatable::<T>(registry)
|
||||
})
|
||||
}
|
||||
@@ -826,10 +805,9 @@ impl RawLua {
|
||||
rawset_field(state, -2, MetaMethod::validate(&k)?)?;
|
||||
}
|
||||
let mut has_name = false;
|
||||
for (k, f) in registry.meta_fields {
|
||||
for (k, push_field) in registry.meta_fields {
|
||||
has_name = has_name || k == MetaMethod::Type;
|
||||
let rawlua = mem::transmute::<&RawLua, &RawLua>(self);
|
||||
mlua_assert!(f(rawlua, 0)? == 1, "field function must return one value");
|
||||
push_field(self)?;
|
||||
rawset_field(state, -2, MetaMethod::validate(&k)?)?;
|
||||
}
|
||||
// Set `__name/__type` if not provided
|
||||
@@ -853,31 +831,38 @@ impl RawLua {
|
||||
ffi::lua_pop(state, 1);
|
||||
push_table(state, 0, fields_nrec, true)?;
|
||||
}
|
||||
for (k, f) in registry.fields {
|
||||
let rawlua = mem::transmute::<&RawLua, &RawLua>(self);
|
||||
mlua_assert!(f(rawlua, 0)? == 1, "field function must return one value");
|
||||
for (k, push_field) in mem::take(&mut registry.fields) {
|
||||
push_field(self)?;
|
||||
rawset_field(state, -2, &k)?;
|
||||
}
|
||||
rawset_field(state, metatable_index, "__index")?;
|
||||
}
|
||||
_ => {
|
||||
ffi::lua_pop(state, 1);
|
||||
// Propagate fields to the field getters
|
||||
for (k, f) in registry.fields {
|
||||
registry.field_getters.push((k, f))
|
||||
}
|
||||
// Fields will be converted to functions and added to field getters
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut field_getters_index = None;
|
||||
let field_getters_nrec = registry.field_getters.len();
|
||||
let field_getters_nrec = registry.field_getters.len() + registry.fields.len();
|
||||
if field_getters_nrec > 0 {
|
||||
push_table(state, 0, field_getters_nrec, true)?;
|
||||
for (k, m) in registry.field_getters {
|
||||
self.push(self.create_callback(m)?)?;
|
||||
rawset_field(state, -2, &k)?;
|
||||
}
|
||||
for (k, push_field) in registry.fields {
|
||||
unsafe extern "C-unwind" fn return_field(state: *mut ffi::lua_State) -> c_int {
|
||||
ffi::lua_pushvalue(state, ffi::lua_upvalueindex(1));
|
||||
1
|
||||
}
|
||||
push_field(self)?;
|
||||
protect_lua!(state, 1, 1, fn(state) {
|
||||
ffi::lua_pushcclosure(state, return_field, 1);
|
||||
})?;
|
||||
rawset_field(state, -2, &k)?;
|
||||
}
|
||||
field_getters_index = Some(ffi::lua_absindex(state, -1));
|
||||
extra_tables_count += 1;
|
||||
}
|
||||
@@ -1043,27 +1028,16 @@ impl RawLua {
|
||||
|
||||
// Creates a Function out of a Callback containing a 'static Fn.
|
||||
pub(crate) fn create_callback(&self, func: Callback) -> Result<Function> {
|
||||
// This is non-scoped version of the callback (upvalue is always valid)
|
||||
// TODO: add a scoped version
|
||||
unsafe extern "C-unwind" fn call_callback(state: *mut ffi::lua_State) -> c_int {
|
||||
// Normal functions can be scoped and therefore destroyed,
|
||||
// so we need to check that the first upvalue is valid
|
||||
let (upvalue, extra) = match ffi::lua_type(state, ffi::lua_upvalueindex(1)) {
|
||||
ffi::LUA_TUSERDATA => {
|
||||
let upvalue = get_userdata::<CallbackUpvalue>(state, ffi::lua_upvalueindex(1));
|
||||
(upvalue, (*upvalue).extra.get())
|
||||
}
|
||||
_ => (ptr::null_mut(), ptr::null_mut()),
|
||||
};
|
||||
callback_error_ext(state, extra, |nargs| {
|
||||
let upvalue = get_userdata::<CallbackUpvalue>(state, ffi::lua_upvalueindex(1));
|
||||
callback_error_ext(state, (*upvalue).extra.get(), |extra, nargs| {
|
||||
// Lua ensures that `LUA_MINSTACK` stack spaces are available (after pushing arguments)
|
||||
if upvalue.is_null() {
|
||||
return Err(Error::CallbackDestructed);
|
||||
}
|
||||
|
||||
// The lock must be already held as the callback is executed
|
||||
let rawlua = (*extra).raw_lua();
|
||||
let _guard = StateGuard::new(rawlua, state);
|
||||
let func = &*(*upvalue).data;
|
||||
|
||||
func(rawlua, nargs)
|
||||
})
|
||||
}
|
||||
@@ -1073,7 +1047,6 @@ impl RawLua {
|
||||
let _sg = StackGuard::new(state);
|
||||
check_stack(state, 4)?;
|
||||
|
||||
let func = mem::transmute::<Callback, Callback<'static>>(func);
|
||||
let extra = XRc::clone(&self.extra);
|
||||
let protect = !self.unlikely_memory_error();
|
||||
push_internal_userdata(state, CallbackUpvalue { data: func, extra }, protect)?;
|
||||
@@ -1091,6 +1064,7 @@ impl RawLua {
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
pub(crate) fn create_async_callback(&self, func: AsyncCallback) -> Result<Function> {
|
||||
// Ensure that the coroutine library is loaded
|
||||
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52", feature = "luau"))]
|
||||
unsafe {
|
||||
if !(*self.extra.get()).libs.contains(StdLib::COROUTINE) {
|
||||
@@ -1104,15 +1078,14 @@ impl RawLua {
|
||||
// so the first upvalue is always valid
|
||||
let upvalue = get_userdata::<AsyncCallbackUpvalue>(state, ffi::lua_upvalueindex(1));
|
||||
let extra = (*upvalue).extra.get();
|
||||
callback_error_ext(state, extra, |nargs| {
|
||||
callback_error_ext(state, extra, |extra, nargs| {
|
||||
// Lua ensures that `LUA_MINSTACK` stack spaces are available (after pushing arguments)
|
||||
// The lock must be already held as the callback is executed
|
||||
let rawlua = (*extra).raw_lua();
|
||||
let _guard = StateGuard::new(rawlua, state);
|
||||
|
||||
let args = MultiValue::from_stack_multi(nargs, rawlua)?;
|
||||
let func = &*(*upvalue).data;
|
||||
let fut = func(rawlua, args);
|
||||
let fut = func(rawlua, nargs);
|
||||
let extra = XRc::clone(&(*upvalue).extra);
|
||||
let protect = !rawlua.unlikely_memory_error();
|
||||
push_internal_userdata(state, AsyncPollUpvalue { data: fut, extra }, protect)?;
|
||||
@@ -1130,8 +1103,7 @@ impl RawLua {
|
||||
|
||||
unsafe extern "C-unwind" fn poll_future(state: *mut ffi::lua_State) -> c_int {
|
||||
let upvalue = get_userdata::<AsyncPollUpvalue>(state, ffi::lua_upvalueindex(1));
|
||||
let extra = (*upvalue).extra.get();
|
||||
callback_error_ext(state, extra, |_| {
|
||||
callback_error_ext(state, (*upvalue).extra.get(), |extra, _| {
|
||||
// Lua ensures that `LUA_MINSTACK` stack spaces are available (after pushing arguments)
|
||||
// The lock must be already held as the future is polled
|
||||
let rawlua = (*extra).raw_lua();
|
||||
@@ -1147,7 +1119,7 @@ impl RawLua {
|
||||
}
|
||||
Poll::Ready(nresults) => {
|
||||
match nresults? {
|
||||
nresults @ 0..=2 => {
|
||||
nresults if nresults < 3 => {
|
||||
// Fast path for up to 2 results without creating a table
|
||||
ffi::lua_pushinteger(state, nresults as _);
|
||||
if nresults > 0 {
|
||||
@@ -1172,7 +1144,6 @@ impl RawLua {
|
||||
let _sg = StackGuard::new(state);
|
||||
check_stack(state, 4)?;
|
||||
|
||||
let func = mem::transmute::<AsyncCallback, AsyncCallback<'static>>(func);
|
||||
let extra = XRc::clone(&self.extra);
|
||||
let protect = !self.unlikely_memory_error();
|
||||
let upvalue = AsyncCallbackUpvalue { data: func, extra };
|
||||
@@ -1198,15 +1169,13 @@ impl RawLua {
|
||||
}
|
||||
|
||||
let lua = self.lua();
|
||||
let coroutine = lua.globals().get::<_, Table>("coroutine")?;
|
||||
let coroutine = lua.globals().get::<Table>("coroutine")?;
|
||||
|
||||
// Prepare environment for the async poller
|
||||
let env = lua.create_table_with_capacity(0, 3)?;
|
||||
env.set("get_poll", get_poll)?;
|
||||
// Cache `yield` function
|
||||
env.set("yield", coroutine.get::<_, Function>("yield")?)?;
|
||||
unsafe {
|
||||
env.set("unpack", lua.create_c_function(unpack)?)?;
|
||||
}
|
||||
env.set("yield", coroutine.get::<Function>("yield")?)?;
|
||||
env.set("unpack", unsafe { lua.create_c_function(unpack)? })?;
|
||||
|
||||
lua.load(
|
||||
r#"
|
||||
|
||||
+2
-3
@@ -8,7 +8,6 @@ use crate::state::{ExtraData, RawLua};
|
||||
use crate::util::{self, get_internal_metatable, WrappedFailure};
|
||||
|
||||
const WRAPPED_FAILURE_POOL_SIZE: usize = 64;
|
||||
// const MULTIVALUE_POOL_SIZE: usize = 64;
|
||||
|
||||
pub(super) struct StateGuard<'a>(&'a RawLua, *mut ffi::lua_State);
|
||||
|
||||
@@ -33,7 +32,7 @@ pub(super) unsafe fn callback_error_ext<F, R>(
|
||||
f: F,
|
||||
) -> R
|
||||
where
|
||||
F: FnOnce(c_int) -> Result<R>,
|
||||
F: FnOnce(*mut ExtraData, c_int) -> Result<R>,
|
||||
{
|
||||
if extra.is_null() {
|
||||
extra = ExtraData::get(state);
|
||||
@@ -114,7 +113,7 @@ where
|
||||
// to store a wrapped failure (error or panic) *before* we proceed.
|
||||
let prealloc_failure = PreallocatedFailure::reserve(state, extra);
|
||||
|
||||
match catch_unwind(AssertUnwindSafe(|| f(nargs))) {
|
||||
match catch_unwind(AssertUnwindSafe(|| f(extra, nargs))) {
|
||||
Ok(Ok(r)) => {
|
||||
// Return unused `WrappedFailure` to the pool
|
||||
prealloc_failure.release(state, extra);
|
||||
|
||||
+13
-7
@@ -12,7 +12,7 @@ use {
|
||||
};
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
use crate::state::LuaGuard;
|
||||
use crate::state::Lua;
|
||||
use crate::types::ValueRef;
|
||||
|
||||
/// Handle to an internal Lua string.
|
||||
@@ -103,9 +103,10 @@ impl String {
|
||||
BorrowedBytes(bytes, guard)
|
||||
}
|
||||
|
||||
unsafe fn to_slice(&self) -> (&[u8], LuaGuard) {
|
||||
let lua = self.0.lua.lock();
|
||||
let ref_thread = lua.ref_thread();
|
||||
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,
|
||||
@@ -117,6 +118,7 @@ impl String {
|
||||
// string type
|
||||
let data = ffi::lua_tolstring(ref_thread, self.0.index, &mut size);
|
||||
|
||||
drop(rawlua);
|
||||
(slice::from_raw_parts(data as *const u8, size + 1), lua)
|
||||
}
|
||||
}
|
||||
@@ -211,7 +213,7 @@ impl Serialize for String {
|
||||
}
|
||||
|
||||
/// A borrowed string (`&str`) that holds a strong reference to the Lua state.
|
||||
pub struct BorrowedStr<'a>(&'a str, #[allow(unused)] LuaGuard);
|
||||
pub struct BorrowedStr<'a>(&'a str, #[allow(unused)] Lua);
|
||||
|
||||
impl Deref for BorrowedStr<'_> {
|
||||
type Target = str;
|
||||
@@ -267,7 +269,7 @@ where
|
||||
}
|
||||
|
||||
/// A borrowed byte slice (`&[u8]`) that holds a strong reference to the Lua state.
|
||||
pub struct BorrowedBytes<'a>(&'a [u8], #[allow(unused)] LuaGuard);
|
||||
pub struct BorrowedBytes<'a>(&'a [u8], #[allow(unused)] Lua);
|
||||
|
||||
impl Deref for BorrowedBytes<'_> {
|
||||
type Target = [u8];
|
||||
@@ -321,7 +323,7 @@ impl<'a> IntoIterator for BorrowedBytes<'a> {
|
||||
type IntoIter = slice::Iter<'a, u8>;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.0.into_iter()
|
||||
self.0.iter()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -333,4 +335,8 @@ mod assertions {
|
||||
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);
|
||||
}
|
||||
|
||||
+78
-128
@@ -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,14 +13,14 @@ use {
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
use crate::function::Function;
|
||||
use crate::private::Sealed;
|
||||
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 std::future::Future;
|
||||
use futures_util::future::{self, Either, Future};
|
||||
|
||||
/// Handle to an internal Lua table.
|
||||
#[derive(Clone)]
|
||||
@@ -59,12 +60,16 @@ impl Table {
|
||||
/// ```
|
||||
///
|
||||
/// [`raw_set`]: #method.raw_set
|
||||
pub fn set<K: IntoLua, V: IntoLua>(&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);
|
||||
}
|
||||
|
||||
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 {
|
||||
@@ -102,12 +107,16 @@ impl Table {
|
||||
/// ```
|
||||
///
|
||||
/// [`raw_get`]: #method.raw_get
|
||||
pub fn get<K: IntoLua, V: FromLua>(&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);
|
||||
}
|
||||
|
||||
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 {
|
||||
@@ -125,15 +134,15 @@ impl Table {
|
||||
/// Checks whether the table contains a non-nil value for `key`.
|
||||
///
|
||||
/// This might invoke the `__index` metamethod.
|
||||
pub fn contains_key<K: IntoLua>(&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>(&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);
|
||||
}
|
||||
@@ -158,7 +167,7 @@ impl Table {
|
||||
///
|
||||
/// This might invoke the `__len` and `__newindex` metamethods.
|
||||
pub fn pop<V: FromLua>(&self) -> Result<V> {
|
||||
// Fast track
|
||||
// Fast track (skip protected call)
|
||||
if !self.has_metatable() {
|
||||
return self.raw_pop();
|
||||
}
|
||||
@@ -220,12 +229,12 @@ impl Table {
|
||||
// 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, other));
|
||||
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, other));
|
||||
return mt.get::<Function>("__eq")?.call((self, other));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -233,7 +242,7 @@ impl Table {
|
||||
}
|
||||
|
||||
/// Sets a key-value pair without invoking metamethods.
|
||||
pub fn raw_set<K: IntoLua, V: IntoLua>(&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()?;
|
||||
|
||||
@@ -258,7 +267,7 @@ impl Table {
|
||||
}
|
||||
|
||||
/// Gets the value associated to `key` without invoking metamethods.
|
||||
pub fn raw_get<K: IntoLua, V: FromLua>(&self, key: K) -> Result<V> {
|
||||
pub fn raw_get<V: FromLua>(&self, key: impl IntoLua) -> Result<V> {
|
||||
let lua = self.0.lua.lock();
|
||||
let state = lua.state();
|
||||
unsafe {
|
||||
@@ -275,7 +284,7 @@ impl Table {
|
||||
|
||||
/// Inserts element value at position `idx` to the table, shifting up the elements from
|
||||
/// `table[idx]`. The worst case complexity is O(n), where n is the table length.
|
||||
pub fn raw_insert<V: IntoLua>(&self, idx: Integer, value: V) -> Result<()> {
|
||||
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"));
|
||||
@@ -301,7 +310,7 @@ impl Table {
|
||||
}
|
||||
|
||||
/// Appends a value to the back of the table without invoking metamethods.
|
||||
pub fn raw_push<V: IntoLua>(&self, value: V) -> Result<()> {
|
||||
pub fn raw_push(&self, value: impl IntoLua) -> Result<()> {
|
||||
#[cfg(feature = "luau")]
|
||||
self.check_readonly_write()?;
|
||||
|
||||
@@ -357,7 +366,7 @@ impl Table {
|
||||
/// where n is the table length.
|
||||
///
|
||||
/// For other key types this is equivalent to setting `table[key] = nil`.
|
||||
pub fn raw_remove<K: IntoLua>(&self, key: K) -> Result<()> {
|
||||
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.lua())?;
|
||||
@@ -433,7 +442,7 @@ impl Table {
|
||||
///
|
||||
/// [`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);
|
||||
}
|
||||
@@ -710,7 +719,7 @@ impl Table {
|
||||
|
||||
/// Sets element value at position `idx` without invoking metamethods.
|
||||
#[doc(hidden)]
|
||||
pub fn raw_seti<V: IntoLua>(&self, idx: usize, value: V) -> Result<()> {
|
||||
pub fn raw_seti(&self, idx: usize, value: impl IntoLua) -> Result<()> {
|
||||
#[cfg(feature = "luau")]
|
||||
self.check_readonly_write()?;
|
||||
|
||||
@@ -858,141 +867,82 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
/// An extension trait for `Table`s that provides a variety of convenient functionality.
|
||||
pub trait TableExt: 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,
|
||||
R: FromLuaMulti;
|
||||
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) -> impl Future<Output = Result<R>>
|
||||
where
|
||||
A: IntoLuaMulti,
|
||||
R: FromLuaMulti;
|
||||
#[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,
|
||||
R: FromLuaMulti;
|
||||
|
||||
/// 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,
|
||||
R: FromLuaMulti;
|
||||
|
||||
/// 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) -> impl Future<Output = Result<R>>
|
||||
where
|
||||
A: IntoLuaMulti,
|
||||
R: FromLuaMulti;
|
||||
|
||||
/// 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) -> impl Future<Output = Result<R>>
|
||||
where
|
||||
A: IntoLuaMulti,
|
||||
R: FromLuaMulti;
|
||||
}
|
||||
|
||||
impl TableExt for Table {
|
||||
fn call<A, R>(&self, args: A) -> Result<R>
|
||||
where
|
||||
A: IntoLuaMulti,
|
||||
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) -> impl Future<Output = Result<R>>
|
||||
#[inline]
|
||||
fn call_async<R>(&self, args: impl IntoLuaMulti) -> impl Future<Output = Result<R>>
|
||||
where
|
||||
A: IntoLuaMulti,
|
||||
R: FromLuaMulti,
|
||||
{
|
||||
let lua = self.0.lua.lock();
|
||||
let args = args.into_lua_multi(lua.lua());
|
||||
async move {
|
||||
let func = Function(self.0.clone());
|
||||
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,
|
||||
R: FromLuaMulti,
|
||||
{
|
||||
self.get::<_, Function>(name)?.call((self, args))
|
||||
}
|
||||
|
||||
fn call_function<A, R>(&self, name: &str, args: A) -> Result<R>
|
||||
where
|
||||
A: IntoLuaMulti,
|
||||
R: FromLuaMulti,
|
||||
{
|
||||
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) -> impl Future<Output = Result<R>>
|
||||
fn call_async_method<R>(&self, name: &str, args: impl IntoLuaMulti) -> impl Future<Output = Result<R>>
|
||||
where
|
||||
A: IntoLuaMulti,
|
||||
R: FromLuaMulti,
|
||||
{
|
||||
self.call_async_function(name, (self, args))
|
||||
}
|
||||
|
||||
#[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")]
|
||||
fn call_async_function<A, R>(&self, name: &str, args: A) -> impl Future<Output = Result<R>>
|
||||
#[inline]
|
||||
fn call_async_function<R>(&self, name: &str, args: impl IntoLuaMulti) -> impl Future<Output = Result<R>>
|
||||
where
|
||||
A: IntoLuaMulti,
|
||||
R: FromLuaMulti,
|
||||
{
|
||||
let lua = self.0.lua.lock();
|
||||
let args = args.into_lua_multi(lua.lua());
|
||||
async move {
|
||||
let func = self.get::<_, Function>(name)?;
|
||||
func.call_async(args?).await
|
||||
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.
|
||||
@@ -1062,7 +1012,7 @@ impl<'a> Serialize for SerializableTable<'a> {
|
||||
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)?;
|
||||
@@ -1087,7 +1037,7 @@ impl<'a> Serialize for SerializableTable<'a> {
|
||||
)
|
||||
.map_err(|err| {
|
||||
serialize_err = Some(err);
|
||||
Error::SerializeError(String::new())
|
||||
Error::SerializeError(StdString::new())
|
||||
})
|
||||
};
|
||||
|
||||
@@ -1142,11 +1092,11 @@ where
|
||||
// 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.lua())?,
|
||||
V::from_stack(-1, &lua)?,
|
||||
V::from_stack(-1, lua)?,
|
||||
)))
|
||||
} else {
|
||||
Ok(None)
|
||||
@@ -1199,7 +1149,7 @@ where
|
||||
ffi::LUA_TNIL => None,
|
||||
_ => {
|
||||
self.index += 1;
|
||||
Some(V::from_stack(-1, &lua))
|
||||
Some(V::from_stack(-1, lua))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+63
-66
@@ -16,7 +16,6 @@ use crate::{
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
use {
|
||||
crate::value::MultiValue,
|
||||
futures_util::stream::Stream,
|
||||
std::{
|
||||
future::Future,
|
||||
@@ -30,14 +29,14 @@ 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,
|
||||
}
|
||||
@@ -60,9 +59,9 @@ unsafe impl Sync for Thread {}
|
||||
#[cfg(feature = "async")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
|
||||
#[must_use = "futures do nothing unless you `.await` or poll them"]
|
||||
pub struct AsyncThread<R> {
|
||||
pub struct AsyncThread<A, R> {
|
||||
thread: Thread,
|
||||
init_args: Option<Result<MultiValue>>,
|
||||
init_args: Option<A>,
|
||||
ret: PhantomData<R>,
|
||||
recycle: bool,
|
||||
}
|
||||
@@ -103,26 +102,24 @@ impl Thread {
|
||||
/// 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,
|
||||
R: FromLuaMulti,
|
||||
{
|
||||
let lua = self.0.lua.lock();
|
||||
|
||||
if unsafe { self.status_unprotected() } != ThreadStatus::Resumable {
|
||||
return Err(Error::CoroutineInactive);
|
||||
if self.status_inner(&lua) != ThreadStatus::Resumable {
|
||||
return Err(Error::CoroutineUnresumable);
|
||||
}
|
||||
|
||||
let state = lua.state();
|
||||
@@ -131,7 +128,7 @@ impl Thread {
|
||||
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);
|
||||
|
||||
@@ -142,8 +139,7 @@ impl Thread {
|
||||
/// Resumes execution of this thread.
|
||||
///
|
||||
/// It's similar to `resume()` but leaves `nresults` values on the thread stack.
|
||||
unsafe fn resume_inner<A: IntoLuaMulti>(&self, args: A) -> Result<c_int> {
|
||||
let lua = self.0.lua.lock();
|
||||
unsafe fn resume_inner(&self, lua: &RawLua, args: impl IntoLuaMulti) -> Result<c_int> {
|
||||
let state = lua.state();
|
||||
let thread_state = self.state();
|
||||
|
||||
@@ -170,25 +166,23 @@ impl Thread {
|
||||
|
||||
/// Gets the status of the thread.
|
||||
pub fn status(&self) -> ThreadStatus {
|
||||
let _guard = self.0.lua.lock();
|
||||
unsafe { self.status_unprotected() }
|
||||
self.status_inner(&self.0.lua.lock())
|
||||
}
|
||||
|
||||
/// Gets the status of the thread without locking the Lua state.
|
||||
pub(crate) unsafe fn status_unprotected(&self) -> ThreadStatus {
|
||||
/// Gets the status of the thread (internal implementation).
|
||||
pub(crate) fn status_inner(&self, lua: &RawLua) -> ThreadStatus {
|
||||
let thread_state = self.state();
|
||||
// FIXME: skip double lock
|
||||
if thread_state == self.0.lua.lock().state() {
|
||||
// The coroutine is currently running
|
||||
return ThreadStatus::Unresumable;
|
||||
if thread_state == lua.state() {
|
||||
// The thread is currently running
|
||||
return ThreadStatus::Running;
|
||||
}
|
||||
let status = ffi::lua_status(thread_state);
|
||||
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 || ffi::lua_gettop(thread_state) > 0 {
|
||||
} else if status == ffi::LUA_YIELD || unsafe { ffi::lua_gettop(thread_state) > 0 } {
|
||||
ThreadStatus::Resumable
|
||||
} else {
|
||||
ThreadStatus::Unresumable
|
||||
ThreadStatus::Finished
|
||||
}
|
||||
}
|
||||
|
||||
@@ -226,10 +220,11 @@ impl Thread {
|
||||
#[cfg_attr(docsrs, doc(cfg(any(feature = "lua54", feature = "luau"))))]
|
||||
pub fn reset(&self, func: crate::function::Function) -> Result<()> {
|
||||
let lua = self.0.lua.lock();
|
||||
let thread_state = self.state();
|
||||
if thread_state == lua.state() {
|
||||
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);
|
||||
@@ -290,7 +285,7 @@ impl Thread {
|
||||
/// 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;
|
||||
@@ -303,13 +298,10 @@ impl Thread {
|
||||
/// ```
|
||||
#[cfg(feature = "async")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
|
||||
pub fn into_async<A, R>(self, args: A) -> AsyncThread<R>
|
||||
pub fn into_async<R>(self, args: impl IntoLuaMulti) -> AsyncThread<impl IntoLuaMulti, R>
|
||||
where
|
||||
A: IntoLuaMulti,
|
||||
R: FromLuaMulti,
|
||||
{
|
||||
let lua = self.0.lua.lock();
|
||||
let args = args.into_lua_multi(lua.lua());
|
||||
AsyncThread {
|
||||
thread: self,
|
||||
init_args: Some(args),
|
||||
@@ -336,14 +328,14 @@ impl Thread {
|
||||
/// 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(())
|
||||
/// # }
|
||||
/// ```
|
||||
@@ -381,7 +373,7 @@ impl PartialEq for Thread {
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
impl<R> AsyncThread<R> {
|
||||
impl<A, R> AsyncThread<A, R> {
|
||||
#[inline]
|
||||
pub(crate) fn set_recyclable(&mut self, recyclable: bool) {
|
||||
self.recycle = recyclable;
|
||||
@@ -390,19 +382,20 @@ impl<R> AsyncThread<R> {
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
#[cfg(any(feature = "lua54", feature = "luau"))]
|
||||
impl<R> Drop for AsyncThread<R> {
|
||||
impl<A, R> Drop for AsyncThread<A, R> {
|
||||
fn drop(&mut self) {
|
||||
if self.recycle {
|
||||
unsafe {
|
||||
let lua = self.thread.0.lua.lock();
|
||||
// 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_unprotected() == 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());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -411,18 +404,18 @@ impl<R> Drop for AsyncThread<R> {
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
impl<R: FromLuaMulti> Stream for AsyncThread<R> {
|
||||
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>> {
|
||||
let lua = self.thread.0.lua.lock();
|
||||
if self.thread.status_inner(&lua) != ThreadStatus::Resumable {
|
||||
return Poll::Ready(None);
|
||||
}
|
||||
|
||||
let state = lua.state();
|
||||
let thread_state = self.thread.state();
|
||||
unsafe {
|
||||
if self.thread.status_unprotected() != ThreadStatus::Resumable {
|
||||
return Poll::Ready(None);
|
||||
}
|
||||
|
||||
let _sg = StackGuard::new(state);
|
||||
let _thread_sg = StackGuard::with_top(thread_state, 0);
|
||||
let _wg = WakerGuard::new(&lua, cx.waker());
|
||||
@@ -430,9 +423,9 @@ impl<R: FromLuaMulti> Stream for AsyncThread<R> {
|
||||
// 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) {
|
||||
@@ -449,18 +442,18 @@ impl<R: FromLuaMulti> Stream for AsyncThread<R> {
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
impl<R: FromLuaMulti> Future for AsyncThread<R> {
|
||||
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> {
|
||||
let lua = self.thread.0.lua.lock();
|
||||
if self.thread.status_inner(&lua) != ThreadStatus::Resumable {
|
||||
return Poll::Ready(Err(Error::CoroutineUnresumable));
|
||||
}
|
||||
|
||||
let state = lua.state();
|
||||
let thread_state = self.thread.state();
|
||||
unsafe {
|
||||
if self.thread.status_unprotected() != ThreadStatus::Resumable {
|
||||
return Poll::Ready(Err(Error::CoroutineInactive));
|
||||
}
|
||||
|
||||
let _sg = StackGuard::new(state);
|
||||
let _thread_sg = StackGuard::with_top(thread_state, 0);
|
||||
let _wg = WakerGuard::new(&lua, cx.waker());
|
||||
@@ -468,9 +461,9 @@ impl<R: FromLuaMulti> Future for AsyncThread<R> {
|
||||
// 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) {
|
||||
@@ -532,4 +525,8 @@ mod assertions {
|
||||
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>;
|
||||
}
|
||||
+43
-196
@@ -1,27 +1,27 @@
|
||||
use std::cell::UnsafeCell;
|
||||
use std::hash::{Hash, Hasher};
|
||||
use std::fmt;
|
||||
use std::os::raw::{c_int, c_void};
|
||||
use std::rc::Rc;
|
||||
use std::sync::Arc;
|
||||
use std::{fmt, mem, ptr};
|
||||
|
||||
use parking_lot::Mutex;
|
||||
|
||||
use crate::error::Result;
|
||||
#[cfg(not(feature = "luau"))]
|
||||
use crate::hook::Debug;
|
||||
use crate::state::{ExtraData, Lua, RawLua, WeakLua};
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
use {crate::value::MultiValue, futures_util::future::LocalBoxFuture};
|
||||
|
||||
#[cfg(all(feature = "luau", feature = "serialize"))]
|
||||
use serde::ser::{Serialize, SerializeTupleStruct, Serializer};
|
||||
|
||||
// Re-export mutex wrappers
|
||||
pub use app_data::{AppData, AppDataRef, AppDataRefMut};
|
||||
pub(crate) use sync::{ArcReentrantMutexGuard, ReentrantMutex, ReentrantMutexGuard, XRc, XWeak};
|
||||
|
||||
#[cfg(all(feature = "async", feature = "send"))]
|
||||
pub(crate) type BoxFuture<'a, T> = futures_util::future::BoxFuture<'a, T>;
|
||||
|
||||
#[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;
|
||||
/// Type of Lua floating point numbers.
|
||||
@@ -46,24 +46,32 @@ unsafe impl Send for LightUserData {}
|
||||
#[cfg(feature = "send")]
|
||||
unsafe impl Sync for LightUserData {}
|
||||
|
||||
pub(crate) type Callback<'a> = Box<dyn Fn(&'a RawLua, c_int) -> Result<c_int> + 'static>;
|
||||
#[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: XRc<UnsafeCell<ExtraData>>,
|
||||
}
|
||||
|
||||
pub(crate) type CallbackUpvalue = Upvalue<Callback<'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<'a> =
|
||||
Box<dyn Fn(&'a RawLua, MultiValue) -> LocalBoxFuture<'a, Result<c_int>> + 'static>;
|
||||
pub(crate) type AsyncCallbackUpvalue = Upvalue<AsyncCallback>;
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
pub(crate) type AsyncCallbackUpvalue = Upvalue<AsyncCallback<'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))]
|
||||
@@ -91,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")]
|
||||
@@ -101,182 +110,8 @@ 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) -> 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()
|
||||
}
|
||||
}
|
||||
|
||||
#[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: 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
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct ValueRef {
|
||||
pub(crate) lua: WeakLua,
|
||||
pub(crate) index: c_int,
|
||||
@@ -298,6 +133,16 @@ impl ValueRef {
|
||||
let lua = self.lua.lock();
|
||||
unsafe { ffi::lua_topointer(lua.ref_thread(), self.index) }
|
||||
}
|
||||
|
||||
/// Returns a copy of the value, which is valid as long as the original value is held.
|
||||
#[inline]
|
||||
pub(crate) fn copy(&self) -> Self {
|
||||
ValueRef {
|
||||
lua: self.lua.clone(),
|
||||
index: self.index,
|
||||
drop: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for ValueRef {
|
||||
@@ -334,14 +179,16 @@ impl PartialEq for ValueRef {
|
||||
}
|
||||
|
||||
mod app_data;
|
||||
mod registry_key;
|
||||
mod sync;
|
||||
|
||||
#[cfg(any(feature = "luau", doc))]
|
||||
mod vector;
|
||||
|
||||
#[cfg(test)]
|
||||
mod assertions {
|
||||
use super::*;
|
||||
|
||||
static_assertions::assert_impl_all!(RegistryKey: Send, Sync);
|
||||
|
||||
#[cfg(not(feature = "send"))]
|
||||
static_assertions::assert_not_impl_any!(ValueRef: Send);
|
||||
#[cfg(feature = "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,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
|
||||
}
|
||||
}
|
||||
+43
-50
@@ -26,7 +26,6 @@ use crate::value::{FromLua, FromLuaMulti, IntoLua, IntoLuaMulti, Value};
|
||||
// Re-export for convenience
|
||||
pub(crate) use cell::UserDataVariant;
|
||||
pub use cell::{UserDataRef, UserDataRefMut};
|
||||
pub use ext::AnyUserDataExt;
|
||||
pub(crate) use registry::UserDataProxy;
|
||||
pub use registry::UserDataRegistry;
|
||||
|
||||
@@ -248,7 +247,7 @@ impl AsRef<str> for MetaMethod {
|
||||
/// Method registry for [`UserData`] implementors.
|
||||
///
|
||||
/// [`UserData`]: crate::UserData
|
||||
pub trait UserDataMethods<'a, T> {
|
||||
pub trait UserDataMethods<T> {
|
||||
/// Add a regular method which accepts a `&T` as the first parameter.
|
||||
///
|
||||
/// Regular methods are implemented by overriding the `__index` metamethod and returning the
|
||||
@@ -258,7 +257,7 @@ pub trait UserDataMethods<'a, T> {
|
||||
/// be used as a fall-back if no regular method is found.
|
||||
fn add_method<M, A, R>(&mut self, name: impl ToString, method: M)
|
||||
where
|
||||
M: Fn(&'a Lua, &T, A) -> Result<R> + MaybeSend + 'static,
|
||||
M: Fn(&Lua, &T, A) -> Result<R> + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
R: IntoLuaMulti;
|
||||
|
||||
@@ -269,7 +268,7 @@ pub trait UserDataMethods<'a, T> {
|
||||
/// [`add_method`]: #method.add_method
|
||||
fn add_method_mut<M, A, R>(&mut self, name: impl ToString, method: M)
|
||||
where
|
||||
M: FnMut(&'a Lua, &mut T, A) -> Result<R> + MaybeSend + 'static,
|
||||
M: FnMut(&Lua, &mut T, A) -> Result<R> + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
R: IntoLuaMulti;
|
||||
|
||||
@@ -285,9 +284,9 @@ pub trait UserDataMethods<'a, T> {
|
||||
fn add_async_method<M, A, MR, R>(&mut self, name: impl ToString, method: M)
|
||||
where
|
||||
T: 'static,
|
||||
M: Fn(&'a Lua, &'a T, A) -> MR + MaybeSend + 'static,
|
||||
M: Fn(Lua, UserDataRef<T>, A) -> MR + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
MR: Future<Output = Result<R>> + 'a,
|
||||
MR: Future<Output = Result<R>> + MaybeSend + 'static,
|
||||
R: IntoLuaMulti;
|
||||
|
||||
/// Add an async method which accepts a `&mut T` as the first parameter and returns Future.
|
||||
@@ -302,9 +301,9 @@ pub trait UserDataMethods<'a, T> {
|
||||
fn add_async_method_mut<M, A, MR, R>(&mut self, name: impl ToString, method: M)
|
||||
where
|
||||
T: 'static,
|
||||
M: Fn(&'a Lua, &'a mut T, A) -> MR + MaybeSend + 'static,
|
||||
M: Fn(Lua, UserDataRefMut<T>, A) -> MR + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
MR: Future<Output = Result<R>> + 'a,
|
||||
MR: Future<Output = Result<R>> + MaybeSend + 'static,
|
||||
R: IntoLuaMulti;
|
||||
|
||||
/// Add a regular method as a function which accepts generic arguments, the first argument will
|
||||
@@ -319,7 +318,7 @@ pub trait UserDataMethods<'a, T> {
|
||||
/// [`add_method_mut`]: #method.add_method_mut
|
||||
fn add_function<F, A, R>(&mut self, name: impl ToString, function: F)
|
||||
where
|
||||
F: Fn(&'a Lua, A) -> Result<R> + MaybeSend + 'static,
|
||||
F: Fn(&Lua, A) -> Result<R> + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
R: IntoLuaMulti;
|
||||
|
||||
@@ -330,7 +329,7 @@ pub trait UserDataMethods<'a, T> {
|
||||
/// [`add_function`]: #method.add_function
|
||||
fn add_function_mut<F, A, R>(&mut self, name: impl ToString, function: F)
|
||||
where
|
||||
F: FnMut(&'a Lua, A) -> Result<R> + MaybeSend + 'static,
|
||||
F: FnMut(&Lua, A) -> Result<R> + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
R: IntoLuaMulti;
|
||||
|
||||
@@ -346,9 +345,9 @@ pub trait UserDataMethods<'a, T> {
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
|
||||
fn add_async_function<F, A, FR, R>(&mut self, name: impl ToString, function: F)
|
||||
where
|
||||
F: Fn(&'a Lua, A) -> FR + MaybeSend + 'static,
|
||||
F: Fn(Lua, A) -> FR + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
FR: Future<Output = Result<R>> + 'a,
|
||||
FR: Future<Output = Result<R>> + MaybeSend + 'static,
|
||||
R: IntoLuaMulti;
|
||||
|
||||
/// Add a metamethod which accepts a `&T` as the first parameter.
|
||||
@@ -361,7 +360,7 @@ pub trait UserDataMethods<'a, T> {
|
||||
/// [`add_meta_function`]: #method.add_meta_function
|
||||
fn add_meta_method<M, A, R>(&mut self, name: impl ToString, method: M)
|
||||
where
|
||||
M: Fn(&'a Lua, &T, A) -> Result<R> + MaybeSend + 'static,
|
||||
M: Fn(&Lua, &T, A) -> Result<R> + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
R: IntoLuaMulti;
|
||||
|
||||
@@ -375,7 +374,7 @@ pub trait UserDataMethods<'a, T> {
|
||||
/// [`add_meta_function`]: #method.add_meta_function
|
||||
fn add_meta_method_mut<M, A, R>(&mut self, name: impl ToString, method: M)
|
||||
where
|
||||
M: FnMut(&'a Lua, &mut T, A) -> Result<R> + MaybeSend + 'static,
|
||||
M: FnMut(&Lua, &mut T, A) -> Result<R> + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
R: IntoLuaMulti;
|
||||
|
||||
@@ -391,9 +390,9 @@ pub trait UserDataMethods<'a, T> {
|
||||
fn add_async_meta_method<M, A, MR, R>(&mut self, name: impl ToString, method: M)
|
||||
where
|
||||
T: 'static,
|
||||
M: Fn(&'a Lua, &'a T, A) -> MR + MaybeSend + 'static,
|
||||
M: Fn(Lua, UserDataRef<T>, A) -> MR + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
MR: Future<Output = Result<R>> + 'a,
|
||||
MR: Future<Output = Result<R>> + MaybeSend + 'static,
|
||||
R: IntoLuaMulti;
|
||||
|
||||
/// Add an async metamethod which accepts a `&mut T` as the first parameter and returns Future.
|
||||
@@ -408,9 +407,9 @@ pub trait UserDataMethods<'a, T> {
|
||||
fn add_async_meta_method_mut<M, A, MR, R>(&mut self, name: impl ToString, method: M)
|
||||
where
|
||||
T: 'static,
|
||||
M: Fn(&'a Lua, &'a mut T, A) -> MR + MaybeSend + 'static,
|
||||
M: Fn(Lua, UserDataRefMut<T>, A) -> MR + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
MR: Future<Output = Result<R>> + 'a,
|
||||
MR: Future<Output = Result<R>> + MaybeSend + 'static,
|
||||
R: IntoLuaMulti;
|
||||
|
||||
/// Add a metamethod which accepts generic arguments.
|
||||
@@ -420,7 +419,7 @@ pub trait UserDataMethods<'a, T> {
|
||||
/// userdata of type `T`.
|
||||
fn add_meta_function<F, A, R>(&mut self, name: impl ToString, function: F)
|
||||
where
|
||||
F: Fn(&'a Lua, A) -> Result<R> + MaybeSend + 'static,
|
||||
F: Fn(&Lua, A) -> Result<R> + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
R: IntoLuaMulti;
|
||||
|
||||
@@ -431,7 +430,7 @@ pub trait UserDataMethods<'a, T> {
|
||||
/// [`add_meta_function`]: #method.add_meta_function
|
||||
fn add_meta_function_mut<F, A, R>(&mut self, name: impl ToString, function: F)
|
||||
where
|
||||
F: FnMut(&'a Lua, A) -> Result<R> + MaybeSend + 'static,
|
||||
F: FnMut(&Lua, A) -> Result<R> + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
R: IntoLuaMulti;
|
||||
|
||||
@@ -446,16 +445,16 @@ pub trait UserDataMethods<'a, T> {
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
|
||||
fn add_async_meta_function<F, A, FR, R>(&mut self, name: impl ToString, function: F)
|
||||
where
|
||||
F: Fn(&'a Lua, A) -> FR + MaybeSend + 'static,
|
||||
F: Fn(Lua, A) -> FR + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
FR: Future<Output = Result<R>> + 'a,
|
||||
FR: Future<Output = Result<R>> + MaybeSend + 'static,
|
||||
R: IntoLuaMulti;
|
||||
}
|
||||
|
||||
/// Field registry for [`UserData`] implementors.
|
||||
///
|
||||
/// [`UserData`]: crate::UserData
|
||||
pub trait UserDataFields<'a, T> {
|
||||
pub trait UserDataFields<T> {
|
||||
/// Add a static field to the `UserData`.
|
||||
///
|
||||
/// Static fields are implemented by updating the `__index` metamethod and returning the
|
||||
@@ -467,7 +466,7 @@ pub trait UserDataFields<'a, T> {
|
||||
/// be used as a fall-back if no regular field or method are found.
|
||||
fn add_field<V>(&mut self, name: impl ToString, value: V)
|
||||
where
|
||||
V: IntoLua + Clone + 'static;
|
||||
V: IntoLua + 'static;
|
||||
|
||||
/// Add a regular field getter as a method which accepts a `&T` as the parameter.
|
||||
///
|
||||
@@ -478,7 +477,7 @@ pub trait UserDataFields<'a, T> {
|
||||
/// be used as a fall-back if no regular field or method are found.
|
||||
fn add_field_method_get<M, R>(&mut self, name: impl ToString, method: M)
|
||||
where
|
||||
M: Fn(&'a Lua, &T) -> Result<R> + MaybeSend + 'static,
|
||||
M: Fn(&Lua, &T) -> Result<R> + MaybeSend + 'static,
|
||||
R: IntoLua;
|
||||
|
||||
/// Add a regular field setter as a method which accepts a `&mut T` as the first parameter.
|
||||
@@ -491,7 +490,7 @@ pub trait UserDataFields<'a, T> {
|
||||
/// will be used as a fall-back if no regular field is found.
|
||||
fn add_field_method_set<M, A>(&mut self, name: impl ToString, method: M)
|
||||
where
|
||||
M: FnMut(&'a Lua, &mut T, A) -> Result<()> + MaybeSend + 'static,
|
||||
M: FnMut(&Lua, &mut T, A) -> Result<()> + MaybeSend + 'static,
|
||||
A: FromLua;
|
||||
|
||||
/// Add a regular field getter as a function which accepts a generic [`AnyUserData`] of type `T`
|
||||
@@ -503,7 +502,7 @@ pub trait UserDataFields<'a, T> {
|
||||
/// [`add_field_method_get`]: #method.add_field_method_get
|
||||
fn add_field_function_get<F, R>(&mut self, name: impl ToString, function: F)
|
||||
where
|
||||
F: Fn(&'a Lua, AnyUserData) -> Result<R> + MaybeSend + 'static,
|
||||
F: Fn(&Lua, AnyUserData) -> Result<R> + MaybeSend + 'static,
|
||||
R: IntoLua;
|
||||
|
||||
/// Add a regular field setter as a function which accepts a generic [`AnyUserData`] of type `T`
|
||||
@@ -515,7 +514,7 @@ pub trait UserDataFields<'a, T> {
|
||||
/// [`add_field_method_set`]: #method.add_field_method_set
|
||||
fn add_field_function_set<F, A>(&mut self, name: impl ToString, function: F)
|
||||
where
|
||||
F: FnMut(&'a Lua, AnyUserData, A) -> Result<()> + MaybeSend + 'static,
|
||||
F: FnMut(&Lua, AnyUserData, A) -> Result<()> + MaybeSend + 'static,
|
||||
A: FromLua;
|
||||
|
||||
/// Add a metatable field.
|
||||
@@ -528,7 +527,7 @@ pub trait UserDataFields<'a, T> {
|
||||
/// like `__gc` or `__metatable`.
|
||||
fn add_meta_field<V>(&mut self, name: impl ToString, value: V)
|
||||
where
|
||||
V: IntoLua + Clone + 'static;
|
||||
V: IntoLua + 'static;
|
||||
|
||||
/// Add a metatable field computed from `f`.
|
||||
///
|
||||
@@ -540,7 +539,7 @@ pub trait UserDataFields<'a, T> {
|
||||
/// like `__gc` or `__metatable`.
|
||||
fn add_meta_field_with<F, R>(&mut self, name: impl ToString, f: F)
|
||||
where
|
||||
F: Fn(&'a Lua) -> Result<R> + MaybeSend + 'static,
|
||||
F: FnOnce(&Lua) -> Result<R> + 'static,
|
||||
R: IntoLua;
|
||||
}
|
||||
|
||||
@@ -579,12 +578,12 @@ pub trait UserDataFields<'a, T> {
|
||||
/// struct MyUserData(i32);
|
||||
///
|
||||
/// impl UserData for MyUserData {
|
||||
/// fn add_fields<'a, F: UserDataFields<'a, Self>>(fields: &mut F) {
|
||||
/// fn add_fields<F: UserDataFields<Self>>(fields: &mut F) {
|
||||
/// fields.add_field_method_get("val", |_, this| Ok(this.0));
|
||||
/// }
|
||||
///
|
||||
/// fn add_methods<'a, M: UserDataMethods<'a, Self>>(methods: &mut M) {
|
||||
/// methods.add_method_mut("add", |_, this, value: i32| {
|
||||
/// fn add_methods<M: UserDataMethods<Self>>(methods: &mut M) {
|
||||
/// methods.add_method_mut("add", |_, mut this, value: i32| {
|
||||
/// this.0 += value;
|
||||
/// Ok(())
|
||||
/// });
|
||||
@@ -614,11 +613,11 @@ pub trait UserDataFields<'a, T> {
|
||||
pub trait UserData: Sized {
|
||||
/// Adds custom fields specific to this userdata.
|
||||
#[allow(unused_variables)]
|
||||
fn add_fields<'a, F: UserDataFields<'a, Self>>(fields: &mut F) {}
|
||||
fn add_fields<F: UserDataFields<Self>>(fields: &mut F) {}
|
||||
|
||||
/// Adds custom methods and operators specific to this userdata.
|
||||
#[allow(unused_variables)]
|
||||
fn add_methods<'a, M: UserDataMethods<'a, Self>>(methods: &mut M) {}
|
||||
fn add_methods<M: UserDataMethods<Self>>(methods: &mut M) {}
|
||||
|
||||
/// Registers this type for use in Lua.
|
||||
///
|
||||
@@ -663,7 +662,7 @@ impl AnyUserData {
|
||||
/// `UserDataTypeMismatch` if the userdata is not of type `T`.
|
||||
#[inline]
|
||||
pub fn borrow<T: 'static>(&self) -> Result<UserDataRef<T>> {
|
||||
self.inspect(|variant, guard| variant.try_make_ref(guard))
|
||||
self.inspect(|variant, _| variant.try_borrow_owned())
|
||||
}
|
||||
|
||||
/// Borrow this userdata mutably if it is of type `T`.
|
||||
@@ -674,7 +673,7 @@ impl AnyUserData {
|
||||
/// Returns a `UserDataTypeMismatch` if the userdata is not of type `T`.
|
||||
#[inline]
|
||||
pub fn borrow_mut<T: 'static>(&self) -> Result<UserDataRefMut<T>> {
|
||||
self.inspect(|variant, guard| variant.try_make_mut_ref(guard))
|
||||
self.inspect(|variant, _| variant.try_borrow_owned_mut())
|
||||
}
|
||||
|
||||
/// Takes the value out of this userdata.
|
||||
@@ -710,7 +709,7 @@ impl AnyUserData {
|
||||
/// [`user_value`]: #method.user_value
|
||||
/// [`set_nth_user_value`]: #method.set_nth_user_value
|
||||
#[inline]
|
||||
pub fn set_user_value<V: IntoLua>(&self, v: V) -> Result<()> {
|
||||
pub fn set_user_value(&self, v: impl IntoLua) -> Result<()> {
|
||||
self.set_nth_user_value(1, v)
|
||||
}
|
||||
|
||||
@@ -741,7 +740,7 @@ impl AnyUserData {
|
||||
/// For other Lua versions this functionality is provided using a wrapping table.
|
||||
///
|
||||
/// [`nth_user_value`]: #method.nth_user_value
|
||||
pub fn set_nth_user_value<V: IntoLua>(&self, n: usize, v: V) -> Result<()> {
|
||||
pub fn set_nth_user_value(&self, n: usize, v: impl IntoLua) -> Result<()> {
|
||||
if n < 1 || n > u16::MAX as usize {
|
||||
return Err(Error::runtime("user value index out of bounds"));
|
||||
}
|
||||
@@ -840,7 +839,7 @@ impl AnyUserData {
|
||||
/// The value can be retrieved with [`named_user_value`].
|
||||
///
|
||||
/// [`named_user_value`]: #method.named_user_value
|
||||
pub fn set_named_user_value<V: IntoLua>(&self, name: &str, v: V) -> Result<()> {
|
||||
pub fn set_named_user_value(&self, name: &str, v: impl IntoLua) -> Result<()> {
|
||||
let lua = self.0.lua.lock();
|
||||
let state = lua.state();
|
||||
unsafe {
|
||||
@@ -940,13 +939,6 @@ impl AnyUserData {
|
||||
self.0.to_pointer()
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
#[inline]
|
||||
pub(crate) fn type_id(&self) -> Result<Option<TypeId>> {
|
||||
let lua = self.0.lua.lock();
|
||||
unsafe { lua.get_userdata_ref_type_id(&self.0) }
|
||||
}
|
||||
|
||||
/// Returns a type name of this `UserData` (from a metatable field).
|
||||
pub(crate) fn type_name(&self) -> Result<Option<StdString>> {
|
||||
match self.1 {
|
||||
@@ -992,7 +984,7 @@ impl AnyUserData {
|
||||
}
|
||||
|
||||
if mt.contains_key("__eq")? {
|
||||
return mt.get::<_, Function>("__eq")?.call((self, other));
|
||||
return mt.get::<Function>("__eq")?.call((self, other));
|
||||
}
|
||||
|
||||
Ok(false)
|
||||
@@ -1075,7 +1067,7 @@ impl UserDataMetatable {
|
||||
/// Access to restricted metamethods such as `__gc` or `__metatable` will cause an error.
|
||||
/// Setting `__index` or `__newindex` metamethods is also restricted because their values are
|
||||
/// cached for `mlua` internal usage.
|
||||
pub fn set<V: IntoLua>(&self, key: impl AsRef<str>, value: V) -> Result<()> {
|
||||
pub fn set(&self, key: impl AsRef<str>, value: impl IntoLua) -> Result<()> {
|
||||
let key = MetaMethod::validate(key.as_ref())?;
|
||||
// `__index` and `__newindex` cannot be changed in runtime, because values are cached
|
||||
if key == MetaMethod::Index || key == MetaMethod::NewIndex {
|
||||
@@ -1181,7 +1173,8 @@ where
|
||||
}
|
||||
|
||||
mod cell;
|
||||
mod ext;
|
||||
mod lock;
|
||||
mod object;
|
||||
mod registry;
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
+107
-138
@@ -1,34 +1,42 @@
|
||||
use std::any::{type_name, TypeId};
|
||||
use std::cell::{Cell, UnsafeCell};
|
||||
use std::cell::UnsafeCell;
|
||||
use std::fmt;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use std::os::raw::c_int;
|
||||
use std::rc::Rc;
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
use serde::ser::{Serialize, Serializer};
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
use crate::state::{Lua, LuaGuard, RawLua};
|
||||
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(Rc<InnerRefCell<T>>),
|
||||
Default(XRc<UserDataCell<T>>),
|
||||
#[cfg(feature = "serialize")]
|
||||
Serializable(Rc<InnerRefCell<Box<dyn erased_serde::Serialize>>>),
|
||||
Serializable(XRc<UserDataCell<Box<DynSerialize>>>),
|
||||
}
|
||||
|
||||
impl<T> Clone for UserDataVariant<T> {
|
||||
#[inline]
|
||||
fn clone(&self) -> Self {
|
||||
match self {
|
||||
Self::Default(inner) => Self::Default(Rc::clone(inner)),
|
||||
Self::Default(inner) => Self::Default(XRc::clone(inner)),
|
||||
#[cfg(feature = "serialize")]
|
||||
Self::Serializable(inner) => UserDataVariant::Serializable(Rc::clone(inner)),
|
||||
Self::Serializable(inner) => Self::Serializable(XRc::clone(inner)),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -36,54 +44,56 @@ impl<T> Clone for UserDataVariant<T> {
|
||||
impl<T> UserDataVariant<T> {
|
||||
#[inline(always)]
|
||||
pub(crate) fn new(data: T) -> Self {
|
||||
Self::Default(Rc::new(InnerRefCell::new(data)))
|
||||
Self::Default(XRc::new(UserDataCell::new(data)))
|
||||
}
|
||||
|
||||
// Immutably borrows the wrapped value in-place.
|
||||
#[inline(always)]
|
||||
pub(crate) unsafe fn try_borrow(&self) -> Result<UserDataBorrowRef<T>> {
|
||||
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_make_ref(&self, guard: LuaGuard) -> Result<UserDataRef<T>> {
|
||||
UserDataRef::try_from(self.clone(), guard)
|
||||
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) unsafe fn try_borrow_mut(&self) -> Result<UserDataBorrowMut<T>> {
|
||||
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_make_mut_ref(&self, guard: LuaGuard) -> Result<UserDataRefMut<T>> {
|
||||
UserDataRefMut::try_from(self.clone(), guard)
|
||||
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> {
|
||||
set_writing(self.flag())?;
|
||||
if !self.raw_lock().try_lock_exclusive() {
|
||||
return Err(Error::UserDataBorrowMutError);
|
||||
}
|
||||
Ok(match self {
|
||||
Self::Default(inner) => Rc::into_inner(inner).unwrap().value.into_inner(),
|
||||
Self::Default(inner) => XRc::into_inner(inner).unwrap().value.into_inner(),
|
||||
#[cfg(feature = "serialize")]
|
||||
Self::Serializable(inner) => unsafe {
|
||||
let raw = Box::into_raw(Rc::into_inner(inner).unwrap().value.into_inner());
|
||||
let raw = Box::into_raw(XRc::into_inner(inner).unwrap().value.into_inner());
|
||||
*Box::from_raw(raw as *mut T)
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn flag(&self) -> &Cell<BorrowFlag> {
|
||||
fn raw_lock(&self) -> &RawLock {
|
||||
match self {
|
||||
Self::Default(inner) => &inner.borrow,
|
||||
Self::Default(inner) => &inner.raw_lock,
|
||||
#[cfg(feature = "serialize")]
|
||||
Self::Serializable(inner) => &inner.borrow,
|
||||
Self::Serializable(inner) => &inner.raw_lock,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -98,11 +108,11 @@ impl<T> UserDataVariant<T> {
|
||||
}
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
impl<T: Serialize + 'static> UserDataVariant<T> {
|
||||
impl<T: Serialize + MaybeSend + 'static> UserDataVariant<T> {
|
||||
#[inline(always)]
|
||||
pub(crate) fn new_ser(data: T) -> Self {
|
||||
let data = Box::new(data) as Box<dyn erased_serde::Serialize>;
|
||||
Self::Serializable(Rc::new(InnerRefCell::new(data)))
|
||||
let data = Box::new(data) as Box<DynSerialize>;
|
||||
Self::Serializable(XRc::new(UserDataCell::new(data)))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -110,29 +120,34 @@ impl<T: Serialize + 'static> UserDataVariant<T> {
|
||||
impl Serialize for UserDataVariant<()> {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error> {
|
||||
match self {
|
||||
UserDataVariant::Default(_) => Err(serde::ser::Error::custom("cannot serialize <userdata>")),
|
||||
UserDataVariant::Serializable(inner) => unsafe {
|
||||
let _ = self.try_borrow().map_err(serde::ser::Error::custom)?;
|
||||
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)
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Inspired by `std::cell::RefCell`` implementation
|
||||
//
|
||||
|
||||
pub(crate) struct InnerRefCell<T> {
|
||||
borrow: Cell<BorrowFlag>,
|
||||
/// A type that provides interior mutability for a userdata value (thread-safe).
|
||||
pub(crate) struct UserDataCell<T> {
|
||||
raw_lock: RawLock,
|
||||
value: UnsafeCell<T>,
|
||||
}
|
||||
|
||||
impl<T> InnerRefCell<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 {
|
||||
InnerRefCell {
|
||||
borrow: Cell::new(UNUSED),
|
||||
UserDataCell {
|
||||
raw_lock: RawLock::INIT,
|
||||
value: UnsafeCell::new(value),
|
||||
}
|
||||
}
|
||||
@@ -141,25 +156,21 @@ impl<T> InnerRefCell<T> {
|
||||
/// 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> {
|
||||
variant: UserDataVariant<T>,
|
||||
#[allow(unused)]
|
||||
guard: LuaGuard,
|
||||
}
|
||||
pub struct UserDataRef<T>(UserDataVariant<T>);
|
||||
|
||||
impl<T> Deref for UserDataRef<T> {
|
||||
type Target = T;
|
||||
|
||||
#[inline]
|
||||
fn deref(&self) -> &T {
|
||||
unsafe { &*self.variant.as_ptr() }
|
||||
unsafe { &*self.0.as_ptr() }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Drop for UserDataRef<T> {
|
||||
#[inline]
|
||||
fn drop(&mut self) {
|
||||
unset_reading(self.variant.flag());
|
||||
unsafe { self.0.raw_lock().unlock_shared() };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -175,11 +186,15 @@ impl<T: fmt::Display> fmt::Display for UserDataRef<T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> UserDataRef<T> {
|
||||
impl<T> TryFrom<UserDataVariant<T>> for UserDataRef<T> {
|
||||
type Error = Error;
|
||||
|
||||
#[inline]
|
||||
fn try_from(variant: UserDataVariant<T>, guard: LuaGuard) -> Result<Self> {
|
||||
set_reading(variant.flag())?;
|
||||
Ok(UserDataRef { variant, guard })
|
||||
fn try_from(variant: UserDataVariant<T>) -> Result<Self> {
|
||||
if !variant.raw_lock().try_lock_shared() {
|
||||
return Err(Error::UserDataBorrowError);
|
||||
}
|
||||
Ok(UserDataRef(variant))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -192,8 +207,7 @@ impl<T: 'static> FromLua for UserDataRef<T> {
|
||||
let type_id = lua.get_userdata_type_id(idx)?;
|
||||
match type_id {
|
||||
Some(type_id) if type_id == TypeId::of::<T>() => {
|
||||
let guard = lua.lua().lock_arc();
|
||||
(*get_userdata::<UserDataVariant<T>>(lua.state(), idx)).try_make_ref(guard)
|
||||
(*get_userdata::<UserDataVariant<T>>(lua.state(), idx)).try_borrow_owned()
|
||||
}
|
||||
_ => Err(Error::UserDataTypeMismatch),
|
||||
}
|
||||
@@ -203,32 +217,28 @@ impl<T: 'static> FromLua for UserDataRef<T> {
|
||||
/// A wrapper type for a mutably borrowed value from a `AnyUserData`.
|
||||
///
|
||||
/// It implements [`FromLua`] and can be used to receive a typed userdata from Lua.
|
||||
pub struct UserDataRefMut<T> {
|
||||
variant: UserDataVariant<T>,
|
||||
#[allow(unused)]
|
||||
guard: LuaGuard,
|
||||
}
|
||||
pub struct UserDataRefMut<T>(UserDataVariant<T>);
|
||||
|
||||
impl<T> Deref for UserDataRefMut<T> {
|
||||
type Target = T;
|
||||
|
||||
#[inline]
|
||||
fn deref(&self) -> &Self::Target {
|
||||
unsafe { &*self.variant.as_ptr() }
|
||||
unsafe { &*self.0.as_ptr() }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> DerefMut for UserDataRefMut<T> {
|
||||
#[inline]
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
unsafe { &mut *self.variant.as_ptr() }
|
||||
unsafe { &mut *self.0.as_ptr() }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Drop for UserDataRefMut<T> {
|
||||
#[inline]
|
||||
fn drop(&mut self) {
|
||||
unset_writing(self.variant.flag());
|
||||
unsafe { self.0.raw_lock().unlock_exclusive() };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -244,11 +254,15 @@ impl<T: fmt::Display> fmt::Display for UserDataRefMut<T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> UserDataRefMut<T> {
|
||||
fn try_from(variant: UserDataVariant<T>, guard: LuaGuard) -> Result<Self> {
|
||||
// There must currently be no existing references
|
||||
set_writing(variant.flag())?;
|
||||
Ok(UserDataRefMut { variant, guard })
|
||||
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))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -261,73 +275,20 @@ impl<T: 'static> FromLua for UserDataRefMut<T> {
|
||||
let type_id = lua.get_userdata_type_id(idx)?;
|
||||
match type_id {
|
||||
Some(type_id) if type_id == TypeId::of::<T>() => {
|
||||
let guard = lua.lua().lock_arc();
|
||||
(*get_userdata::<UserDataVariant<T>>(lua.state(), idx)).try_make_mut_ref(guard)
|
||||
(*get_userdata::<UserDataVariant<T>>(lua.state(), idx)).try_borrow_owned_mut()
|
||||
}
|
||||
_ => Err(Error::UserDataTypeMismatch),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Positive values represent the number of `Ref` active. Negative values
|
||||
// represent the number of `RefMut` active. Multiple `RefMut`s can only be
|
||||
// active at a time if they refer to distinct, nonoverlapping components of a
|
||||
// `RefCell` (e.g., different ranges of a slice).
|
||||
type BorrowFlag = isize;
|
||||
const UNUSED: BorrowFlag = 0;
|
||||
|
||||
#[inline(always)]
|
||||
fn is_writing(x: BorrowFlag) -> bool {
|
||||
x < UNUSED
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn is_reading(x: BorrowFlag) -> bool {
|
||||
x > UNUSED
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn set_writing(borrow: &Cell<BorrowFlag>) -> Result<()> {
|
||||
let flag = borrow.get();
|
||||
if flag != UNUSED {
|
||||
return Err(Error::UserDataBorrowMutError);
|
||||
}
|
||||
borrow.set(UNUSED - 1);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn set_reading(borrow: &Cell<BorrowFlag>) -> Result<()> {
|
||||
let flag = borrow.get().wrapping_add(1);
|
||||
if !is_reading(flag) {
|
||||
return Err(Error::UserDataBorrowError);
|
||||
}
|
||||
borrow.set(flag);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
#[track_caller]
|
||||
fn unset_writing(borrow: &Cell<BorrowFlag>) {
|
||||
let flag = borrow.get();
|
||||
debug_assert!(is_writing(flag));
|
||||
borrow.set(flag + 1);
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
#[track_caller]
|
||||
fn unset_reading(borrow: &Cell<BorrowFlag>) {
|
||||
let flag = borrow.get();
|
||||
debug_assert!(is_reading(flag));
|
||||
borrow.set(flag - 1);
|
||||
}
|
||||
|
||||
/// 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) {
|
||||
unset_reading(self.0.flag());
|
||||
unsafe { self.0.raw_lock().unlock_shared() };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -336,6 +297,7 @@ impl<'a, T> Deref for UserDataBorrowRef<'a, T> {
|
||||
|
||||
#[inline]
|
||||
fn deref(&self) -> &T {
|
||||
// SAFETY: `UserDataBorrowRef` is only created with shared access to the value.
|
||||
unsafe { &*self.0.as_ptr() }
|
||||
}
|
||||
}
|
||||
@@ -345,25 +307,19 @@ impl<'a, T> TryFrom<&'a UserDataVariant<T>> for UserDataBorrowRef<'a, T> {
|
||||
|
||||
#[inline(always)]
|
||||
fn try_from(variant: &'a UserDataVariant<T>) -> Result<Self> {
|
||||
set_reading(variant.flag())?;
|
||||
if !variant.raw_lock().try_lock_shared() {
|
||||
return Err(Error::UserDataBorrowError);
|
||||
}
|
||||
Ok(UserDataBorrowRef(variant))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> UserDataBorrowRef<'a, T> {
|
||||
#[inline(always)]
|
||||
pub(crate) fn get_ref(&self) -> &'a T {
|
||||
// SAFETY: `UserDataBorrowRef` is only created when the borrow flag is set to reading.
|
||||
unsafe { &*self.0.as_ptr() }
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct UserDataBorrowMut<'a, T>(&'a UserDataVariant<T>);
|
||||
|
||||
impl<'a, T> Drop for UserDataBorrowMut<'a, T> {
|
||||
#[inline]
|
||||
fn drop(&mut self) {
|
||||
unset_writing(self.0.flag());
|
||||
unsafe { self.0.raw_lock().unlock_exclusive() };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -388,19 +344,13 @@ impl<'a, T> TryFrom<&'a UserDataVariant<T>> for UserDataBorrowMut<'a, T> {
|
||||
|
||||
#[inline(always)]
|
||||
fn try_from(variant: &'a UserDataVariant<T>) -> Result<Self> {
|
||||
set_writing(variant.flag())?;
|
||||
if !variant.raw_lock().try_lock_exclusive() {
|
||||
return Err(Error::UserDataBorrowMutError);
|
||||
}
|
||||
Ok(UserDataBorrowMut(variant))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> UserDataBorrowMut<'a, T> {
|
||||
#[inline(always)]
|
||||
pub(crate) fn get_mut(&mut self) -> &'a mut T {
|
||||
// SAFETY: `UserDataBorrowMut` is only created when the borrow flag is set to writing.
|
||||
unsafe { &mut *self.0.as_ptr() }
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn try_value_to_userdata<T>(value: Value) -> Result<AnyUserData> {
|
||||
match value {
|
||||
@@ -417,6 +367,25 @@ fn try_value_to_userdata<T>(value: Value) -> Result<AnyUserData> {
|
||||
mod assertions {
|
||||
use super::*;
|
||||
|
||||
static_assertions::assert_not_impl_all!(UserDataRef<()>: Sync, Send);
|
||||
static_assertions::assert_not_impl_all!(UserDataRefMut<()>: Sync, Send);
|
||||
#[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);
|
||||
}
|
||||
|
||||
@@ -1,180 +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 std::future::Future;
|
||||
|
||||
/// An extension trait for [`AnyUserData`] that provides a variety of convenient functionality.
|
||||
pub trait AnyUserDataExt: Sealed {
|
||||
/// Gets the value associated to `key` from the userdata, assuming it has `__index` metamethod.
|
||||
fn get<K: IntoLua, V: FromLua>(&self, key: K) -> Result<V>;
|
||||
|
||||
/// Sets the value associated to `key` in the userdata, assuming it has `__newindex` metamethod.
|
||||
fn set<K: IntoLua, V: IntoLua>(&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,
|
||||
R: FromLuaMulti;
|
||||
|
||||
/// 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) -> impl Future<Output = Result<R>>
|
||||
where
|
||||
A: IntoLuaMulti,
|
||||
R: FromLuaMulti;
|
||||
|
||||
/// 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,
|
||||
R: FromLuaMulti;
|
||||
|
||||
/// 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) -> impl Future<Output = Result<R>>
|
||||
where
|
||||
A: IntoLuaMulti,
|
||||
R: FromLuaMulti;
|
||||
|
||||
/// 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,
|
||||
R: FromLuaMulti;
|
||||
|
||||
/// 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) -> impl Future<Output = Result<R>>
|
||||
where
|
||||
A: IntoLuaMulti,
|
||||
R: FromLuaMulti;
|
||||
}
|
||||
|
||||
impl AnyUserDataExt for AnyUserData {
|
||||
fn get<K: IntoLua, V: FromLua>(&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, key)),
|
||||
_ => Err(Error::runtime("attempt to index a userdata value")),
|
||||
}
|
||||
}
|
||||
|
||||
fn set<K: IntoLua, V: IntoLua>(&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, key, value)),
|
||||
_ => Err(Error::runtime("attempt to index a userdata value")),
|
||||
}
|
||||
}
|
||||
|
||||
fn call<A, R>(&self, args: A) -> Result<R>
|
||||
where
|
||||
A: IntoLuaMulti,
|
||||
R: FromLuaMulti,
|
||||
{
|
||||
let metatable = self.get_metatable()?;
|
||||
match metatable.get::<Value>(MetaMethod::Call)? {
|
||||
Value::Function(func) => func.call((self, args)),
|
||||
_ => Err(Error::runtime("attempt to call a userdata value")),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
fn call_async<A, R>(&self, args: A) -> impl Future<Output = Result<R>>
|
||||
where
|
||||
A: IntoLuaMulti,
|
||||
R: FromLuaMulti,
|
||||
{
|
||||
let lua = self.0.lua.lock();
|
||||
let args = (self, args).into_lua_multi(lua.lua());
|
||||
async move {
|
||||
let metatable = self.get_metatable()?;
|
||||
match metatable.get::<Value>(MetaMethod::Call)? {
|
||||
Value::Function(func) => func.call_async(args?).await,
|
||||
_ => Err(Error::runtime("attempt to call a userdata value")),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn call_method<A, R>(&self, name: &str, args: A) -> Result<R>
|
||||
where
|
||||
A: IntoLuaMulti,
|
||||
R: FromLuaMulti,
|
||||
{
|
||||
self.call_function(name, (self, args))
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
fn call_async_method<A, R>(&self, name: &str, args: A) -> impl Future<Output = Result<R>>
|
||||
where
|
||||
A: IntoLuaMulti,
|
||||
R: FromLuaMulti,
|
||||
{
|
||||
self.call_async_function(name, (self, args))
|
||||
}
|
||||
|
||||
fn call_function<A, R>(&self, name: &str, args: A) -> Result<R>
|
||||
where
|
||||
A: IntoLuaMulti,
|
||||
R: FromLuaMulti,
|
||||
{
|
||||
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) -> impl Future<Output = Result<R>>
|
||||
where
|
||||
A: IntoLuaMulti,
|
||||
R: FromLuaMulti,
|
||||
{
|
||||
let lua = self.0.lua.lock();
|
||||
let args = args.into_lua_multi(lua.lua());
|
||||
async move {
|
||||
match self.get::<_, Value>(name)? {
|
||||
Value::Function(func) => func.call_async(args?).await,
|
||||
val => {
|
||||
let msg = format!("attempt to call a {} value", val.type_name());
|
||||
Err(Error::runtime(msg))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
+116
-125
@@ -7,9 +7,11 @@ use std::os::raw::c_int;
|
||||
use std::string::String as StdString;
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
use crate::state::Lua;
|
||||
use crate::state::{Lua, RawLua};
|
||||
use crate::types::{Callback, MaybeSend};
|
||||
use crate::userdata::{AnyUserData, MetaMethod, UserData, UserDataFields, UserDataMethods};
|
||||
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};
|
||||
|
||||
@@ -21,26 +23,28 @@ use {
|
||||
std::future::{self, Future},
|
||||
};
|
||||
|
||||
type StaticFieldCallback = Box<dyn FnOnce(&RawLua) -> Result<()> + 'static>;
|
||||
|
||||
/// Handle to registry for userdata methods and metamethods.
|
||||
pub struct UserDataRegistry<'a, T: 'static> {
|
||||
pub struct UserDataRegistry<T: 'static> {
|
||||
// Fields
|
||||
pub(crate) fields: Vec<(String, Callback<'a>)>,
|
||||
pub(crate) field_getters: Vec<(String, Callback<'a>)>,
|
||||
pub(crate) field_setters: Vec<(String, Callback<'a>)>,
|
||||
pub(crate) meta_fields: Vec<(String, Callback<'a>)>,
|
||||
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<'a>)>,
|
||||
pub(crate) methods: Vec<(String, Callback)>,
|
||||
#[cfg(feature = "async")]
|
||||
pub(crate) async_methods: Vec<(String, AsyncCallback<'a>)>,
|
||||
pub(crate) meta_methods: Vec<(String, Callback<'a>)>,
|
||||
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<'a>)>,
|
||||
pub(crate) async_meta_methods: Vec<(String, AsyncCallback)>,
|
||||
|
||||
_type: PhantomData<T>,
|
||||
}
|
||||
|
||||
impl<'a, T: 'static> UserDataRegistry<'a, T> {
|
||||
impl<T: 'static> UserDataRegistry<T> {
|
||||
pub(crate) const fn new() -> Self {
|
||||
UserDataRegistry {
|
||||
fields: Vec::new(),
|
||||
@@ -57,9 +61,9 @@ impl<'a, T: 'static> UserDataRegistry<'a, T> {
|
||||
}
|
||||
}
|
||||
|
||||
fn box_method<M, A, R>(name: &str, method: M) -> Callback<'a>
|
||||
fn box_method<M, A, R>(name: &str, method: M) -> Callback
|
||||
where
|
||||
M: Fn(&'a Lua, &T, A) -> Result<R> + MaybeSend + 'static,
|
||||
M: Fn(&Lua, &T, A) -> Result<R> + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
R: IntoLuaMulti,
|
||||
{
|
||||
@@ -77,13 +81,13 @@ impl<'a, T: 'static> UserDataRegistry<'a, T> {
|
||||
}
|
||||
let state = rawlua.state();
|
||||
// Find absolute "self" index before processing args
|
||||
let index = ffi::lua_absindex(state, -nargs);
|
||||
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(index)) {
|
||||
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, index));
|
||||
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)),
|
||||
@@ -91,9 +95,9 @@ impl<'a, T: 'static> UserDataRegistry<'a, T> {
|
||||
})
|
||||
}
|
||||
|
||||
fn box_method_mut<M, A, R>(name: &str, method: M) -> Callback<'a>
|
||||
fn box_method_mut<M, A, R>(name: &str, method: M) -> Callback
|
||||
where
|
||||
M: FnMut(&'a Lua, &mut T, A) -> Result<R> + MaybeSend + 'static,
|
||||
M: FnMut(&Lua, &mut T, A) -> Result<R> + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
R: IntoLuaMulti,
|
||||
{
|
||||
@@ -113,13 +117,13 @@ impl<'a, T: 'static> UserDataRegistry<'a, T> {
|
||||
}
|
||||
let state = rawlua.state();
|
||||
// Find absolute "self" index before processing args
|
||||
let index = ffi::lua_absindex(state, -nargs);
|
||||
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(index)) {
|
||||
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, index));
|
||||
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)),
|
||||
@@ -128,11 +132,11 @@ impl<'a, T: 'static> UserDataRegistry<'a, T> {
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
fn box_async_method<M, A, MR, R>(name: &str, method: M) -> AsyncCallback<'a>
|
||||
fn box_async_method<M, A, MR, R>(name: &str, method: M) -> AsyncCallback
|
||||
where
|
||||
M: Fn(&'a Lua, &'a T, A) -> MR + MaybeSend + 'static,
|
||||
M: Fn(Lua, UserDataRef<T>, A) -> MR + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
MR: Future<Output = Result<R>> + 'a,
|
||||
MR: Future<Output = Result<R>> + MaybeSend + 'static,
|
||||
R: IntoLuaMulti,
|
||||
{
|
||||
let name = get_function_name::<T>(name);
|
||||
@@ -145,39 +149,33 @@ impl<'a, T: 'static> UserDataRegistry<'a, T> {
|
||||
};
|
||||
}
|
||||
|
||||
Box::new(move |rawlua, mut args| unsafe {
|
||||
let this = args
|
||||
.pop_front()
|
||||
.ok_or_else(|| Error::from_lua_conversion("missing argument", "userdata", None));
|
||||
let lua = rawlua.lua();
|
||||
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) = (rawlua.ref_thread(), this.0.index);
|
||||
match try_self_arg!(this.type_id()) {
|
||||
Some(id) if id == TypeId::of::<T>() => {
|
||||
let ud = try_self_arg!(borrow_userdata_ref::<T>(ref_thread, index));
|
||||
let args = match args {
|
||||
Ok(args) => args,
|
||||
Err(e) => return Box::pin(future::ready(Err(e))),
|
||||
};
|
||||
let fut = method(lua, ud.get_ref(), args);
|
||||
Box::pin(async move { fut.await?.push_into_stack_multi(rawlua) })
|
||||
}
|
||||
_ => {
|
||||
let err = Error::bad_self_argument(&name, Error::UserDataTypeMismatch);
|
||||
Box::pin(future::ready(Err(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<'a>
|
||||
fn box_async_method_mut<M, A, MR, R>(name: &str, method: M) -> AsyncCallback
|
||||
where
|
||||
M: Fn(&'a Lua, &'a mut T, A) -> MR + MaybeSend + 'static,
|
||||
M: Fn(Lua, UserDataRefMut<T>, A) -> MR + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
MR: Future<Output = Result<R>> + 'a,
|
||||
MR: Future<Output = Result<R>> + MaybeSend + 'static,
|
||||
R: IntoLuaMulti,
|
||||
{
|
||||
let name = get_function_name::<T>(name);
|
||||
@@ -190,36 +188,30 @@ impl<'a, T: 'static> UserDataRegistry<'a, T> {
|
||||
};
|
||||
}
|
||||
|
||||
Box::new(move |rawlua, mut args| unsafe {
|
||||
let this = args
|
||||
.pop_front()
|
||||
.ok_or_else(|| Error::from_lua_conversion("missing argument", "userdata", None));
|
||||
let lua = rawlua.lua();
|
||||
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) = (rawlua.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!(borrow_userdata_mut::<T>(ref_thread, index));
|
||||
let args = match args {
|
||||
Ok(args) => args,
|
||||
Err(e) => return Box::pin(future::ready(Err(e))),
|
||||
};
|
||||
let fut = method(lua, ud.get_mut(), args);
|
||||
Box::pin(async move { fut.await?.push_into_stack_multi(rawlua) })
|
||||
}
|
||||
_ => {
|
||||
let err = Error::bad_self_argument(&name, Error::UserDataTypeMismatch);
|
||||
Box::pin(future::ready(Err(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<'a>
|
||||
fn box_function<F, A, R>(name: &str, function: F) -> Callback
|
||||
where
|
||||
F: Fn(&'a Lua, A) -> Result<R> + MaybeSend + 'static,
|
||||
F: Fn(&Lua, A) -> Result<R> + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
R: IntoLuaMulti,
|
||||
{
|
||||
@@ -230,9 +222,9 @@ impl<'a, T: 'static> UserDataRegistry<'a, T> {
|
||||
})
|
||||
}
|
||||
|
||||
fn box_function_mut<F, A, R>(name: &str, function: F) -> Callback<'a>
|
||||
fn box_function_mut<F, A, R>(name: &str, function: F) -> Callback
|
||||
where
|
||||
F: FnMut(&'a Lua, A) -> Result<R> + MaybeSend + 'static,
|
||||
F: FnMut(&Lua, A) -> Result<R> + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
R: IntoLuaMulti,
|
||||
{
|
||||
@@ -248,29 +240,26 @@ impl<'a, T: 'static> UserDataRegistry<'a, T> {
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
fn box_async_function<F, A, FR, R>(name: &str, function: F) -> AsyncCallback<'a>
|
||||
fn box_async_function<F, A, FR, R>(name: &str, function: F) -> AsyncCallback
|
||||
where
|
||||
F: Fn(&'a Lua, A) -> FR + MaybeSend + 'static,
|
||||
F: Fn(Lua, A) -> FR + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
FR: Future<Output = Result<R>> + 'a,
|
||||
FR: Future<Output = Result<R>> + MaybeSend + 'static,
|
||||
R: IntoLuaMulti,
|
||||
{
|
||||
let name = get_function_name::<T>(name);
|
||||
Box::new(move |rawlua, args| unsafe {
|
||||
let lua = rawlua.lua();
|
||||
let args = match A::from_lua_args(args, 1, Some(&name), lua) {
|
||||
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 fut = function(lua, args);
|
||||
Box::pin(async move { fut.await?.push_into_stack_multi(rawlua) })
|
||||
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<V>(lua: &Lua, name: &str, value: V) -> Result<Value>
|
||||
where
|
||||
V: IntoLua,
|
||||
{
|
||||
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 {
|
||||
@@ -293,19 +282,21 @@ fn get_function_name<T>(name: &str) -> StdString {
|
||||
format!("{}.{name}", short_type_name::<T>())
|
||||
}
|
||||
|
||||
impl<'a, T: 'static> UserDataFields<'a, T> for UserDataRegistry<'a, T> {
|
||||
impl<T: 'static> UserDataFields<T> for UserDataRegistry<T> {
|
||||
fn add_field<V>(&mut self, name: impl ToString, value: V)
|
||||
where
|
||||
V: IntoLua + Clone + 'static,
|
||||
V: IntoLua + 'static,
|
||||
{
|
||||
let name = name.to_string();
|
||||
let callback = Box::new(move |lua, _| unsafe { value.clone().push_into_stack_multi(lua) });
|
||||
self.fields.push((name, callback));
|
||||
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(&'a Lua, &T) -> Result<R> + MaybeSend + 'static,
|
||||
M: Fn(&Lua, &T) -> Result<R> + MaybeSend + 'static,
|
||||
R: IntoLua,
|
||||
{
|
||||
let name = name.to_string();
|
||||
@@ -315,7 +306,7 @@ impl<'a, T: 'static> UserDataFields<'a, T> for UserDataRegistry<'a, T> {
|
||||
|
||||
fn add_field_method_set<M, A>(&mut self, name: impl ToString, method: M)
|
||||
where
|
||||
M: FnMut(&'a Lua, &mut T, A) -> Result<()> + MaybeSend + 'static,
|
||||
M: FnMut(&Lua, &mut T, A) -> Result<()> + MaybeSend + 'static,
|
||||
A: FromLua,
|
||||
{
|
||||
let name = name.to_string();
|
||||
@@ -325,7 +316,7 @@ impl<'a, T: 'static> UserDataFields<'a, T> for UserDataRegistry<'a, T> {
|
||||
|
||||
fn add_field_function_get<F, R>(&mut self, name: impl ToString, function: F)
|
||||
where
|
||||
F: Fn(&'a Lua, AnyUserData) -> Result<R> + MaybeSend + 'static,
|
||||
F: Fn(&Lua, AnyUserData) -> Result<R> + MaybeSend + 'static,
|
||||
R: IntoLua,
|
||||
{
|
||||
let name = name.to_string();
|
||||
@@ -335,7 +326,7 @@ impl<'a, T: 'static> UserDataFields<'a, T> for UserDataRegistry<'a, T> {
|
||||
|
||||
fn add_field_function_set<F, A>(&mut self, name: impl ToString, mut function: F)
|
||||
where
|
||||
F: FnMut(&'a Lua, AnyUserData, A) -> Result<()> + MaybeSend + 'static,
|
||||
F: FnMut(&Lua, AnyUserData, A) -> Result<()> + MaybeSend + 'static,
|
||||
A: FromLua,
|
||||
{
|
||||
let name = name.to_string();
|
||||
@@ -345,37 +336,37 @@ impl<'a, T: 'static> UserDataFields<'a, T> for UserDataRegistry<'a, T> {
|
||||
|
||||
fn add_meta_field<V>(&mut self, name: impl ToString, value: V)
|
||||
where
|
||||
V: IntoLua + Clone + 'static,
|
||||
V: IntoLua + 'static,
|
||||
{
|
||||
let name = name.to_string();
|
||||
self.meta_fields.push((
|
||||
name.clone(),
|
||||
Box::new(move |lua, _| unsafe {
|
||||
Self::check_meta_field(lua.lua(), &name, value.clone())?.push_into_stack_multi(lua)
|
||||
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: Fn(&'a Lua) -> Result<R> + MaybeSend + 'static,
|
||||
F: FnOnce(&Lua) -> Result<R> + 'static,
|
||||
R: IntoLua,
|
||||
{
|
||||
let name = name.to_string();
|
||||
self.meta_fields.push((
|
||||
name.clone(),
|
||||
Box::new(move |rawlua, _| unsafe {
|
||||
Box::new(move |rawlua| unsafe {
|
||||
let lua = rawlua.lua();
|
||||
Self::check_meta_field(lua, &name, f(lua)?)?.push_into_stack_multi(rawlua)
|
||||
Self::check_meta_field(lua, &name, f(lua)?)?.push_into_stack(rawlua)
|
||||
}),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: 'static> UserDataMethods<'a, T> for UserDataRegistry<'a, T> {
|
||||
impl<T: 'static> UserDataMethods<T> for UserDataRegistry<T> {
|
||||
fn add_method<M, A, R>(&mut self, name: impl ToString, method: M)
|
||||
where
|
||||
M: Fn(&'a Lua, &T, A) -> Result<R> + MaybeSend + 'static,
|
||||
M: Fn(&Lua, &T, A) -> Result<R> + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
R: IntoLuaMulti,
|
||||
{
|
||||
@@ -386,7 +377,7 @@ impl<'a, T: 'static> UserDataMethods<'a, T> for UserDataRegistry<'a, T> {
|
||||
|
||||
fn add_method_mut<M, A, R>(&mut self, name: impl ToString, method: M)
|
||||
where
|
||||
M: FnMut(&'a Lua, &mut T, A) -> Result<R> + MaybeSend + 'static,
|
||||
M: FnMut(&Lua, &mut T, A) -> Result<R> + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
R: IntoLuaMulti,
|
||||
{
|
||||
@@ -398,9 +389,9 @@ impl<'a, T: 'static> UserDataMethods<'a, T> for UserDataRegistry<'a, T> {
|
||||
#[cfg(feature = "async")]
|
||||
fn add_async_method<M, A, MR, R>(&mut self, name: impl ToString, method: M)
|
||||
where
|
||||
M: Fn(&'a Lua, &'a T, A) -> MR + MaybeSend + 'static,
|
||||
M: Fn(Lua, UserDataRef<T>, A) -> MR + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
MR: Future<Output = Result<R>> + 'a,
|
||||
MR: Future<Output = Result<R>> + MaybeSend + 'static,
|
||||
R: IntoLuaMulti,
|
||||
{
|
||||
let name = name.to_string();
|
||||
@@ -411,9 +402,9 @@ impl<'a, T: 'static> UserDataMethods<'a, T> for UserDataRegistry<'a, T> {
|
||||
#[cfg(feature = "async")]
|
||||
fn add_async_method_mut<M, A, MR, R>(&mut self, name: impl ToString, method: M)
|
||||
where
|
||||
M: Fn(&'a Lua, &'a mut T, A) -> MR + MaybeSend + 'static,
|
||||
M: Fn(Lua, UserDataRefMut<T>, A) -> MR + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
MR: Future<Output = Result<R>> + 'a,
|
||||
MR: Future<Output = Result<R>> + MaybeSend + 'static,
|
||||
R: IntoLuaMulti,
|
||||
{
|
||||
let name = name.to_string();
|
||||
@@ -423,7 +414,7 @@ impl<'a, T: 'static> UserDataMethods<'a, T> for UserDataRegistry<'a, T> {
|
||||
|
||||
fn add_function<F, A, R>(&mut self, name: impl ToString, function: F)
|
||||
where
|
||||
F: Fn(&'a Lua, A) -> Result<R> + MaybeSend + 'static,
|
||||
F: Fn(&Lua, A) -> Result<R> + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
R: IntoLuaMulti,
|
||||
{
|
||||
@@ -434,7 +425,7 @@ impl<'a, T: 'static> UserDataMethods<'a, T> for UserDataRegistry<'a, T> {
|
||||
|
||||
fn add_function_mut<F, A, R>(&mut self, name: impl ToString, function: F)
|
||||
where
|
||||
F: FnMut(&'a Lua, A) -> Result<R> + MaybeSend + 'static,
|
||||
F: FnMut(&Lua, A) -> Result<R> + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
R: IntoLuaMulti,
|
||||
{
|
||||
@@ -446,9 +437,9 @@ impl<'a, T: 'static> UserDataMethods<'a, T> for UserDataRegistry<'a, T> {
|
||||
#[cfg(feature = "async")]
|
||||
fn add_async_function<F, A, FR, R>(&mut self, name: impl ToString, function: F)
|
||||
where
|
||||
F: Fn(&'a Lua, A) -> FR + MaybeSend + 'static,
|
||||
F: Fn(Lua, A) -> FR + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
FR: Future<Output = Result<R>> + 'a,
|
||||
FR: Future<Output = Result<R>> + MaybeSend + 'static,
|
||||
R: IntoLuaMulti,
|
||||
{
|
||||
let name = name.to_string();
|
||||
@@ -458,7 +449,7 @@ impl<'a, T: 'static> UserDataMethods<'a, T> for UserDataRegistry<'a, T> {
|
||||
|
||||
fn add_meta_method<M, A, R>(&mut self, name: impl ToString, method: M)
|
||||
where
|
||||
M: Fn(&'a Lua, &T, A) -> Result<R> + MaybeSend + 'static,
|
||||
M: Fn(&Lua, &T, A) -> Result<R> + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
R: IntoLuaMulti,
|
||||
{
|
||||
@@ -469,7 +460,7 @@ impl<'a, T: 'static> UserDataMethods<'a, T> for UserDataRegistry<'a, T> {
|
||||
|
||||
fn add_meta_method_mut<M, A, R>(&mut self, name: impl ToString, method: M)
|
||||
where
|
||||
M: FnMut(&'a Lua, &mut T, A) -> Result<R> + MaybeSend + 'static,
|
||||
M: FnMut(&Lua, &mut T, A) -> Result<R> + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
R: IntoLuaMulti,
|
||||
{
|
||||
@@ -481,9 +472,9 @@ impl<'a, T: 'static> UserDataMethods<'a, T> for UserDataRegistry<'a, T> {
|
||||
#[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(&'a Lua, &'a T, A) -> MR + MaybeSend + 'static,
|
||||
M: Fn(Lua, UserDataRef<T>, A) -> MR + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
MR: Future<Output = Result<R>> + 'a,
|
||||
MR: Future<Output = Result<R>> + MaybeSend + 'static,
|
||||
R: IntoLuaMulti,
|
||||
{
|
||||
let name = name.to_string();
|
||||
@@ -494,9 +485,9 @@ impl<'a, T: 'static> UserDataMethods<'a, T> for UserDataRegistry<'a, T> {
|
||||
#[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(&'a Lua, &'a mut T, A) -> MR + MaybeSend + 'static,
|
||||
M: Fn(Lua, UserDataRefMut<T>, A) -> MR + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
MR: Future<Output = Result<R>> + 'a,
|
||||
MR: Future<Output = Result<R>> + MaybeSend + 'static,
|
||||
R: IntoLuaMulti,
|
||||
{
|
||||
let name = name.to_string();
|
||||
@@ -506,7 +497,7 @@ impl<'a, T: 'static> UserDataMethods<'a, T> for UserDataRegistry<'a, T> {
|
||||
|
||||
fn add_meta_function<F, A, R>(&mut self, name: impl ToString, function: F)
|
||||
where
|
||||
F: Fn(&'a Lua, A) -> Result<R> + MaybeSend + 'static,
|
||||
F: Fn(&Lua, A) -> Result<R> + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
R: IntoLuaMulti,
|
||||
{
|
||||
@@ -517,7 +508,7 @@ impl<'a, T: 'static> UserDataMethods<'a, T> for UserDataRegistry<'a, T> {
|
||||
|
||||
fn add_meta_function_mut<F, A, R>(&mut self, name: impl ToString, function: F)
|
||||
where
|
||||
F: FnMut(&'a Lua, A) -> Result<R> + MaybeSend + 'static,
|
||||
F: FnMut(&Lua, A) -> Result<R> + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
R: IntoLuaMulti,
|
||||
{
|
||||
@@ -529,9 +520,9 @@ impl<'a, T: 'static> UserDataMethods<'a, T> for UserDataRegistry<'a, T> {
|
||||
#[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(&'a Lua, A) -> FR + MaybeSend + 'static,
|
||||
F: Fn(Lua, A) -> FR + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
FR: Future<Output = Result<R>> + 'a,
|
||||
FR: Future<Output = Result<R>> + MaybeSend + 'static,
|
||||
R: IntoLuaMulti,
|
||||
{
|
||||
let name = name.to_string();
|
||||
|
||||
+2
-2
@@ -21,7 +21,7 @@ impl TypeKey for String {
|
||||
|
||||
static CALLBACK_TYPE_KEY: u8 = 0;
|
||||
|
||||
impl TypeKey for Callback<'static> {
|
||||
impl TypeKey for Callback {
|
||||
#[inline(always)]
|
||||
fn type_key() -> *const c_void {
|
||||
&CALLBACK_TYPE_KEY as *const u8 as *const c_void
|
||||
@@ -41,7 +41,7 @@ impl TypeKey for CallbackUpvalue {
|
||||
static ASYNC_CALLBACK_TYPE_KEY: u8 = 0;
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
impl TypeKey for AsyncCallback<'static> {
|
||||
impl TypeKey for AsyncCallback {
|
||||
#[inline(always)]
|
||||
fn type_key() -> *const c_void {
|
||||
&ASYNC_CALLBACK_TYPE_KEY as *const u8 as *const c_void
|
||||
|
||||
+24
-61
@@ -1,3 +1,4 @@
|
||||
use std::cell::RefCell;
|
||||
use std::cmp::Ordering;
|
||||
use std::collections::{vec_deque, HashSet, VecDeque};
|
||||
use std::ops::{Deref, DerefMut};
|
||||
@@ -23,7 +24,7 @@ use {
|
||||
crate::table::SerializableTable,
|
||||
rustc_hash::FxHashSet,
|
||||
serde::ser::{self, Serialize, Serializer},
|
||||
std::{cell::RefCell, rc::Rc, result::Result as StdResult},
|
||||
std::{rc::Rc, result::Result as StdResult},
|
||||
};
|
||||
|
||||
/// A dynamically typed Lua value. The `String`, `Table`, `Function`, `Thread`, and `UserData`
|
||||
@@ -728,7 +729,7 @@ pub trait FromLua: Sized {
|
||||
#[doc(hidden)]
|
||||
#[inline]
|
||||
unsafe fn from_stack(idx: c_int, lua: &RawLua) -> Result<Self> {
|
||||
Self::from_lua(lua.stack_value(idx), lua.lua())
|
||||
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`.
|
||||
@@ -745,92 +746,54 @@ pub trait FromLua: Sized {
|
||||
}
|
||||
|
||||
/// Multiple Lua values used for both argument passing and also for multiple return values.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct MultiValue {
|
||||
deque: VecDeque<Value>,
|
||||
// FIXME
|
||||
// lua: Option<&'static Lua>,
|
||||
}
|
||||
|
||||
impl Drop for MultiValue {
|
||||
fn drop(&mut self) {
|
||||
// FIXME
|
||||
// if let Some(lua) = self.lua {
|
||||
// let vec = mem::take(&mut self.deque);
|
||||
// lua.push_multivalue_to_pool(vec);
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for MultiValue {
|
||||
#[inline]
|
||||
fn default() -> MultiValue {
|
||||
MultiValue::new()
|
||||
}
|
||||
}
|
||||
#[derive(Default, Debug, Clone)]
|
||||
pub struct MultiValue(VecDeque<Value>);
|
||||
|
||||
impl Deref for MultiValue {
|
||||
type Target = VecDeque<Value>;
|
||||
|
||||
#[inline]
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.deque
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl DerefMut for MultiValue {
|
||||
#[inline]
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.deque
|
||||
&mut self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl MultiValue {
|
||||
/// Creates an empty `MultiValue` containing no values.
|
||||
#[inline]
|
||||
pub const fn new() -> MultiValue {
|
||||
MultiValue {
|
||||
deque: VecDeque::new(),
|
||||
// lua: None,
|
||||
}
|
||||
MultiValue(VecDeque::new())
|
||||
}
|
||||
|
||||
/// Similar to `new` but can reuse previously used container with allocated capacity.
|
||||
#[inline]
|
||||
pub(crate) fn with_lua_and_capacity(_lua: &Lua, capacity: usize) -> MultiValue {
|
||||
// FIXME
|
||||
// let deque = lua
|
||||
// .pop_multivalue_from_pool()
|
||||
// .map(|mut deque| {
|
||||
// if capacity > 0 {
|
||||
// deque.reserve(capacity);
|
||||
// }
|
||||
// deque
|
||||
// })
|
||||
// .unwrap_or_else(|| VecDeque::with_capacity(capacity));
|
||||
let deque = VecDeque::with_capacity(capacity);
|
||||
MultiValue {
|
||||
deque,
|
||||
// lua: Some(lua),
|
||||
}
|
||||
/// 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 extend_from_values(&mut self, iter: impl IntoIterator<Item = Result<Value>>) -> Result<()> {
|
||||
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 {
|
||||
self.push_back(value?);
|
||||
multi_value.push_back(value.into_lua(lua)?);
|
||||
}
|
||||
Ok(())
|
||||
Ok(multi_value)
|
||||
}
|
||||
}
|
||||
|
||||
impl FromIterator<Value> for MultiValue {
|
||||
#[inline]
|
||||
fn from_iter<I: IntoIterator<Item = Value>>(iter: I) -> Self {
|
||||
let deque = VecDeque::from_iter(iter);
|
||||
MultiValue {
|
||||
deque,
|
||||
// lua: None,
|
||||
}
|
||||
let mut multi_value = MultiValue::new();
|
||||
multi_value.extend(iter);
|
||||
multi_value
|
||||
}
|
||||
}
|
||||
|
||||
@@ -840,7 +803,7 @@ impl IntoIterator for MultiValue {
|
||||
|
||||
#[inline]
|
||||
fn into_iter(mut self) -> Self::IntoIter {
|
||||
let deque = mem::take(&mut self.deque);
|
||||
let deque = mem::take(&mut self.0);
|
||||
mem::forget(self);
|
||||
deque.into_iter()
|
||||
}
|
||||
@@ -852,7 +815,7 @@ impl<'a> IntoIterator for &'a MultiValue {
|
||||
|
||||
#[inline]
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.deque.iter()
|
||||
self.0.iter()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -910,9 +873,9 @@ pub trait FromLuaMulti: Sized {
|
||||
#[doc(hidden)]
|
||||
#[inline]
|
||||
unsafe fn from_stack_multi(nvals: c_int, lua: &RawLua) -> Result<Self> {
|
||||
let mut values = MultiValue::with_lua_and_capacity(lua.lua(), nvals as usize);
|
||||
let mut values = MultiValue::with_capacity(nvals as usize);
|
||||
for idx in 0..nvals {
|
||||
values.push_back(lua.stack_value(-nvals + idx));
|
||||
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
|
||||
|
||||
+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"
|
||||
|
||||
+56
-52
@@ -1,12 +1,13 @@
|
||||
#![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,
|
||||
Error, Function, Lua, LuaOptions, MultiValue, ObjectLike, Result, StdLib, Table, UserData,
|
||||
UserDataMethods, Value,
|
||||
};
|
||||
|
||||
@@ -72,16 +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(())
|
||||
}
|
||||
@@ -95,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");
|
||||
@@ -158,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(())
|
||||
}
|
||||
@@ -227,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(())
|
||||
}
|
||||
@@ -259,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;
|
||||
@@ -307,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)?;
|
||||
|
||||
@@ -323,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)?;
|
||||
|
||||
@@ -333,19 +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(())
|
||||
}
|
||||
@@ -365,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(())
|
||||
}
|
||||
@@ -377,13 +379,13 @@ async fn test_async_userdata() -> Result<()> {
|
||||
struct MyUserData(u64);
|
||||
|
||||
impl UserData for MyUserData {
|
||||
fn add_methods<'a, M: UserDataMethods<'a, 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(())
|
||||
@@ -414,7 +416,7 @@ async fn test_async_userdata() -> Result<()> {
|
||||
#[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,
|
||||
@@ -460,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(())
|
||||
}
|
||||
@@ -476,7 +479,7 @@ async fn test_async_thread_error() -> Result<()> {
|
||||
struct MyUserData;
|
||||
|
||||
impl UserData for MyUserData {
|
||||
fn add_methods<'a, M: UserDataMethods<'a, Self>>(methods: &mut M) {
|
||||
fn add_methods<M: UserDataMethods<Self>>(methods: &mut M) {
|
||||
methods.add_meta_method("__tostring", |_, _this, ()| Ok("myuserdata error"))
|
||||
}
|
||||
}
|
||||
@@ -485,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")),
|
||||
@@ -497,21 +500,22 @@ async fn test_async_thread_error() -> Result<()> {
|
||||
|
||||
#[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#"
|
||||
|
||||
+1
-1
@@ -64,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
|
||||
}
|
||||
|
||||
+84
-22
@@ -20,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(())
|
||||
}
|
||||
@@ -37,7 +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(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_string_from_lua() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
// From stack
|
||||
let f = lua.create_function(|_, s: mlua::String| Ok(s))?;
|
||||
let s = f.call::<String>("hello, world!")?;
|
||||
assert_eq!(s, "hello, world!");
|
||||
|
||||
// Should fallback to default conversion
|
||||
let s = f.call::<String>(42)?;
|
||||
assert_eq!(s, "42");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -53,8 +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")?);
|
||||
f.call::<()>((&t, "hello"))?;
|
||||
assert_eq!("hello", t.get::<String>("s")?);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -71,7 +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")?);
|
||||
assert_eq!(f, table.get::<Function>("f")?);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -89,7 +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")?);
|
||||
assert_eq!(th, table.get::<Thread>("th")?);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -106,8 +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")?);
|
||||
assert_eq!(ud, table.get::<AnyUserData>("ud")?);
|
||||
assert_eq!("hello", *table.get::<UserDataRef<String>>("ud")?);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -128,20 +144,20 @@ fn test_registry_value_into_lua() -> Result<()> {
|
||||
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(())
|
||||
}
|
||||
@@ -152,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(())
|
||||
}
|
||||
@@ -285,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(())
|
||||
@@ -307,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(())
|
||||
@@ -328,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(())
|
||||
@@ -345,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(())
|
||||
}
|
||||
|
||||
+2
-2
@@ -18,7 +18,7 @@ fn test_error_context() -> Result<()> {
|
||||
|
||||
let func2 = lua.create_function(|lua, ()| {
|
||||
lua.globals()
|
||||
.get::<_, String>("nonextant")
|
||||
.get::<String>("nonextant")
|
||||
.with_context(|_| "failed to find global")
|
||||
})?;
|
||||
lua.globals().set("func2", func2)?;
|
||||
@@ -36,7 +36,7 @@ fn test_error_context() -> Result<()> {
|
||||
.context("some context")
|
||||
.context("some new context")
|
||||
})?;
|
||||
let res = func3.call::<_, ()>(()).err().unwrap();
|
||||
let res = func3.call::<()>(()).err().unwrap();
|
||||
let Error::CallbackError { cause, .. } = &res else {
|
||||
unreachable!()
|
||||
};
|
||||
|
||||
+38
-31
@@ -14,8 +14,8 @@ fn test_function() -> Result<()> {
|
||||
)
|
||||
.exec()?;
|
||||
|
||||
let concat = globals.get::<_, Function>("concat")?;
|
||||
assert_eq!(concat.call::<_, String>(("foo", "bar"))?, "foobar");
|
||||
let concat = globals.get::<Function>("concat")?;
|
||||
assert_eq!(concat.call::<String>(("foo", "bar"))?, "foobar");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -38,17 +38,17 @@ fn test_bind() -> Result<()> {
|
||||
)
|
||||
.exec()?;
|
||||
|
||||
let mut concat = globals.get::<_, Function>("concat")?;
|
||||
let mut concat = globals.get::<Function>("concat")?;
|
||||
concat = concat.bind("foo")?;
|
||||
concat = concat.bind("bar")?;
|
||||
concat = concat.bind(("baz", "baf"))?;
|
||||
assert_eq!(concat.call::<_, String>(())?, "foobarbazbaf");
|
||||
assert_eq!(concat.call::<_, String>(("hi", "wut"))?, "foobarbazbafhiwut");
|
||||
assert_eq!(concat.call::<String>(())?, "foobarbazbaf");
|
||||
assert_eq!(concat.call::<String>(("hi", "wut"))?, "foobarbazbafhiwut");
|
||||
|
||||
let mut concat2 = globals.get::<_, Function>("concat")?;
|
||||
let mut concat2 = globals.get::<Function>("concat")?;
|
||||
concat2 = concat2.bind(())?;
|
||||
assert_eq!(concat2.call::<_, String>(())?, "");
|
||||
assert_eq!(concat2.call::<_, String>(("ab", "cd"))?, "abcd");
|
||||
assert_eq!(concat2.call::<String>(())?, "");
|
||||
assert_eq!(concat2.call::<String>(("ab", "cd"))?, "abcd");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -70,11 +70,11 @@ fn test_rust_function() -> Result<()> {
|
||||
)
|
||||
.exec()?;
|
||||
|
||||
let lua_function = globals.get::<_, Function>("lua_function")?;
|
||||
let lua_function = globals.get::<Function>("lua_function")?;
|
||||
let rust_function = lua.create_function(|_, ()| Ok("hello"))?;
|
||||
|
||||
globals.set("rust_function", rust_function)?;
|
||||
assert_eq!(lua_function.call::<_, String>(())?, "hello");
|
||||
assert_eq!(lua_function.call::<String>(())?, "hello");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -84,14 +84,14 @@ fn test_c_function() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
unsafe extern "C-unwind" fn c_function(state: *mut mlua::lua_State) -> std::os::raw::c_int {
|
||||
let lua = Lua::init_from_ptr(state);
|
||||
lua.globals().set("c_function", true).unwrap();
|
||||
ffi::lua_pushboolean(state, 1);
|
||||
ffi::lua_setglobal(state, b"c_function\0" as *const _ as *const _);
|
||||
0
|
||||
}
|
||||
|
||||
let func = unsafe { lua.create_c_function(c_function)? };
|
||||
func.call(())?;
|
||||
assert_eq!(lua.globals().get::<_, bool>("c_function")?, true);
|
||||
func.call::<()>(())?;
|
||||
assert_eq!(lua.globals().get::<bool>("c_function")?, true);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -106,7 +106,7 @@ fn test_dump() -> Result<()> {
|
||||
.eval::<Function>()?;
|
||||
let concat = lua.load(&concat_lua.dump(false)).into_function()?;
|
||||
|
||||
assert_eq!(concat.call::<_, String>(("foo", "bar"))?, "foobar");
|
||||
assert_eq!(concat.call::<String>(("foo", "bar"))?, "foobar");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -134,14 +134,14 @@ fn test_function_environment() -> Result<()> {
|
||||
)
|
||||
.eval::<Function>()?;
|
||||
let lua_func2 = lua.load("return hello").into_function()?;
|
||||
assert_eq!(lua_func.call::<_, String>(())?, "global");
|
||||
assert_eq!(lua_func.call::<String>(())?, "global");
|
||||
assert_eq!(lua_func.environment(), Some(lua.globals()));
|
||||
|
||||
// Test changing the environment
|
||||
let env = lua.create_table_from([("hello", "local")])?;
|
||||
assert!(lua_func.set_environment(env.clone())?);
|
||||
assert_eq!(lua_func.call::<_, String>(())?, "local");
|
||||
assert_eq!(lua_func2.call::<_, String>(())?, "global");
|
||||
assert_eq!(lua_func.call::<String>(())?, "local");
|
||||
assert_eq!(lua_func2.call::<String>(())?, "global");
|
||||
|
||||
// More complex case
|
||||
lua.load(
|
||||
@@ -154,11 +154,11 @@ fn test_function_environment() -> Result<()> {
|
||||
"#,
|
||||
)
|
||||
.exec()?;
|
||||
let lucky = lua.globals().get::<_, Function>("lucky")?;
|
||||
assert_eq!(lucky.call::<_, String>(())?, "number is 15");
|
||||
let new_env = lua.globals().get::<_, Table>("new_env")?;
|
||||
let lucky = lua.globals().get::<Function>("lucky")?;
|
||||
assert_eq!(lucky.call::<String>(())?, "number is 15");
|
||||
let new_env = lua.globals().get::<Table>("new_env")?;
|
||||
lucky.set_environment(new_env)?;
|
||||
assert_eq!(lucky.call::<_, String>(())?, "15");
|
||||
assert_eq!(lucky.call::<String>(())?, "15");
|
||||
|
||||
// Test inheritance
|
||||
let lua_func2 = lua
|
||||
@@ -166,7 +166,14 @@ fn test_function_environment() -> Result<()> {
|
||||
.eval::<Function>()?;
|
||||
assert!(lua_func2.set_environment(env.clone())?);
|
||||
lua.gc_collect()?;
|
||||
assert_eq!(lua_func2.call::<_, String>(())?, "local");
|
||||
assert_eq!(lua_func2.call::<String>(())?, "local");
|
||||
|
||||
// Test getting environment set by chunk loader
|
||||
let chunk = lua
|
||||
.load("return hello")
|
||||
.set_environment(lua.create_table_from([("hello", "chunk")])?)
|
||||
.into_function()?;
|
||||
assert_eq!(chunk.environment().unwrap().get::<String>("hello")?, "chunk");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -186,8 +193,8 @@ fn test_function_info() -> Result<()> {
|
||||
.set_name("source1")
|
||||
.exec()?;
|
||||
|
||||
let function1 = globals.get::<_, Function>("function1")?;
|
||||
let function2 = function1.call::<_, Function>(())?;
|
||||
let function1 = globals.get::<Function>("function1")?;
|
||||
let function2 = function1.call::<Function>(())?;
|
||||
let function3 = lua.create_function(|_, ()| Ok(()))?;
|
||||
|
||||
let function1_info = function1.info();
|
||||
@@ -218,7 +225,7 @@ fn test_function_info() -> Result<()> {
|
||||
assert_eq!(function3_info.last_line_defined, None);
|
||||
assert_eq!(function3_info.what, "C");
|
||||
|
||||
let print_info = globals.get::<_, Function>("print")?.info();
|
||||
let print_info = globals.get::<Function>("print")?.info();
|
||||
#[cfg(feature = "luau")]
|
||||
assert_eq!(print_info.name.as_deref(), Some("print"));
|
||||
assert_eq!(print_info.source.as_deref(), Some("=[C]"));
|
||||
@@ -233,7 +240,7 @@ fn test_function_pointer() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let func1 = lua.load("return function() end").into_function()?;
|
||||
let func2 = func1.call::<_, Function>(())?;
|
||||
let func2 = func1.call::<Function>(())?;
|
||||
|
||||
assert_eq!(func1.to_pointer(), func1.clone().to_pointer());
|
||||
assert_ne!(func1.to_pointer(), func2.to_pointer());
|
||||
@@ -251,8 +258,8 @@ fn test_function_deep_clone() -> Result<()> {
|
||||
let func2 = func1.deep_clone();
|
||||
|
||||
assert_ne!(func1.to_pointer(), func2.to_pointer());
|
||||
assert_eq!(func1.call::<_, i32>(())?, 2);
|
||||
assert_eq!(func2.call::<_, i32>(())?, 3);
|
||||
assert_eq!(func1.call::<i32>(())?, 2);
|
||||
assert_eq!(func2.call::<i32>(())?, 3);
|
||||
|
||||
// Check that for Rust functions deep_clone is just a clone
|
||||
let rust_func = lua.create_function(|_, ()| Ok(42))?;
|
||||
@@ -276,10 +283,10 @@ fn test_function_wrap() -> Result<()> {
|
||||
"f",
|
||||
Function::wrap_mut(move |lua, ()| {
|
||||
_i = true;
|
||||
lua.globals().get::<_, Function>("f")?.call::<_, ()>(())
|
||||
lua.globals().get::<Function>("f")?.call::<()>(())
|
||||
}),
|
||||
)?;
|
||||
match lua.globals().get::<_, Function>("f")?.call::<_, ()>(()) {
|
||||
match lua.globals().get::<Function>("f")?.call::<()>(()) {
|
||||
Err(Error::CallbackError { ref cause, .. }) => match *cause.as_ref() {
|
||||
Error::CallbackError { ref cause, .. } => match *cause.as_ref() {
|
||||
Error::RecursiveMutCallback { .. } => {}
|
||||
|
||||
+2
-2
@@ -209,7 +209,7 @@ fn test_hook_swap_within_hook() -> Result<()> {
|
||||
"#,
|
||||
)
|
||||
.exec()?;
|
||||
assert_eq!(lua.globals().get::<_, i64>("ok")?, 2);
|
||||
assert_eq!(lua.globals().get::<i64>("ok")?, 2);
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
@@ -237,7 +237,7 @@ fn test_hook_threads() -> Result<()> {
|
||||
Ok(())
|
||||
});
|
||||
|
||||
co.resume(())?;
|
||||
co.resume::<()>(())?;
|
||||
lua.remove_hook();
|
||||
|
||||
let output = output.lock().unwrap();
|
||||
|
||||
+37
-26
@@ -14,7 +14,7 @@ use mlua::{
|
||||
#[test]
|
||||
fn test_version() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
assert!(lua.globals().get::<_, String>("_VERSION")?.starts_with("Luau 0."));
|
||||
assert!(lua.globals().get::<String>("_VERSION")?.starts_with("Luau 0."));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -22,8 +22,8 @@ fn test_version() -> Result<()> {
|
||||
fn test_require() -> Result<()> {
|
||||
// Ensure that require() is not available if package module is not loaded
|
||||
let mut lua = Lua::new_with(StdLib::NONE, LuaOptions::default())?;
|
||||
assert!(lua.globals().get::<_, Option<Value>>("require")?.is_none());
|
||||
assert!(lua.globals().get::<_, Option<Value>>("package")?.is_none());
|
||||
assert!(lua.globals().get::<Option<Value>>("require")?.is_none());
|
||||
assert!(lua.globals().get::<Option<Value>>("package")?.is_none());
|
||||
|
||||
if cfg!(target_arch = "wasm32") {
|
||||
// TODO: figure out why emscripten fails on file operations
|
||||
@@ -33,6 +33,17 @@ fn test_require() -> Result<()> {
|
||||
|
||||
lua = Lua::new();
|
||||
|
||||
// Check that require() can load stdlib modules (including `package`)
|
||||
lua.load(
|
||||
r#"
|
||||
local math = require("math")
|
||||
assert(math == _G.math, "math module does not match _G.math")
|
||||
local package = require("package")
|
||||
assert(package == _G.package, "package module does not match _G.package")
|
||||
"#,
|
||||
)
|
||||
.exec()?;
|
||||
|
||||
let temp_dir = tempfile::tempdir().unwrap();
|
||||
fs::write(
|
||||
temp_dir.path().join("module.luau"),
|
||||
@@ -46,7 +57,7 @@ fn test_require() -> Result<()> {
|
||||
)?;
|
||||
|
||||
lua.globals()
|
||||
.get::<_, Table>("package")?
|
||||
.get::<Table>("package")?
|
||||
.set("path", temp_dir.path().join("?.luau").to_string_lossy())?;
|
||||
|
||||
lua.load(
|
||||
@@ -70,7 +81,7 @@ fn test_require() -> Result<()> {
|
||||
|
||||
// Require binary module in safe mode
|
||||
lua.globals()
|
||||
.get::<_, Table>("package")?
|
||||
.get::<Table>("package")?
|
||||
.set("cpath", temp_dir.path().join("?.so").to_string_lossy())?;
|
||||
fs::write(temp_dir.path().join("dylib.so"), "")?;
|
||||
match lua.load("require('dylib')").exec() {
|
||||
@@ -162,7 +173,7 @@ fn test_vectors() -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(all(not(feature = "luau-vector4"), feature = "unstable"))]
|
||||
#[cfg(not(feature = "luau-vector4"))]
|
||||
#[test]
|
||||
fn test_vector_metatable() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
@@ -246,25 +257,25 @@ fn test_sandbox() -> Result<()> {
|
||||
lua.load("global = 123").exec()?;
|
||||
let n: i32 = lua.load("return global").eval()?;
|
||||
assert_eq!(n, 123);
|
||||
assert_eq!(lua.globals().get::<_, Option<i32>>("global")?, Some(123));
|
||||
assert_eq!(lua.globals().get::<Option<i32>>("global")?, Some(123));
|
||||
|
||||
// Threads should inherit "main" globals
|
||||
let f = lua.create_function(|lua, ()| lua.globals().get::<_, i32>("global"))?;
|
||||
let f = lua.create_function(|lua, ()| lua.globals().get::<i32>("global"))?;
|
||||
let co = lua.create_thread(f.clone())?;
|
||||
assert_eq!(co.resume::<_, Option<i32>>(())?, Some(123));
|
||||
assert_eq!(co.resume::<Option<i32>>(())?, Some(123));
|
||||
|
||||
// Sandboxed threads should also inherit "main" globals
|
||||
let co = lua.create_thread(f)?;
|
||||
co.sandbox()?;
|
||||
assert_eq!(co.resume::<_, Option<i32>>(())?, Some(123));
|
||||
assert_eq!(co.resume::<Option<i32>>(())?, Some(123));
|
||||
|
||||
lua.sandbox(false)?;
|
||||
|
||||
// Previously set variable `global` should be cleared now
|
||||
assert_eq!(lua.globals().get::<_, Option<i32>>("global")?, None);
|
||||
assert_eq!(lua.globals().get::<Option<i32>>("global")?, None);
|
||||
|
||||
// Readonly flags should be cleared as well
|
||||
let table = lua.globals().get::<_, Table>("table")?;
|
||||
let table = lua.globals().get::<Table>("table")?;
|
||||
table.set("test", "test")?;
|
||||
|
||||
Ok(())
|
||||
@@ -278,10 +289,10 @@ fn test_sandbox_nolibs() -> Result<()> {
|
||||
lua.load("global = 123").exec()?;
|
||||
let n: i32 = lua.load("return global").eval()?;
|
||||
assert_eq!(n, 123);
|
||||
assert_eq!(lua.globals().get::<_, Option<i32>>("global")?, Some(123));
|
||||
assert_eq!(lua.globals().get::<Option<i32>>("global")?, Some(123));
|
||||
|
||||
lua.sandbox(false)?;
|
||||
assert_eq!(lua.globals().get::<_, Option<i32>>("global")?, None);
|
||||
assert_eq!(lua.globals().get::<Option<i32>>("global")?, None);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -293,20 +304,20 @@ fn test_sandbox_threads() -> Result<()> {
|
||||
let f = lua.create_function(|lua, v: Value| lua.globals().set("global", v))?;
|
||||
|
||||
let co = lua.create_thread(f.clone())?;
|
||||
co.resume(321)?;
|
||||
co.resume::<()>(321)?;
|
||||
// The main state should see the `global` variable (as the thread is not sandboxed)
|
||||
assert_eq!(lua.globals().get::<_, Option<i32>>("global")?, Some(321));
|
||||
assert_eq!(lua.globals().get::<Option<i32>>("global")?, Some(321));
|
||||
|
||||
let co = lua.create_thread(f.clone())?;
|
||||
co.sandbox()?;
|
||||
co.resume(123)?;
|
||||
co.resume::<()>(123)?;
|
||||
// The main state should see the previous `global` value (as the thread is sandboxed)
|
||||
assert_eq!(lua.globals().get::<_, Option<i32>>("global")?, Some(321));
|
||||
assert_eq!(lua.globals().get::<Option<i32>>("global")?, Some(321));
|
||||
|
||||
// Try to reset the (sandboxed) thread
|
||||
co.reset(f)?;
|
||||
co.resume(111)?;
|
||||
assert_eq!(lua.globals().get::<_, Option<i32>>("global")?, Some(111));
|
||||
co.resume::<()>(111)?;
|
||||
assert_eq!(lua.globals().get::<Option<i32>>("global")?, Some(111));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -331,7 +342,7 @@ fn test_interrupts() -> Result<()> {
|
||||
"#,
|
||||
)
|
||||
.into_function()?;
|
||||
f.call(())?;
|
||||
f.call::<()>(())?;
|
||||
|
||||
assert!(interrupts_count.load(Ordering::Relaxed) > 0);
|
||||
|
||||
@@ -357,18 +368,18 @@ fn test_interrupts() -> Result<()> {
|
||||
)
|
||||
.into_function()?,
|
||||
)?;
|
||||
co.resume(())?;
|
||||
co.resume::<()>(())?;
|
||||
assert_eq!(co.status(), ThreadStatus::Resumable);
|
||||
let result: i32 = co.resume(())?;
|
||||
assert_eq!(result, 6);
|
||||
assert_eq!(yield_count.load(Ordering::Relaxed), 7);
|
||||
assert_eq!(co.status(), ThreadStatus::Unresumable);
|
||||
assert_eq!(co.status(), ThreadStatus::Finished);
|
||||
|
||||
//
|
||||
// Test errors in interrupts
|
||||
//
|
||||
lua.set_interrupt(|_| Err(Error::runtime("error from interrupt")));
|
||||
match f.call::<_, ()>(()) {
|
||||
match f.call::<()>(()) {
|
||||
Err(Error::CallbackError { cause, .. }) => match *cause {
|
||||
Error::RuntimeError(ref m) if m == "error from interrupt" => {}
|
||||
ref e => panic!("expected RuntimeError with a specific message, got {:?}", e),
|
||||
@@ -407,7 +418,7 @@ fn test_coverage() -> Result<()> {
|
||||
)
|
||||
.into_function()?;
|
||||
|
||||
f.call(())?;
|
||||
f.call::<()>(())?;
|
||||
|
||||
let mut report = Vec::new();
|
||||
f.coverage(|cov| {
|
||||
@@ -475,7 +486,7 @@ fn test_buffer() -> Result<()> {
|
||||
|
||||
// Check that we can pass buffer type to Lua
|
||||
let func = lua.create_function(|_, buf: Value| return buf.to_string())?;
|
||||
assert!(func.call::<_, String>(buf1)?.starts_with("buffer:"));
|
||||
assert!(func.call::<String>(buf1)?.starts_with("buffer:"));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
+4
-4
@@ -15,7 +15,7 @@ fn test_memory_limit() -> Result<()> {
|
||||
let f = lua
|
||||
.load("local t = {}; for i = 1,10000 do t[i] = i end")
|
||||
.into_function()?;
|
||||
f.call::<_, ()>(()).expect("should trigger no memory limit");
|
||||
f.call::<()>(()).expect("should trigger no memory limit");
|
||||
|
||||
if cfg!(feature = "luajit") && lua.set_memory_limit(0).is_err() {
|
||||
// seems this luajit version does not support memory limit
|
||||
@@ -23,13 +23,13 @@ fn test_memory_limit() -> Result<()> {
|
||||
}
|
||||
|
||||
lua.set_memory_limit(initial_memory + 10000)?;
|
||||
match f.call::<_, ()>(()) {
|
||||
match f.call::<()>(()) {
|
||||
Err(Error::MemoryError(_)) => {}
|
||||
something_else => panic!("did not trigger memory error: {:?}", something_else),
|
||||
};
|
||||
|
||||
lua.set_memory_limit(0)?;
|
||||
f.call::<_, ()>(()).expect("should trigger no memory limit");
|
||||
f.call::<()>(()).expect("should trigger no memory limit");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -49,7 +49,7 @@ fn test_memory_limit_thread() -> Result<()> {
|
||||
|
||||
lua.set_memory_limit(lua.used_memory() + 10000)?;
|
||||
let thread = lua.create_thread(f)?;
|
||||
match thread.resume::<_, ()>(()) {
|
||||
match thread.resume::<()>(()) {
|
||||
Err(Error::MemoryError(_)) => {}
|
||||
something_else => panic!("did not trigger memory error: {:?}", something_else),
|
||||
};
|
||||
|
||||
@@ -92,6 +92,18 @@ fn test_module_multi_from_thread() -> Result<()> {
|
||||
.exec()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_module_new_vm() -> Result<()> {
|
||||
let lua = make_lua()?;
|
||||
lua.load(
|
||||
r#"
|
||||
local mod = require("test_module.new_vm")
|
||||
assert(mod.eval("return \"hello, world\"") == "hello, world")
|
||||
"#,
|
||||
)
|
||||
.exec()
|
||||
}
|
||||
|
||||
fn make_lua() -> Result<Lua> {
|
||||
let (dylib_path, dylib_ext, separator);
|
||||
if cfg!(target_os = "macos") {
|
||||
|
||||
@@ -33,6 +33,18 @@ fn test_module2(lua: &Lua) -> LuaResult<LuaTable> {
|
||||
Ok(exports)
|
||||
}
|
||||
|
||||
#[mlua::lua_module]
|
||||
fn test_module_new_vm(lua: &Lua) -> LuaResult<LuaTable> {
|
||||
let eval = lua.create_function(|_, prog: String| {
|
||||
let lua = Lua::new();
|
||||
lua.load(prog).eval::<Option<String>>()
|
||||
})?;
|
||||
|
||||
let exports = lua.create_table()?;
|
||||
exports.set("eval", eval)?;
|
||||
Ok(exports)
|
||||
}
|
||||
|
||||
#[mlua::lua_module]
|
||||
fn test_module_error(_: &Lua) -> LuaResult<LuaTable> {
|
||||
Err("custom module error".into_lua_err())
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
#![cfg(feature = "send")]
|
||||
|
||||
use std::cell::UnsafeCell;
|
||||
use std::marker::PhantomData;
|
||||
use std::string::String as StdString;
|
||||
|
||||
use mlua::{AnyUserData, Error, Lua, Result, UserDataRef};
|
||||
use static_assertions::{assert_impl_all, assert_not_impl_all};
|
||||
|
||||
#[test]
|
||||
fn test_userdata_multithread_access() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
// This type is `Send` but not `Sync`.
|
||||
struct MyUserData(#[allow(unused)] StdString, PhantomData<UnsafeCell<()>>);
|
||||
|
||||
assert_impl_all!(MyUserData: Send);
|
||||
assert_not_impl_all!(MyUserData: Sync);
|
||||
|
||||
lua.globals().set(
|
||||
"ud",
|
||||
AnyUserData::wrap(MyUserData("hello".to_string(), PhantomData)),
|
||||
)?;
|
||||
// We acquired the exclusive reference.
|
||||
let _ud1 = lua.globals().get::<UserDataRef<MyUserData>>("ud")?;
|
||||
|
||||
std::thread::scope(|s| {
|
||||
s.spawn(|| {
|
||||
let res = lua.globals().get::<UserDataRef<MyUserData>>("ud");
|
||||
assert!(matches!(res, Err(Error::UserDataBorrowError)));
|
||||
});
|
||||
});
|
||||
|
||||
Ok(())
|
||||
}
|
||||
-122
@@ -1,122 +0,0 @@
|
||||
use std::cell::RefCell;
|
||||
|
||||
use mlua::{Lua, Result, Table};
|
||||
|
||||
#[test]
|
||||
fn test_static_lua() -> Result<()> {
|
||||
let lua = Lua::new().into_static();
|
||||
|
||||
thread_local! {
|
||||
static TABLE: RefCell<Option<Table>> = RefCell::new(None);
|
||||
}
|
||||
|
||||
let f = lua.create_function(|_, table: Table| {
|
||||
TABLE.with(|t| {
|
||||
table.raw_insert(1, "hello")?;
|
||||
*t.borrow_mut() = Some(table);
|
||||
Ok(())
|
||||
})
|
||||
})?;
|
||||
|
||||
f.call(lua.create_table()?)?;
|
||||
drop(f);
|
||||
lua.gc_collect()?;
|
||||
|
||||
TABLE.with(|t| {
|
||||
assert!(t.borrow().as_ref().unwrap().len().unwrap() == 1);
|
||||
*t.borrow_mut() = None;
|
||||
});
|
||||
|
||||
// Consume the Lua instance
|
||||
unsafe { Lua::from_static(lua) };
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_static_lua_coroutine() -> Result<()> {
|
||||
let lua = Lua::new().into_static();
|
||||
|
||||
thread_local! {
|
||||
static TABLE: RefCell<Option<Table>> = RefCell::new(None);
|
||||
}
|
||||
|
||||
let f = lua.create_function(|_, table: Table| {
|
||||
TABLE.with(|t| {
|
||||
table.raw_insert(1, "hello")?;
|
||||
*t.borrow_mut() = Some(table);
|
||||
Ok(())
|
||||
})
|
||||
})?;
|
||||
|
||||
let co = lua.create_thread(f)?;
|
||||
co.resume::<_, ()>(lua.create_table()?)?;
|
||||
drop(co);
|
||||
lua.gc_collect()?;
|
||||
|
||||
TABLE.with(|t| {
|
||||
assert_eq!(
|
||||
t.borrow().as_ref().unwrap().get::<_, String>(1i32).unwrap(),
|
||||
"hello".to_string()
|
||||
);
|
||||
*t.borrow_mut() = None;
|
||||
});
|
||||
|
||||
// Consume the Lua instance
|
||||
unsafe { Lua::from_static(lua) };
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
#[tokio::test]
|
||||
async fn test_static_async() -> Result<()> {
|
||||
let lua = Lua::new().into_static();
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
async fn sleep_ms(ms: u64) {
|
||||
tokio::time::sleep(std::time::Duration::from_millis(ms)).await;
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
async fn sleep_ms(_ms: u64) {
|
||||
tokio::task::yield_now().await;
|
||||
}
|
||||
|
||||
let timer = lua.create_async_function(|_, (i, n, f): (u64, u64, mlua::Function)| async move {
|
||||
tokio::task::spawn_local(async move {
|
||||
for _ in 0..n {
|
||||
tokio::task::spawn_local(f.call_async::<(), ()>(()));
|
||||
sleep_ms(i).await;
|
||||
}
|
||||
});
|
||||
Ok(())
|
||||
})?;
|
||||
lua.globals().set("timer", timer)?;
|
||||
|
||||
{
|
||||
let local_set = tokio::task::LocalSet::new();
|
||||
local_set
|
||||
.run_until(
|
||||
lua.load(
|
||||
r#"
|
||||
local cnt = 0
|
||||
timer(1, 100, function()
|
||||
cnt = cnt + 1
|
||||
if cnt % 10 == 0 then
|
||||
collectgarbage()
|
||||
end
|
||||
end)
|
||||
"#,
|
||||
)
|
||||
.exec_async(),
|
||||
)
|
||||
.await?;
|
||||
local_set.await;
|
||||
}
|
||||
|
||||
// Consume the Lua instance
|
||||
unsafe { Lua::from_static(lua) };
|
||||
|
||||
Ok(())
|
||||
}
|
||||
+46
-32
@@ -1,4 +1,4 @@
|
||||
use mlua::{Error, Lua, Nil, Result, Table, TableExt, Value};
|
||||
use mlua::{Error, Lua, Nil, ObjectLike, Result, Table, Value};
|
||||
|
||||
#[test]
|
||||
fn test_globals_set_get() -> Result<()> {
|
||||
@@ -7,8 +7,8 @@ fn test_globals_set_get() -> Result<()> {
|
||||
let globals = lua.globals();
|
||||
globals.set("foo", "bar")?;
|
||||
globals.set("baz", "baf")?;
|
||||
assert_eq!(globals.get::<_, String>("foo")?, "bar");
|
||||
assert_eq!(globals.get::<_, String>("baz")?, "baf");
|
||||
assert_eq!(globals.get::<String>("foo")?, "bar");
|
||||
assert_eq!(globals.get::<String>("baz")?, "baf");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -26,8 +26,8 @@ fn test_table() -> Result<()> {
|
||||
table1.set("foo", "bar")?;
|
||||
table2.set("baz", "baf")?;
|
||||
|
||||
assert_eq!(table2.get::<_, String>("foo")?, "bar");
|
||||
assert_eq!(table1.get::<_, String>("baz")?, "baf");
|
||||
assert_eq!(table2.get::<String>("foo")?, "bar");
|
||||
assert_eq!(table1.get::<String>("baz")?, "baf");
|
||||
|
||||
lua.load(
|
||||
r#"
|
||||
@@ -38,9 +38,9 @@ fn test_table() -> Result<()> {
|
||||
)
|
||||
.exec()?;
|
||||
|
||||
let table1 = globals.get::<_, Table>("table1")?;
|
||||
let table2 = globals.get::<_, Table>("table2")?;
|
||||
let table3 = globals.get::<_, Table>("table3")?;
|
||||
let table1 = globals.get::<Table>("table1")?;
|
||||
let table2 = globals.get::<Table>("table2")?;
|
||||
let table3 = globals.get::<Table>("table3")?;
|
||||
|
||||
assert_eq!(table1.len()?, 5);
|
||||
assert!(!table1.is_empty());
|
||||
@@ -70,7 +70,7 @@ fn test_table() -> Result<()> {
|
||||
);
|
||||
|
||||
globals.set("table4", lua.create_sequence_from(vec![1, 2, 3, 4, 5])?)?;
|
||||
let table4 = globals.get::<_, Table>("table4")?;
|
||||
let table4 = globals.get::<Table>("table4")?;
|
||||
assert_eq!(
|
||||
table4.clone().pairs().collect::<Result<Vec<(i64, i64)>>>()?,
|
||||
vec![(1, 1), (2, 2), (3, 3), (4, 4), (5, 5)]
|
||||
@@ -180,7 +180,7 @@ fn test_table_clear() -> Result<()> {
|
||||
t2.clear()?;
|
||||
assert_eq!(t2.raw_len(), 0);
|
||||
assert!(t2.is_empty());
|
||||
assert_eq!(t2.raw_get::<_, Value>("a")?, Value::Nil);
|
||||
assert_eq!(t2.raw_get::<Value>("a")?, Value::Nil);
|
||||
assert_ne!(t2.get_metatable(), None);
|
||||
|
||||
Ok(())
|
||||
@@ -192,9 +192,9 @@ fn test_table_sequence_from() -> Result<()> {
|
||||
|
||||
let get_table = lua.create_function(|_, t: Table| Ok(t))?;
|
||||
|
||||
assert_eq!(get_table.call::<_, Table>(vec![1, 2, 3])?, [1, 2, 3]);
|
||||
assert_eq!(get_table.call::<_, Table>([4, 5, 6])?, [4, 5, 6]);
|
||||
assert_eq!(get_table.call::<_, Table>([7, 8, 9].as_slice())?, [7, 8, 9]);
|
||||
assert_eq!(get_table.call::<Table>(vec![1, 2, 3])?, [1, 2, 3]);
|
||||
assert_eq!(get_table.call::<Table>([4, 5, 6])?, [4, 5, 6]);
|
||||
assert_eq!(get_table.call::<Table>([7, 8, 9].as_slice())?, [7, 8, 9]);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -284,13 +284,13 @@ fn test_table_scope() -> Result<()> {
|
||||
// Make sure that table gets do not borrow the table, but instead just borrow lua.
|
||||
let tin;
|
||||
{
|
||||
let touter = globals.get::<_, Table>("touter")?;
|
||||
tin = touter.get::<_, Table>("tin")?;
|
||||
let touter = globals.get::<Table>("touter")?;
|
||||
tin = touter.get::<Table>("tin")?;
|
||||
}
|
||||
|
||||
assert_eq!(tin.get::<_, i64>(1)?, 1);
|
||||
assert_eq!(tin.get::<_, i64>(2)?, 2);
|
||||
assert_eq!(tin.get::<_, i64>(3)?, 3);
|
||||
assert_eq!(tin.get::<i64>(1)?, 1);
|
||||
assert_eq!(tin.get::<i64>(2)?, 2);
|
||||
assert_eq!(tin.get::<i64>(3)?, 3);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -303,13 +303,13 @@ fn test_metatable() -> Result<()> {
|
||||
let metatable = lua.create_table()?;
|
||||
metatable.set("__index", lua.create_function(|_, ()| Ok("index_value"))?)?;
|
||||
table.set_metatable(Some(metatable));
|
||||
assert_eq!(table.get::<_, String>("any_key")?, "index_value");
|
||||
match table.raw_get::<_, Value>("any_key")? {
|
||||
assert_eq!(table.get::<String>("any_key")?, "index_value");
|
||||
match table.raw_get::<Value>("any_key")? {
|
||||
Nil => {}
|
||||
_ => panic!(),
|
||||
}
|
||||
table.set_metatable(None);
|
||||
match table.get::<_, Value>("any_key")? {
|
||||
match table.get::<Value>("any_key")? {
|
||||
Nil => {}
|
||||
_ => panic!(),
|
||||
};
|
||||
@@ -336,10 +336,10 @@ fn test_table_eq() -> Result<()> {
|
||||
)
|
||||
.exec()?;
|
||||
|
||||
let table1 = globals.get::<_, Table>("table1")?;
|
||||
let table2 = globals.get::<_, Table>("table2")?;
|
||||
let table3 = globals.get::<_, Table>("table3")?;
|
||||
let table4 = globals.get::<_, Table>("table4")?;
|
||||
let table1 = globals.get::<Table>("table1")?;
|
||||
let table2 = globals.get::<Table>("table2")?;
|
||||
let table3 = globals.get::<Table>("table3")?;
|
||||
let table4 = globals.get::<Table>("table4")?;
|
||||
|
||||
assert!(table1 != table2);
|
||||
assert!(!table1.equals(&table2)?);
|
||||
@@ -389,17 +389,17 @@ fn test_table_error() -> Result<()> {
|
||||
|
||||
let bad_table: Table = globals.get("table")?;
|
||||
assert!(bad_table.set(1, 1).is_err());
|
||||
assert!(bad_table.get::<_, i32>(1).is_err());
|
||||
assert!(bad_table.get::<i32>(1).is_err());
|
||||
assert!(bad_table.len().is_err());
|
||||
assert!(bad_table.raw_set(1, 1).is_ok());
|
||||
assert!(bad_table.raw_get::<_, i32>(1).is_ok());
|
||||
assert!(bad_table.raw_get::<i32>(1).is_ok());
|
||||
assert_eq!(bad_table.raw_len(), 1);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_table_call() -> Result<()> {
|
||||
fn test_table_object_like() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
lua.load(
|
||||
@@ -408,6 +408,10 @@ fn test_table_call() -> Result<()> {
|
||||
setmetatable(table, {
|
||||
__call = function(t, key)
|
||||
return "call_"..t[key]
|
||||
end,
|
||||
|
||||
__tostring = function()
|
||||
return "table object"
|
||||
end
|
||||
})
|
||||
|
||||
@@ -424,13 +428,23 @@ fn test_table_call() -> Result<()> {
|
||||
|
||||
let table: Table = lua.globals().get("table")?;
|
||||
|
||||
assert_eq!(table.call::<_, String>("b")?, "call_2");
|
||||
assert_eq!(table.call_function::<_, String>("func", "a")?, "func_a");
|
||||
assert_eq!(table.call_method::<_, String>("method", "a")?, "method_1");
|
||||
<Table as ObjectLike>::set(&table, "c", 3)?;
|
||||
assert_eq!(<Table as ObjectLike>::get::<i32>(&table, "c")?, 3);
|
||||
assert_eq!(table.call::<String>("b")?, "call_2");
|
||||
assert_eq!(table.call_function::<String>("func", "a")?, "func_a");
|
||||
assert_eq!(table.call_method::<String>("method", "a")?, "method_1");
|
||||
assert_eq!(table.to_string()?, "table object");
|
||||
|
||||
match table.call_method::<()>("non_existent", ()) {
|
||||
Err(Error::RuntimeError(err)) => {
|
||||
assert!(err.contains("attempt to call a nil value (function 'non_existent')"))
|
||||
}
|
||||
r => panic!("expected RuntimeError, got {r:?}"),
|
||||
}
|
||||
|
||||
// Test calling non-callable table
|
||||
let table2 = lua.create_table()?;
|
||||
assert!(matches!(table2.call::<_, ()>(()), Err(Error::RuntimeError(_))));
|
||||
assert!(matches!(table2.call::<()>(()), Err(Error::RuntimeError(_))));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
+61
-68
@@ -52,7 +52,7 @@ fn test_safety() -> Result<()> {
|
||||
|
||||
// Test safety rules after dynamically loading `package` library
|
||||
let lua = Lua::new_with(StdLib::NONE, LuaOptions::default())?;
|
||||
assert!(lua.globals().get::<_, Option<Value>>("require")?.is_none());
|
||||
assert!(lua.globals().get::<Option<Value>>("require")?.is_none());
|
||||
lua.load_std_libs(StdLib::PACKAGE)?;
|
||||
match lua.load(r#"package.loadlib()"#).exec() {
|
||||
Err(Error::CallbackError { ref cause, .. }) => match cause.as_ref() {
|
||||
@@ -91,7 +91,7 @@ fn test_exec() -> Result<()> {
|
||||
"#,
|
||||
)
|
||||
.exec()?;
|
||||
assert_eq!(globals.get::<_, String>("res")?, "foobar");
|
||||
assert_eq!(globals.get::<String>("res")?, "foobar");
|
||||
|
||||
let module: Table = lua
|
||||
.load(
|
||||
@@ -108,7 +108,7 @@ fn test_exec() -> Result<()> {
|
||||
.eval()?;
|
||||
println!("checkpoint");
|
||||
assert!(module.contains_key("func")?);
|
||||
assert_eq!(module.get::<_, Function>("func")?.call::<_, String>(())?, "hello");
|
||||
assert_eq!(module.get::<Function>("func")?.call::<String>(())?, "hello");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -147,7 +147,7 @@ fn test_load_mode() -> Result<()> {
|
||||
#[cfg(not(feature = "luau"))]
|
||||
let bytecode = lua.load("return 1 + 1").into_function()?.dump(true);
|
||||
#[cfg(feature = "luau")]
|
||||
let bytecode = mlua::Compiler::new().compile("return 1 + 1");
|
||||
let bytecode = mlua::Compiler::new().compile("return 1 + 1")?;
|
||||
assert_eq!(lua.load(&bytecode).eval::<i32>()?, 2);
|
||||
assert_eq!(lua.load(&bytecode).set_mode(ChunkMode::Binary).eval::<i32>()?, 2);
|
||||
match lua.load(&bytecode).set_mode(ChunkMode::Text).exec() {
|
||||
@@ -179,13 +179,13 @@ fn test_lua_multi() -> Result<()> {
|
||||
.exec()?;
|
||||
|
||||
let globals = lua.globals();
|
||||
let concat = globals.get::<_, Function>("concat")?;
|
||||
let mreturn = globals.get::<_, Function>("mreturn")?;
|
||||
let concat = globals.get::<Function>("concat")?;
|
||||
let mreturn = globals.get::<Function>("mreturn")?;
|
||||
|
||||
assert_eq!(concat.call::<_, String>(("foo", "bar"))?, "foobar");
|
||||
let (a, b) = mreturn.call::<_, (u64, u64)>(())?;
|
||||
assert_eq!(concat.call::<String>(("foo", "bar"))?, "foobar");
|
||||
let (a, b) = mreturn.call::<(u64, u64)>(())?;
|
||||
assert_eq!((a, b), (1, 2));
|
||||
let (a, b, v) = mreturn.call::<_, (u64, u64, Variadic<u64>)>(())?;
|
||||
let (a, b, v) = mreturn.call::<(u64, u64, Variadic<u64>)>(())?;
|
||||
assert_eq!((a, b), (1, 2));
|
||||
assert_eq!(v[..], [3, 4, 5, 6]);
|
||||
|
||||
@@ -207,10 +207,10 @@ fn test_coercion() -> Result<()> {
|
||||
.exec()?;
|
||||
|
||||
let globals = lua.globals();
|
||||
assert_eq!(globals.get::<_, String>("int")?, "123");
|
||||
assert_eq!(globals.get::<_, i32>("str")?, 123);
|
||||
assert_eq!(globals.get::<_, i32>("num")?, 123);
|
||||
assert!(globals.get::<_, String>("func").is_err());
|
||||
assert_eq!(globals.get::<String>("int")?, "123");
|
||||
assert_eq!(globals.get::<i32>("str")?, 123);
|
||||
assert_eq!(globals.get::<i32>("num")?, 123);
|
||||
assert!(globals.get::<String>("func").is_err());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -297,31 +297,31 @@ fn test_error() -> Result<()> {
|
||||
let rust_error_function = lua.create_function(|_, ()| -> Result<()> { Err(TestError.into_lua_err()) })?;
|
||||
globals.set("rust_error_function", rust_error_function)?;
|
||||
|
||||
let no_error = globals.get::<_, Function>("no_error")?;
|
||||
assert!(no_error.call::<_, ()>(()).is_ok());
|
||||
let no_error = globals.get::<Function>("no_error")?;
|
||||
assert!(no_error.call::<()>(()).is_ok());
|
||||
|
||||
let lua_error = globals.get::<_, Function>("lua_error")?;
|
||||
match lua_error.call::<_, ()>(()) {
|
||||
let lua_error = globals.get::<Function>("lua_error")?;
|
||||
match lua_error.call::<()>(()) {
|
||||
Err(Error::RuntimeError(_)) => {}
|
||||
Err(e) => panic!("error is not RuntimeError kind, got {:?}", e),
|
||||
_ => panic!("error not returned"),
|
||||
}
|
||||
|
||||
let rust_error = globals.get::<_, Function>("rust_error")?;
|
||||
match rust_error.call::<_, ()>(()) {
|
||||
let rust_error = globals.get::<Function>("rust_error")?;
|
||||
match rust_error.call::<()>(()) {
|
||||
Err(Error::CallbackError { .. }) => {}
|
||||
Err(e) => panic!("error is not CallbackError kind, got {:?}", e),
|
||||
_ => panic!("error not returned"),
|
||||
}
|
||||
|
||||
let return_error = globals.get::<_, Function>("return_error")?;
|
||||
match return_error.call::<_, Value>(()) {
|
||||
let return_error = globals.get::<Function>("return_error")?;
|
||||
match return_error.call::<Value>(()) {
|
||||
Ok(Value::Error(_)) => {}
|
||||
_ => panic!("Value::Error not returned"),
|
||||
}
|
||||
|
||||
let return_string_error = globals.get::<_, Function>("return_string_error")?;
|
||||
assert!(return_string_error.call::<_, Error>(()).is_ok());
|
||||
let return_string_error = globals.get::<Function>("return_string_error")?;
|
||||
assert!(return_string_error.call::<Error>(()).is_ok());
|
||||
|
||||
match lua.load("if youre happy and you know it syntax error").exec() {
|
||||
Err(Error::SyntaxError {
|
||||
@@ -340,13 +340,13 @@ fn test_error() -> Result<()> {
|
||||
_ => panic!("error not returned"),
|
||||
}
|
||||
|
||||
let test_pcall = globals.get::<_, Function>("test_pcall")?;
|
||||
test_pcall.call::<_, ()>(())?;
|
||||
let test_pcall = globals.get::<Function>("test_pcall")?;
|
||||
test_pcall.call::<()>(())?;
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
{
|
||||
let understand_recursion = globals.get::<_, Function>("understand_recursion")?;
|
||||
assert!(understand_recursion.call::<_, ()>(()).is_err());
|
||||
let understand_recursion = globals.get::<Function>("understand_recursion")?;
|
||||
assert!(understand_recursion.call::<()>(()).is_err());
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@@ -411,7 +411,7 @@ fn test_panic() -> Result<()> {
|
||||
Err(_) => {}
|
||||
};
|
||||
|
||||
assert!(lua.globals().get::<_, Value>("err")? == Value::Nil);
|
||||
assert!(lua.globals().get::<Value>("err")? == Value::Nil);
|
||||
match lua.load("tostring(err)").exec() {
|
||||
Ok(_) => panic!("no error was detected"),
|
||||
Err(Error::CallbackError { ref cause, .. }) => match cause.as_ref() {
|
||||
@@ -591,18 +591,15 @@ fn test_pcall_xpcall() -> Result<()> {
|
||||
)
|
||||
.exec()?;
|
||||
|
||||
assert_eq!(globals.get::<_, bool>("pcall_status")?, false);
|
||||
assert_eq!(globals.get::<_, String>("pcall_error")?, "testerror");
|
||||
assert_eq!(globals.get::<bool>("pcall_status")?, false);
|
||||
assert_eq!(globals.get::<String>("pcall_error")?, "testerror");
|
||||
|
||||
assert_eq!(globals.get::<_, bool>("xpcall_statusr")?, false);
|
||||
assert_eq!(globals.get::<bool>("xpcall_statusr")?, false);
|
||||
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52", feature = "luajit"))]
|
||||
assert_eq!(
|
||||
globals.get::<_, std::string::String>("xpcall_error")?,
|
||||
"testerror"
|
||||
);
|
||||
assert_eq!(globals.get::<std::string::String>("xpcall_error")?, "testerror");
|
||||
#[cfg(feature = "lua51")]
|
||||
assert!(globals
|
||||
.get::<_, String>("xpcall_error")?
|
||||
.get::<String>("xpcall_error")?
|
||||
.to_str()?
|
||||
.ends_with(": testerror"));
|
||||
|
||||
@@ -615,7 +612,7 @@ fn test_pcall_xpcall() -> Result<()> {
|
||||
"#,
|
||||
)
|
||||
.exec()?;
|
||||
let _ = globals.get::<_, Function>("xpcall_recursion")?.call::<_, ()>(());
|
||||
let _ = globals.get::<Function>("xpcall_recursion")?.call::<()>(());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -632,7 +629,7 @@ fn test_recursive_mut_callback_error() -> Result<()> {
|
||||
// Produce a mutable reference
|
||||
let r = v.as_mut().unwrap();
|
||||
// Whoops, this will recurse into the function and produce another mutable reference!
|
||||
lua.globals().get::<_, Function>("f")?.call::<_, ()>(true)?;
|
||||
lua.globals().get::<Function>("f")?.call::<()>(true)?;
|
||||
println!("Should not get here, mutable aliasing has occurred!");
|
||||
println!("value at {:p}", r as *mut _);
|
||||
println!("value is {}", r);
|
||||
@@ -641,7 +638,7 @@ fn test_recursive_mut_callback_error() -> Result<()> {
|
||||
Ok(())
|
||||
})?;
|
||||
lua.globals().set("f", f)?;
|
||||
match lua.globals().get::<_, Function>("f")?.call::<_, ()>(false) {
|
||||
match lua.globals().get::<Function>("f")?.call::<()>(false) {
|
||||
Err(Error::CallbackError { ref cause, .. }) => match *cause.as_ref() {
|
||||
Error::CallbackError { ref cause, .. } => match *cause.as_ref() {
|
||||
Error::RecursiveMutCallback { .. } => {}
|
||||
@@ -672,13 +669,13 @@ fn test_set_metatable_nil() -> Result<()> {
|
||||
fn test_named_registry_value() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
lua.set_named_registry_value::<i32>("test", 42)?;
|
||||
lua.set_named_registry_value("test", 42)?;
|
||||
let f = lua.create_function(move |lua, ()| {
|
||||
assert_eq!(lua.named_registry_value::<i32>("test")?, 42);
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
f.call::<_, ()>(())?;
|
||||
f.call::<()>(())?;
|
||||
|
||||
lua.unset_named_registry_value("test")?;
|
||||
match lua.named_registry_value("test")? {
|
||||
@@ -693,7 +690,7 @@ fn test_named_registry_value() -> Result<()> {
|
||||
fn test_registry_value() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let mut r = Some(lua.create_registry_value::<i32>(42)?);
|
||||
let mut r = Some(lua.create_registry_value(42)?);
|
||||
let f = lua.create_function_mut(move |lua, ()| {
|
||||
if let Some(r) = r.take() {
|
||||
assert_eq!(lua.registry_value::<i32>(&r)?, 42);
|
||||
@@ -704,7 +701,7 @@ fn test_registry_value() -> Result<()> {
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
f.call::<_, ()>(())?;
|
||||
f.call::<()>(())?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -735,7 +732,7 @@ fn test_drop_registry_value() -> Result<()> {
|
||||
fn test_replace_registry_value() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let mut key = lua.create_registry_value::<i32>(42)?;
|
||||
let mut key = lua.create_registry_value(42)?;
|
||||
lua.replace_registry_value(&mut key, "new value")?;
|
||||
assert_eq!(lua.registry_value::<String>(&key)?, "new value");
|
||||
lua.replace_registry_value(&mut key, Value::Nil)?;
|
||||
@@ -866,7 +863,7 @@ fn test_application_data() -> Result<()> {
|
||||
|
||||
Ok(())
|
||||
})?;
|
||||
f.call(())?;
|
||||
f.call::<()>(())?;
|
||||
|
||||
assert_eq!(*lua.app_data_ref::<&str>().unwrap(), "test4");
|
||||
assert_eq!(*lua.app_data_ref::<Vec<&str>>().unwrap(), vec!["test2", "test3"]);
|
||||
@@ -884,13 +881,13 @@ fn test_recursion() -> Result<()> {
|
||||
|
||||
let f = lua.create_function(move |lua, i: i32| {
|
||||
if i < 64 {
|
||||
lua.globals().get::<_, Function>("f")?.call::<_, ()>(i + 1)?;
|
||||
lua.globals().get::<Function>("f")?.call::<()>(i + 1)?;
|
||||
}
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
lua.globals().set("f", &f)?;
|
||||
f.call::<_, ()>(1)?;
|
||||
f.call::<()>(1)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -900,7 +897,7 @@ fn test_recursion() -> Result<()> {
|
||||
fn test_too_many_returns() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
let f = lua.create_function(|_, ()| Ok(Variadic::from_iter(1..1000000)))?;
|
||||
assert!(f.call::<_, Variadic<u32>>(()).is_err());
|
||||
assert!(f.call::<Variadic<u32>>(()).is_err());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -910,11 +907,7 @@ fn test_too_many_arguments() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
lua.load("function test(...) end").exec()?;
|
||||
let args = Variadic::from_iter(1..1000000);
|
||||
assert!(lua
|
||||
.globals()
|
||||
.get::<_, Function>("test")?
|
||||
.call::<_, ()>(args)
|
||||
.is_err());
|
||||
assert!(lua.globals().get::<Function>("test")?.call::<()>(args).is_err());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -924,10 +917,10 @@ fn test_too_many_arguments() -> Result<()> {
|
||||
fn test_too_many_recursions() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let f = lua.create_function(move |lua, ()| lua.globals().get::<_, Function>("f")?.call::<_, ()>(()))?;
|
||||
let f = lua.create_function(move |lua, ()| lua.globals().get::<Function>("f")?.call::<()>(()))?;
|
||||
|
||||
lua.globals().set("f", &f)?;
|
||||
assert!(f.call::<_, ()>(()).is_err());
|
||||
assert!(f.call::<()>(()).is_err());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -945,9 +938,9 @@ fn test_too_many_binds() -> Result<()> {
|
||||
)
|
||||
.exec()?;
|
||||
|
||||
let concat = globals.get::<_, Function>("f")?;
|
||||
let concat = globals.get::<Function>("f")?;
|
||||
assert!(concat.bind(Variadic::from_iter(1..1000000)).is_err());
|
||||
assert!(concat.call::<_, ()>(Variadic::from_iter(1..1000000)).is_err());
|
||||
assert!(concat.call::<()>(Variadic::from_iter(1..1000000)).is_err());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -998,7 +991,7 @@ fn test_large_args() -> Result<()> {
|
||||
)
|
||||
.eval()?;
|
||||
|
||||
assert_eq!(f.call::<_, usize>((0..100).collect::<Variadic<usize>>())?, 4950);
|
||||
assert_eq!(f.call::<usize>((0..100).collect::<Variadic<usize>>())?, 4950);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -1014,7 +1007,7 @@ fn test_large_args_ref() -> Result<()> {
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
f.call::<_, ()>((0..100).map(|i| i.to_string()).collect::<Variadic<_>>())?;
|
||||
f.call::<()>((0..100).map(|i| i.to_string()).collect::<Variadic<_>>())?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -1068,14 +1061,14 @@ fn test_context_thread() -> Result<()> {
|
||||
.into_function()?;
|
||||
|
||||
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52", feature = "luajit52"))]
|
||||
f.call::<_, ()>(lua.current_thread())?;
|
||||
f.call::<()>(lua.current_thread())?;
|
||||
|
||||
#[cfg(any(
|
||||
feature = "lua51",
|
||||
all(feature = "luajit", not(feature = "luajit52")),
|
||||
feature = "luau"
|
||||
))]
|
||||
f.call::<_, ()>(Nil)?;
|
||||
f.call::<()>(Nil)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -1096,7 +1089,7 @@ fn test_context_thread_51() -> Result<()> {
|
||||
.eval()?,
|
||||
)?;
|
||||
|
||||
thread.resume::<_, ()>(thread.clone())?;
|
||||
thread.resume::<()>(thread.clone())?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -1106,7 +1099,7 @@ fn test_context_thread_51() -> Result<()> {
|
||||
fn test_jit_version() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
let jit: Table = lua.globals().get("jit")?;
|
||||
assert!(jit.get::<_, String>("version")?.to_str()?.contains("LuaJIT"));
|
||||
assert!(jit.get::<String>("version")?.to_str()?.contains("LuaJIT"));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1124,7 +1117,7 @@ fn test_load_from_function() -> Result<()> {
|
||||
})?;
|
||||
|
||||
let t: Table = lua.load_from_function("my_module", func.clone())?;
|
||||
assert_eq!(t.get::<_, String>("__name")?, "my_module");
|
||||
assert_eq!(t.get::<String>("__name")?, "my_module");
|
||||
assert_eq!(i.load(Ordering::Relaxed), 1);
|
||||
|
||||
let _: Value = lua.load_from_function("my_module", func.clone())?;
|
||||
@@ -1188,7 +1181,7 @@ fn test_multi_states() -> Result<()> {
|
||||
|
||||
let f = lua.create_function(|_, g: Option<Function>| {
|
||||
if let Some(g) = g {
|
||||
g.call(())?;
|
||||
g.call::<()>(())?;
|
||||
}
|
||||
Ok(())
|
||||
})?;
|
||||
@@ -1282,17 +1275,17 @@ fn test_multi_thread() -> Result<()> {
|
||||
std::thread::scope(|s| {
|
||||
s.spawn(|| {
|
||||
for _ in 0..5 {
|
||||
func.call::<_, ()>(()).unwrap();
|
||||
func.call::<()>(()).unwrap();
|
||||
}
|
||||
});
|
||||
s.spawn(|| {
|
||||
for _ in 0..5 {
|
||||
func.call::<_, ()>(()).unwrap();
|
||||
func.call::<()>(()).unwrap();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
assert_eq!(lua.globals().get::<_, i32>("i")?, 10);
|
||||
assert_eq!(lua.globals().get::<i32>("i")?, 10);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
+27
-27
@@ -22,16 +22,16 @@ fn test_thread() -> Result<()> {
|
||||
)?;
|
||||
|
||||
assert_eq!(thread.status(), ThreadStatus::Resumable);
|
||||
assert_eq!(thread.resume::<_, i64>(0)?, 0);
|
||||
assert_eq!(thread.resume::<i64>(0)?, 0);
|
||||
assert_eq!(thread.status(), ThreadStatus::Resumable);
|
||||
assert_eq!(thread.resume::<_, i64>(1)?, 1);
|
||||
assert_eq!(thread.resume::<i64>(1)?, 1);
|
||||
assert_eq!(thread.status(), ThreadStatus::Resumable);
|
||||
assert_eq!(thread.resume::<_, i64>(2)?, 3);
|
||||
assert_eq!(thread.resume::<i64>(2)?, 3);
|
||||
assert_eq!(thread.status(), ThreadStatus::Resumable);
|
||||
assert_eq!(thread.resume::<_, i64>(3)?, 6);
|
||||
assert_eq!(thread.resume::<i64>(3)?, 6);
|
||||
assert_eq!(thread.status(), ThreadStatus::Resumable);
|
||||
assert_eq!(thread.resume::<_, i64>(4)?, 10);
|
||||
assert_eq!(thread.status(), ThreadStatus::Unresumable);
|
||||
assert_eq!(thread.resume::<i64>(4)?, 10);
|
||||
assert_eq!(thread.status(), ThreadStatus::Finished);
|
||||
|
||||
let accumulate = lua.create_thread(
|
||||
lua.load(
|
||||
@@ -47,11 +47,11 @@ fn test_thread() -> Result<()> {
|
||||
)?;
|
||||
|
||||
for i in 0..4 {
|
||||
accumulate.resume::<_, ()>(i)?;
|
||||
accumulate.resume::<()>(i)?;
|
||||
}
|
||||
assert_eq!(accumulate.resume::<_, i64>(4)?, 10);
|
||||
assert_eq!(accumulate.resume::<i64>(4)?, 10);
|
||||
assert_eq!(accumulate.status(), ThreadStatus::Resumable);
|
||||
assert!(accumulate.resume::<_, ()>("error").is_err());
|
||||
assert!(accumulate.resume::<()>("error").is_err());
|
||||
assert_eq!(accumulate.status(), ThreadStatus::Error);
|
||||
|
||||
let thread = lua
|
||||
@@ -66,7 +66,7 @@ fn test_thread() -> Result<()> {
|
||||
)
|
||||
.eval::<Thread>()?;
|
||||
assert_eq!(thread.status(), ThreadStatus::Resumable);
|
||||
assert_eq!(thread.resume::<_, i64>(())?, 42);
|
||||
assert_eq!(thread.resume::<i64>(())?, 42);
|
||||
|
||||
let thread: Thread = lua
|
||||
.load(
|
||||
@@ -81,26 +81,26 @@ fn test_thread() -> Result<()> {
|
||||
)
|
||||
.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);
|
||||
|
||||
match thread.resume::<_, u32>(()) {
|
||||
Err(Error::CoroutineInactive) => {}
|
||||
match thread.resume::<u32>(()) {
|
||||
Err(Error::CoroutineUnresumable) => {}
|
||||
Err(_) => panic!("resuming dead coroutine error is not CoroutineInactive kind"),
|
||||
_ => panic!("resuming dead coroutine did not return error"),
|
||||
}
|
||||
|
||||
// Already running thread must be unresumable
|
||||
let thread = lua.create_thread(lua.create_function(|lua, ()| {
|
||||
assert_eq!(lua.current_thread().status(), ThreadStatus::Unresumable);
|
||||
let result = lua.current_thread().resume::<_, ()>(());
|
||||
assert_eq!(lua.current_thread().status(), ThreadStatus::Running);
|
||||
let result = lua.current_thread().resume::<()>(());
|
||||
assert!(
|
||||
matches!(result, Err(Error::CoroutineInactive)),
|
||||
matches!(result, Err(Error::CoroutineUnresumable)),
|
||||
"unexpected result: {result:?}",
|
||||
);
|
||||
Ok(())
|
||||
})?)?;
|
||||
let result = thread.resume::<_, ()>(());
|
||||
let result = thread.resume::<()>(());
|
||||
assert!(result.is_ok(), "unexpected result: {result:?}");
|
||||
|
||||
Ok(())
|
||||
@@ -124,11 +124,11 @@ fn test_thread_reset() -> Result<()> {
|
||||
|
||||
for _ in 0..2 {
|
||||
assert_eq!(thread.status(), ThreadStatus::Resumable);
|
||||
let _ = thread.resume::<_, AnyUserData>(MyUserData(arc.clone()))?;
|
||||
let _ = thread.resume::<AnyUserData>(MyUserData(arc.clone()))?;
|
||||
assert_eq!(thread.status(), ThreadStatus::Resumable);
|
||||
assert_eq!(Arc::strong_count(&arc), 2);
|
||||
thread.resume::<_, ()>(())?;
|
||||
assert_eq!(thread.status(), ThreadStatus::Unresumable);
|
||||
thread.resume::<()>(())?;
|
||||
assert_eq!(thread.status(), ThreadStatus::Finished);
|
||||
thread.reset(func.clone())?;
|
||||
lua.gc_collect()?;
|
||||
assert_eq!(Arc::strong_count(&arc), 1);
|
||||
@@ -137,7 +137,7 @@ fn test_thread_reset() -> Result<()> {
|
||||
// Check for errors
|
||||
let func: Function = lua.load(r#"function(ud) error("test error") end"#).eval()?;
|
||||
let thread = lua.create_thread(func.clone())?;
|
||||
let _ = thread.resume::<_, AnyUserData>(MyUserData(arc.clone()));
|
||||
let _ = thread.resume::<AnyUserData>(MyUserData(arc.clone()));
|
||||
assert_eq!(thread.status(), ThreadStatus::Error);
|
||||
assert_eq!(Arc::strong_count(&arc), 2);
|
||||
#[cfg(feature = "lua54")]
|
||||
@@ -147,11 +147,11 @@ fn test_thread_reset() -> Result<()> {
|
||||
// It's became possible to force reset thread by popping error object
|
||||
assert!(matches!(
|
||||
thread.status(),
|
||||
ThreadStatus::Unresumable | ThreadStatus::Error
|
||||
ThreadStatus::Finished | ThreadStatus::Error
|
||||
));
|
||||
// Would pass in 5.4.4
|
||||
// assert!(thread.reset(func.clone()).is_ok());
|
||||
// assert_eq!(thread.status(), ThreadStatus::Resumable);
|
||||
assert!(thread.reset(func.clone()).is_ok());
|
||||
assert_eq!(thread.status(), ThreadStatus::Resumable);
|
||||
}
|
||||
#[cfg(any(feature = "lua54", feature = "luau"))]
|
||||
{
|
||||
@@ -165,7 +165,7 @@ fn test_thread_reset() -> Result<()> {
|
||||
this.reset(lua.create_function(|_, ()| Ok(()))?)?;
|
||||
Ok(())
|
||||
})?)?;
|
||||
let result = thread.resume::<_, ()>(());
|
||||
let result = thread.resume::<()>(());
|
||||
assert!(
|
||||
matches!(result, Err(Error::CallbackError{ ref cause, ..})
|
||||
if matches!(cause.as_ref(), Error::RuntimeError(ref err)
|
||||
@@ -197,7 +197,7 @@ fn test_coroutine_from_closure() -> Result<()> {
|
||||
.load("coroutine.create(function(...) return main(unpack(arg)) end)")
|
||||
.eval()?;
|
||||
|
||||
thrd.resume::<_, ()>(())?;
|
||||
thrd.resume::<()>(())?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
+2
-2
@@ -17,8 +17,8 @@ fn test_lightuserdata() -> Result<()> {
|
||||
.exec()?;
|
||||
|
||||
let res = globals
|
||||
.get::<_, Function>("id")?
|
||||
.call::<_, LightUserData>(LightUserData(42 as *mut c_void))?;
|
||||
.get::<Function>("id")?
|
||||
.call::<LightUserData>(LightUserData(42 as *mut c_void))?;
|
||||
|
||||
assert_eq!(res, LightUserData(42 as *mut c_void));
|
||||
|
||||
|
||||
+72
-45
@@ -6,8 +6,8 @@ use std::sync::Arc;
|
||||
use std::sync::atomic::{AtomicI64, Ordering};
|
||||
|
||||
use mlua::{
|
||||
AnyUserData, AnyUserDataExt, Error, ExternalError, Function, Lua, MetaMethod, Nil, Result, String,
|
||||
UserData, UserDataFields, UserDataMethods, UserDataRef, Value, Variadic,
|
||||
AnyUserData, Error, ExternalError, Function, Lua, MetaMethod, Nil, ObjectLike, Result, String, UserData,
|
||||
UserDataFields, UserDataMethods, UserDataRef, Value, Variadic,
|
||||
};
|
||||
|
||||
#[test]
|
||||
@@ -39,7 +39,7 @@ fn test_methods() -> Result<()> {
|
||||
struct MyUserData(i64);
|
||||
|
||||
impl UserData for MyUserData {
|
||||
fn add_methods<'a, M: UserDataMethods<'a, Self>>(methods: &mut M) {
|
||||
fn add_methods<M: UserDataMethods<Self>>(methods: &mut M) {
|
||||
methods.add_method("get_value", |_, data, ()| Ok(data.0));
|
||||
methods.add_method_mut("set_value", |_, data, args| {
|
||||
data.0 = args;
|
||||
@@ -63,13 +63,13 @@ fn test_methods() -> Result<()> {
|
||||
"#,
|
||||
)
|
||||
.exec()?;
|
||||
let get = globals.get::<_, Function>("get_it")?;
|
||||
let set = globals.get::<_, Function>("set_it")?;
|
||||
assert_eq!(get.call::<_, i64>(())?, 42);
|
||||
let get = globals.get::<Function>("get_it")?;
|
||||
let set = globals.get::<Function>("set_it")?;
|
||||
assert_eq!(get.call::<i64>(())?, 42);
|
||||
userdata.borrow_mut::<MyUserData>()?.0 = 64;
|
||||
assert_eq!(get.call::<_, i64>(())?, 64);
|
||||
set.call::<_, ()>(100)?;
|
||||
assert_eq!(get.call::<_, i64>(())?, 100);
|
||||
assert_eq!(get.call::<i64>(())?, 64);
|
||||
set.call::<()>(100)?;
|
||||
assert_eq!(get.call::<i64>(())?, 100);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -89,7 +89,7 @@ fn test_method_variadic() -> Result<()> {
|
||||
struct MyUserData(i64);
|
||||
|
||||
impl UserData for MyUserData {
|
||||
fn add_methods<'a, M: UserDataMethods<'a, Self>>(methods: &mut M) {
|
||||
fn add_methods<M: UserDataMethods<Self>>(methods: &mut M) {
|
||||
methods.add_method("get", |_, data, ()| Ok(data.0));
|
||||
methods.add_method_mut("add", |_, data, vals: Variadic<i64>| {
|
||||
data.0 += vals.into_iter().sum::<i64>();
|
||||
@@ -114,7 +114,7 @@ fn test_metamethods() -> Result<()> {
|
||||
struct MyUserData(i64);
|
||||
|
||||
impl UserData for MyUserData {
|
||||
fn add_methods<'a, M: UserDataMethods<'a, Self>>(methods: &mut M) {
|
||||
fn add_methods<M: UserDataMethods<Self>>(methods: &mut M) {
|
||||
methods.add_method("get", |_, data, ()| Ok(data.0));
|
||||
methods.add_meta_function(
|
||||
MetaMethod::Add,
|
||||
@@ -188,7 +188,7 @@ fn test_metamethods() -> Result<()> {
|
||||
assert!(lua.load("userdata2.nonexist_field").eval::<()>().is_err());
|
||||
|
||||
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52", feature = "luajit52"))]
|
||||
assert_eq!(pairs_it.call::<_, i64>(())?, 28);
|
||||
assert_eq!(pairs_it.call::<i64>(())?, 28);
|
||||
|
||||
let userdata2: Value = globals.get("userdata2")?;
|
||||
let userdata3: Value = globals.get("userdata3")?;
|
||||
@@ -213,7 +213,7 @@ fn test_metamethod_close() -> Result<()> {
|
||||
struct MyUserData(Arc<AtomicI64>);
|
||||
|
||||
impl UserData for MyUserData {
|
||||
fn add_methods<'a, M: UserDataMethods<'a, Self>>(methods: &mut M) {
|
||||
fn add_methods<M: UserDataMethods<Self>>(methods: &mut M) {
|
||||
methods.add_method("get", |_, data, ()| Ok(data.0.load(Ordering::Relaxed)));
|
||||
methods.add_meta_method(MetaMethod::Close, |_, data, _err: Value| {
|
||||
data.0.store(0, Ordering::Relaxed);
|
||||
@@ -259,7 +259,7 @@ fn test_gc_userdata() -> Result<()> {
|
||||
}
|
||||
|
||||
impl UserData for MyUserdata {
|
||||
fn add_methods<'a, M: UserDataMethods<'a, Self>>(methods: &mut M) {
|
||||
fn add_methods<M: UserDataMethods<Self>>(methods: &mut M) {
|
||||
methods.add_method("access", |_, this, ()| {
|
||||
assert!(this.id == 123);
|
||||
Ok(())
|
||||
@@ -298,7 +298,7 @@ fn test_userdata_take() -> Result<()> {
|
||||
struct MyUserdata(Arc<i64>);
|
||||
|
||||
impl UserData for MyUserdata {
|
||||
fn add_methods<'a, M: UserDataMethods<'a, Self>>(methods: &mut M) {
|
||||
fn add_methods<M: UserDataMethods<Self>>(methods: &mut M) {
|
||||
methods.add_method("num", |_, this, ()| Ok(*this.0))
|
||||
}
|
||||
}
|
||||
@@ -433,7 +433,7 @@ fn test_functions() -> Result<()> {
|
||||
struct MyUserData(i64);
|
||||
|
||||
impl UserData for MyUserData {
|
||||
fn add_methods<'a, M: UserDataMethods<'a, Self>>(methods: &mut M) {
|
||||
fn add_methods<M: UserDataMethods<Self>>(methods: &mut M) {
|
||||
methods.add_function("get_value", |_, ud: AnyUserData| Ok(ud.borrow::<MyUserData>()?.0));
|
||||
methods.add_function_mut("set_value", |_, (ud, value): (AnyUserData, i64)| {
|
||||
ud.borrow_mut::<MyUserData>()?.0 = value;
|
||||
@@ -463,15 +463,15 @@ fn test_functions() -> Result<()> {
|
||||
"#,
|
||||
)
|
||||
.exec()?;
|
||||
let get = globals.get::<_, Function>("get_it")?;
|
||||
let set = globals.get::<_, Function>("set_it")?;
|
||||
let get_constant = globals.get::<_, Function>("get_constant")?;
|
||||
assert_eq!(get.call::<_, i64>(())?, 42);
|
||||
let get = globals.get::<Function>("get_it")?;
|
||||
let set = globals.get::<Function>("set_it")?;
|
||||
let get_constant = globals.get::<Function>("get_constant")?;
|
||||
assert_eq!(get.call::<i64>(())?, 42);
|
||||
userdata.borrow_mut::<MyUserData>()?.0 = 64;
|
||||
assert_eq!(get.call::<_, i64>(())?, 64);
|
||||
set.call::<_, ()>(100)?;
|
||||
assert_eq!(get.call::<_, i64>(())?, 100);
|
||||
assert_eq!(get_constant.call::<_, i64>(())?, 7);
|
||||
assert_eq!(get.call::<i64>(())?, 64);
|
||||
set.call::<()>(100)?;
|
||||
assert_eq!(get.call::<i64>(())?, 100);
|
||||
assert_eq!(get_constant.call::<i64>(())?, 7);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -485,7 +485,7 @@ fn test_fields() -> Result<()> {
|
||||
struct MyUserData(i64);
|
||||
|
||||
impl UserData for MyUserData {
|
||||
fn add_fields<'a, F: UserDataFields<'a, Self>>(fields: &mut F) {
|
||||
fn add_fields<F: UserDataFields<Self>>(fields: &mut F) {
|
||||
fields.add_field("static", "constant");
|
||||
fields.add_field_method_get("val", |_, data| Ok(data.0));
|
||||
fields.add_field_method_set("val", |_, data, val| {
|
||||
@@ -495,7 +495,7 @@ fn test_fields() -> Result<()> {
|
||||
|
||||
// Use userdata "uservalue" storage
|
||||
fields.add_field_function_get("uval", |_, ud| ud.user_value::<Option<String>>());
|
||||
fields.add_field_function_set("uval", |_, ud, s| ud.set_user_value::<Option<String>>(s));
|
||||
fields.add_field_function_set("uval", |_, ud, s: Option<String>| ud.set_user_value(s));
|
||||
|
||||
fields.add_meta_field(MetaMethod::Index, HashMap::from([("f", 321)]));
|
||||
fields.add_meta_field_with(MetaMethod::NewIndex, |lua| {
|
||||
@@ -531,12 +531,12 @@ fn test_fields() -> Result<()> {
|
||||
struct MyUserData2(i64);
|
||||
|
||||
impl UserData for MyUserData2 {
|
||||
fn add_fields<'a, F: UserDataFields<'a, Self>>(fields: &mut F) {
|
||||
fn add_fields<F: UserDataFields<Self>>(fields: &mut F) {
|
||||
fields.add_field("z", 0);
|
||||
fields.add_field_method_get("x", |_, data| Ok(data.0));
|
||||
}
|
||||
|
||||
fn add_methods<'a, M: UserDataMethods<'a, Self>>(methods: &mut M) {
|
||||
fn add_methods<M: UserDataMethods<Self>>(methods: &mut M) {
|
||||
methods.add_meta_method(MetaMethod::Index, |_, _, name: StdString| match &*name {
|
||||
"y" => Ok(Some(-1)),
|
||||
_ => Ok(None),
|
||||
@@ -563,7 +563,7 @@ fn test_metatable() -> Result<()> {
|
||||
struct MyUserData;
|
||||
|
||||
impl UserData for MyUserData {
|
||||
fn add_methods<'a, M: UserDataMethods<'a, Self>>(methods: &mut M) {
|
||||
fn add_methods<M: UserDataMethods<Self>>(methods: &mut M) {
|
||||
methods.add_function("my_type_name", |_, data: AnyUserData| {
|
||||
let metatable = data.get_metatable()?;
|
||||
metatable.get::<String>(MetaMethod::Type)
|
||||
@@ -608,7 +608,7 @@ fn test_metatable() -> Result<()> {
|
||||
struct MyUserData2;
|
||||
|
||||
impl UserData for MyUserData2 {
|
||||
fn add_fields<'a, F: UserDataFields<'a, Self>>(fields: &mut F) {
|
||||
fn add_fields<F: UserDataFields<Self>>(fields: &mut F) {
|
||||
fields.add_meta_field_with("__index", |_| Ok(1));
|
||||
}
|
||||
}
|
||||
@@ -623,7 +623,7 @@ fn test_metatable() -> Result<()> {
|
||||
struct MyUserData3;
|
||||
|
||||
impl UserData for MyUserData3 {
|
||||
fn add_fields<'a, F: UserDataFields<'a, Self>>(fields: &mut F) {
|
||||
fn add_fields<F: UserDataFields<Self>>(fields: &mut F) {
|
||||
fields.add_meta_field_with(MetaMethod::Type, |_| Ok("CustomName"));
|
||||
}
|
||||
}
|
||||
@@ -640,12 +640,12 @@ fn test_userdata_proxy() -> Result<()> {
|
||||
struct MyUserData(i64);
|
||||
|
||||
impl UserData for MyUserData {
|
||||
fn add_fields<'a, F: UserDataFields<'a, Self>>(fields: &mut F) {
|
||||
fn add_fields<F: UserDataFields<Self>>(fields: &mut F) {
|
||||
fields.add_field("static_field", 123);
|
||||
fields.add_field_method_get("n", |_, this| Ok(this.0));
|
||||
}
|
||||
|
||||
fn add_methods<'a, M: UserDataMethods<'a, Self>>(methods: &mut M) {
|
||||
fn add_methods<M: UserDataMethods<Self>>(methods: &mut M) {
|
||||
methods.add_function("new", |_, n| Ok(Self(n)));
|
||||
|
||||
methods.add_method("plus", |_, this, n: i64| Ok(this.0 + n));
|
||||
@@ -726,14 +726,14 @@ fn test_any_userdata_wrap() -> Result<()> {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_userdata_ext() -> Result<()> {
|
||||
fn test_userdata_object_like() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
struct MyUserData(u32);
|
||||
|
||||
impl UserData for MyUserData {
|
||||
fn add_fields<'a, F: UserDataFields<'a, Self>>(fields: &mut F) {
|
||||
fn add_fields<F: UserDataFields<Self>>(fields: &mut F) {
|
||||
fields.add_field_method_get("n", |_, this| Ok(this.0));
|
||||
fields.add_field_method_set("n", |_, this, val| {
|
||||
this.0 = val;
|
||||
@@ -741,7 +741,7 @@ fn test_userdata_ext() -> Result<()> {
|
||||
});
|
||||
}
|
||||
|
||||
fn add_methods<'a, M: UserDataMethods<'a, Self>>(methods: &mut M) {
|
||||
fn add_methods<M: UserDataMethods<Self>>(methods: &mut M) {
|
||||
methods.add_meta_method(MetaMethod::Call, |_, _this, ()| Ok("called"));
|
||||
methods.add_method_mut("add", |_, this, x: u32| {
|
||||
this.0 += x;
|
||||
@@ -752,19 +752,28 @@ fn test_userdata_ext() -> Result<()> {
|
||||
|
||||
let ud = lua.create_userdata(MyUserData(123))?;
|
||||
|
||||
assert_eq!(ud.get::<_, u32>("n")?, 123);
|
||||
assert_eq!(ud.get::<u32>("n")?, 123);
|
||||
ud.set("n", 321)?;
|
||||
assert_eq!(ud.get::<_, u32>("n")?, 321);
|
||||
assert_eq!(ud.get::<_, Option<u32>>("non-existent")?, None);
|
||||
match ud.set::<_, u32>("non-existent", 123) {
|
||||
assert_eq!(ud.get::<u32>("n")?, 321);
|
||||
assert_eq!(ud.get::<Option<u32>>("non-existent")?, None);
|
||||
match ud.set("non-existent", 123) {
|
||||
Err(Error::RuntimeError(_)) => {}
|
||||
r => panic!("expected RuntimeError, got {r:?}"),
|
||||
}
|
||||
|
||||
assert_eq!(ud.call::<_, String>(())?, "called");
|
||||
assert_eq!(ud.call::<String>(())?, "called");
|
||||
|
||||
ud.call_method("add", 2)?;
|
||||
assert_eq!(ud.get::<_, u32>("n")?, 323);
|
||||
ud.call_method::<()>("add", 2)?;
|
||||
assert_eq!(ud.get::<u32>("n")?, 323);
|
||||
|
||||
match ud.call_method::<()>("non_existent", ()) {
|
||||
Err(Error::RuntimeError(err)) => {
|
||||
assert!(err.contains("attempt to call a nil value (function 'non_existent')"))
|
||||
}
|
||||
r => panic!("expected RuntimeError, got {r:?}"),
|
||||
}
|
||||
|
||||
assert!(ud.to_string()?.starts_with("MyUserData"));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -774,7 +783,7 @@ fn test_userdata_method_errors() -> Result<()> {
|
||||
struct MyUserData(i64);
|
||||
|
||||
impl UserData for MyUserData {
|
||||
fn add_methods<'a, M: UserDataMethods<'a, Self>>(methods: &mut M) {
|
||||
fn add_methods<M: UserDataMethods<Self>>(methods: &mut M) {
|
||||
methods.add_method("get_value", |_, data, ()| Ok(data.0));
|
||||
}
|
||||
}
|
||||
@@ -782,7 +791,7 @@ fn test_userdata_method_errors() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let ud = lua.create_userdata(MyUserData(123))?;
|
||||
let res = ud.call_function::<_, ()>("get_value", ());
|
||||
let res = ud.call_function::<()>("get_value", ());
|
||||
let Err(Error::CallbackError { cause, .. }) = res else {
|
||||
panic!("expected CallbackError, got {res:?}");
|
||||
};
|
||||
@@ -845,3 +854,21 @@ fn test_userdata_derive() -> Result<()> {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_nested_userdata_gc() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let counter = Arc::new(());
|
||||
let arr = vec![lua.create_any_userdata(counter.clone())?];
|
||||
let arr_ud = lua.create_any_userdata(arr)?;
|
||||
|
||||
assert_eq!(Arc::strong_count(&counter), 2);
|
||||
drop(arr_ud);
|
||||
// On first iteration Lua will destroy the array, on second - userdata
|
||||
lua.gc_collect()?;
|
||||
lua.gc_collect()?;
|
||||
assert_eq!(Arc::strong_count(&counter), 1);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user