Compare commits

...

47 Commits

Author SHA1 Message Date
Alex Orlenko 7c2e9b5a7c v0.10.0-beta.2 2024-09-07 23:47:21 +01:00
Alex Orlenko 5db545e7b4 mlua_derive: v0.10.0-beta.1 2024-09-07 23:46:13 +01:00
Alex Orlenko b88228b3d4 Update CHANGELOG 2024-09-07 23:43:20 +01:00
Alex Orlenko 8677b57847 Update README 2024-09-07 23:27:35 +01:00
Alex Orlenko 7543b0674e mlua-sys: v0.6.3 2024-09-07 23:23:47 +01:00
Alex Orlenko 8e111058c3 Make BorrowedBytes/BorrowedStr: Send + Sync
They are immutable and don't require holding a lock
2024-09-07 23:17:25 +01:00
Alex Orlenko e1c0aa8491 Fix test test_integer_from_lua 2024-09-07 17:01:04 +01:00
Alex Orlenko 7957c6868d Fastpath for LuaString/integer/float conversion from Lua 2024-09-07 12:25:35 +01:00
Caleb Maclennan 9c86eefb76 Update documentation of traits to match the expected argument name (#447) 2024-09-06 19:28:40 +01:00
Alex Orlenko 7272e40c23 Faster non-scoped callbacks 2024-09-03 01:34:55 +01:00
Alex Orlenko c6ef393ce9 Turn Lua::entrypoint() to constructor (don't require initializing Lua first) 2024-09-02 23:43:52 +01:00
Alex Orlenko d25f2fc07c Take Table instead of impl IntoLua in Chunk::set_environment() 2024-09-01 23:06:27 +01:00
Alex Orlenko 104e242ddd Some cosmetic changes 2024-09-01 22:48:56 +01:00
Alex Orlenko 825bdbfa04 Use dynamic Lua state ownership instead of compile-time module cfg flag to allow
creating new VMs in module mode (and destructing them properly).
2024-08-31 22:52:55 +01:00
Alex Orlenko 1634c43f0a Disable send feature in module mode
We don't have exclusive access to Lua VM and cannot provide `Sync` soundness
2024-08-30 23:37:26 +01:00
Alex Orlenko d6b27de34e Optimize ObjectLike::to_string for tables and userdata 2024-08-30 22:55:53 +01:00
Alex Orlenko 4018a17e26 Combine TableExt and AnyUserDataExt traits into ObjectLike 2024-08-30 22:50:03 +01:00
Alex Orlenko 5ebbc0868c More inline const expressions 2024-08-29 22:05:28 +01:00
Alex Orlenko 3774296835 Run gargabe collection on main Lua instance drop
This should help preventing leaking memory when capturing Lua in async block
and dropping future without finishing polling.
2024-08-29 11:59:52 +01:00
Alex Orlenko ece66c46bf Remove unstable feature flag 2024-08-26 23:51:58 +01:00
Alex Orlenko 66b4a865c2 Remove MultiValue pool 2024-08-26 11:26:12 +01:00
Alex Orlenko 21149106ee Remove drop field from ValueRef 2024-08-26 11:18:17 +01:00
Alex Orlenko 74bebe6da3 Add optional Send requirement to internall callbacks 2024-08-26 11:13:06 +01:00
Alex Orlenko 9891e86d16 Remove Clone requirement from UserDataFields::add_field() 2024-08-26 11:10:34 +01:00
Alex Orlenko e3c5cfdf19 Extract registry_key and vector modules from types 2024-08-26 00:27:41 +01:00
Alex Orlenko 4977b91a98 Detect compilation error and return Result when using Compiler::compile() interface.
Closes #387
2024-08-25 23:07:13 +01:00
Alex Orlenko 6317b8e0c8 Test GC for nested userdata (userdata in userdata) 2024-08-25 21:02:12 +01:00
Alex Orlenko ecc09c4387 Add luaL_loadbufferenv helper to all Lua versions 2024-08-25 17:29:58 +01:00
bjcscat 7bfd32750d Change chunk env to use luau's load env parameter (#442) 2024-08-25 16:50:41 +01:00
Alex Orlenko 23d4e2519b Switch to Mutex from RwLock for userdata access in send mode.
Unfortunately RwLock allow access to the userdata from multiple threads
without enforcing `Sync` marker.
2024-08-24 09:44:39 +01:00
Alex Orlenko 2857cb76c6 Add A param to AsyncThread<A, R>.
This reduces internal dependency on `MultiValue` container and delay args conversion to the future `poll()` stage.
2024-08-23 01:04:06 +01:00
Alex Orlenko fdc50bffc9 Fix memory leak when polling async futures 2024-08-23 01:03:54 +01:00
Alex Orlenko 9931709ecd Remove explicit lifetime from UserDataMethods and UserDataFields traits.
Pass `'static` arguments to async functions and require `'static` Future.
(in future we can use async closures to make it more elegant).
2024-08-23 00:10:29 +01:00
Alex Orlenko 8092f00930 Do not require Lua to be alive when dropping AsyncThread 2024-08-22 23:41:51 +01:00
Alex Orlenko d2e87943ac Skip extra lock when resuming thread 2024-08-22 23:41:50 +01:00
Sven Niederberger 26b9bdb362 serde_userdata: Remove map_err to reduce compile time impact (#441) 2024-08-21 12:06:49 +01:00
Alex Orlenko c58f67b140 Add MaybeSend requirement to Lua futures 2024-08-10 17:55:39 +01:00
Sven Niederberger 0c08cdaf7c Reduce compile time contribution of next_key_seed and next_value_seed (#436)
* factor out common code

* changelog entry
2024-08-06 20:04:17 +01:00
Alex Orlenko 10999babe0 Replace MultiValue::extend_from_values with from_lua_iter 2024-08-06 01:32:20 +01:00
Alex Orlenko f0a995a357 Make MultiValue::with_capacity public 2024-08-05 23:36:17 +01:00
Alex Orlenko aa47324ee9 Use pool for MultiValue container 2024-08-05 22:07:19 +01:00
Alex Orlenko ac6a391426 Remove Lua::into_static and Lua::from_static (undocumented). 2024-08-05 14:57:15 +01:00
Alex Orlenko 8e14b6e40b Fix tests 2024-08-04 22:22:02 +01:00
Alex Orlenko c117a4c1af Fix loading (fetching) stdlib modules when using require in Luau.
Fixes #435.
2024-08-04 18:55:17 +01:00
Alex Orlenko 4082b354fe clippy 2024-08-01 00:55:10 +01:00
Alex Orlenko 3641c98959 Prepare for Rust 2024 edition (see rust-lang/rust#123748)
Replace `IntoLua(Multi)` generic with positional arg (impl trait) where possible
This allow to shorten syntax from `a.get::<_, T>` to `a.get::<T>`
2024-07-31 23:42:43 +01:00
Alex Orlenko b7d170ab9b Refactor ThreadStatus:
- Add `ThreadStatus::Running`
- Replace `ThreadStatus::Unresumable` with `ThreadStatus::Finished`
Change `Error::CoroutineInactive` to `Error::CoroutineUnresumable`
2024-07-31 22:34:45 +01:00
86 changed files with 2245 additions and 2130 deletions
+12 -11
View File
@@ -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"
+17
View File
@@ -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
View File
@@ -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 }
+12 -15
View File
@@ -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
View File
@@ -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
View File
@@ -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,
);
+2 -2
View File
@@ -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();
+5 -5
View File
@@ -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());
+7 -7
View File
@@ -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);
}
});
+7 -7
View File
@@ -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())
+2 -2
View File
@@ -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
View File
@@ -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"
+19
View File
@@ -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,
+19
View File
@@ -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
}
+21 -1
View File
@@ -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
}
+19
View File
@@ -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
//
+16 -4
View File
@@ -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)]
+2 -2
View File
@@ -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 }
+4 -3
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -52,7 +52,7 @@ pub struct Options {
impl Default for Options {
fn default() -> Self {
Self::new()
const { Self::new() }
}
}
+80 -87
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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);
}
+78
View File
@@ -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
View File
@@ -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")]
+101
View File
@@ -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);
}
+86
View File
@@ -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
View File
@@ -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
View File
@@ -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);
}
-180
View File
@@ -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))
}
}
}
}
}
+93
View File
@@ -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)
}
}
}
+93
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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(())
}
-1
View File
@@ -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")]
{
+9 -12
View File
@@ -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
|
+57 -38
View File
@@ -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> {
+1 -1
View File
@@ -11,7 +11,7 @@ fn main() -> Result<()> {
lua.create_function(move |_, ()| {
Ok(data.get())
})?
.call::<_, i32>(())?;
.call::<i32>(())?;
Ok(())
}
+144 -46
View File
@@ -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> {
+1 -1
View File
@@ -12,7 +12,7 @@ fn main() {
inner = Some(t);
Ok(())
})?;
f.call::<_, ()>(lua.create_table()?)?;
f.call::<()>(lua.create_table()?)?;
Ok(())
});
}
+4 -25
View File
@@ -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`
+1 -1
View File
@@ -9,7 +9,7 @@ fn main() {
inner = Some(t);
Ok(())
})?;
f.call::<_, ()>(lua.create_table()?)?;
f.call::<()>(lua.create_table()?)?;
Ok(())
});
}
+5 -42
View File
@@ -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`
+1 -1
View File
@@ -9,7 +9,7 @@ fn main() {
outer = Some(t);
Ok(())
})?;
f.call::<_, ()>(lua.create_table()?)?;
f.call::<()>(lua.create_table()?)?;
Ok(())
});
}
+5 -30
View File
@@ -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`
+1 -1
View File
@@ -18,6 +18,6 @@ fn main() {
})?
};
f.call::<_, ()>(())
f.call::<()>(())
});
}
+5 -25
View File
@@ -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`
+4 -8
View File
@@ -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`
+3 -13
View File
@@ -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`
-32
View File
@@ -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(())
}
-31
View File
@@ -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
+2 -2
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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),
};
+12
View File
@@ -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") {
+12
View File
@@ -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())
+35
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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(())
}