Compare commits

...

43 Commits

Author SHA1 Message Date
Alex Orlenko 0453029765 v0.10.0-rc.1 2024-10-08 23:00:04 +01:00
Alex Orlenko 7b777d074e Update README 2024-10-08 22:57:32 +01:00
Alex Orlenko c6cd1c53c3 Update CHANGELOG 2024-10-08 22:57:18 +01:00
Alex Orlenko 669349d704 mlua_derive: v0.10.0-rc.1 2024-10-08 22:40:07 +01:00
Alex Orlenko c086c144d0 Use impl IntoIterator in Lua::create_table_from/create_sequence_from 2024-10-08 11:53:31 +01:00
Alex Orlenko 640cb2c182 Add _unguarded to RawLua::app_data_ref (for internal use only) 2024-10-07 13:37:41 +01:00
Alex Orlenko 03a4068d55 Move MultiValue from value to multi module 2024-10-07 13:37:39 +01:00
Alex Orlenko 9f6c78532f Move IntoLua/FromLua and IntoLuaMulti/FromLuaMulti to traits module 2024-10-07 10:32:50 +01:00
Alex Orlenko 8aecc83f53 clippy 2024-10-06 22:48:58 +01:00
Alex Orlenko 4891b6535c Remove const from Value::type_name 2024-10-06 22:45:56 +01:00
Alex Orlenko fa343c2c69 More optimal OsStr/Path conversion to Lua 2024-10-06 22:45:36 +01:00
psentee 6d5e735bed Add IntoLua/FromLua for OsString/OsStr and PathBuf/Path (#459) 2024-10-06 13:43:20 +01:00
Alex Orlenko ac315fd80b Add AnyUserData::wrap_ser function 2024-10-06 11:12:44 +01:00
Alex Orlenko f95161c6e0 Add missing documentation for Function::wrap_raw* functions 2024-10-05 23:26:03 +01:00
Alex Orlenko 4bc846a119 Add optional anyhow dependency (under the same feature flag) to implement IntoLua for anyhow::Error 2024-10-05 23:16:36 +01:00
Alex Orlenko a3ca95fc8f Include Value::Other variant into Value::to_pointer() helper.
Closes #465
2024-10-04 10:33:56 +01:00
Alex Orlenko ae4897ab2e Add Value::is_error and Value::as_error helpers 2024-10-02 12:59:04 +01:00
Alex Orlenko 4ac87c7208 Derive PartialEq instead of implementing manually 2024-10-02 12:21:04 +01:00
Alex Orlenko 04d8106676 Remove SubtypeId from AnyUserData and instead add Value::Other variant that will cover any unknown types (eg. LuaJIT CData) 2024-10-02 12:16:48 +01:00
Alex Orlenko b6cdf32f16 Update MSRV in README 2024-10-01 23:21:28 +01:00
Alex Orlenko ad9bc36764 impl Eq/Ord for Lua String 2024-10-01 23:20:19 +01:00
Alex Orlenko 4b8c26e682 Invoke __tostring metamethod when calling Value::to_string() for Buffer type 2024-10-01 22:59:19 +01:00
Alex Orlenko 7839c4438c Fix compilation warnings 2024-10-01 22:28:47 +01:00
Alex Orlenko 529361fcbc Add new Buffer type for Luau.
Previously it was represented as `AnyUserData` which is not always convenient.
2024-10-01 15:21:19 +01:00
Alex Orlenko 4dddf3c18d Use fmt::Debug implementation for Lua string from bstr 2024-09-26 22:46:02 +01:00
Alex Orlenko fb0c0d9ee9 Add Either<L, R> enum to combine two types into a single one.
It implements `FromLua` and `IntoLua` traits for easy type conversions..
2024-09-26 18:58:28 +01:00
Alex Orlenko 235c32006c Rename Lua::with_raw_state to Lua::exec_raw 2024-09-24 23:11:16 +01:00
Alex Orlenko b65901e444 Add Error::chain method to return iterator over nested errors 2024-09-24 22:48:19 +01:00
Alex Orlenko 91fe02da45 Add LuaNativeFn/LuaNativeFnMut/LuaNativeAsyncFn traits for using in Function::wrap 2024-09-24 14:35:03 +01:00
Alex Orlenko 8274b5fa88 More user-friendly error message on userdata mismatch 2024-09-23 15:56:29 +01:00
Alex Orlenko 762e677a70 Update Error matching code
This is mostly cosmetic change.
2024-09-23 15:53:08 +01:00
Alex Orlenko 5b5f1e4669 Remove undocumented Lua::push in favour of Lua::with_raw_state 2024-09-23 11:14:24 +01:00
Alex Orlenko e582e7c57f Rename get_metatable to metatable for Table/AnyUserData types 2024-09-23 11:13:16 +01:00
Alex Orlenko 3714da5ec8 Fix doc test for Lua::set_type_metatable 2024-09-23 11:01:32 +01:00
Alex Orlenko ca69be07ff Support setting metatable for Lua builtin types.
Closes #445
2024-09-23 10:45:57 +01:00
Alex Orlenko 16951e3628 Move ValueRef to a new module 2024-09-22 23:58:53 +01:00
Alex Orlenko 8bb2b444ab Run tests with forced memory limit checks 2024-09-22 23:20:18 +01:00
Alex Orlenko fc1570d2d7 Support yielding from hooks for Lua 5.3+ 2024-09-22 19:04:32 +01:00
Alex Orlenko fce85381c6 Fix clippy warnings 2024-09-22 19:02:16 +01:00
Alex Orlenko 3088516851 Update luaL_checkstack messages 2024-09-22 17:33:28 +01:00
Alex Orlenko 5162a0f46e Add Lua::with_raw_state to provide easy low-level access to the Lua state. 2024-09-22 11:25:38 +01:00
Alex Orlenko 640d27697d Fix compile error in non-send mode 2024-09-20 21:20:52 +01:00
Alex Orlenko da4404baa5 Add Lua::scope back 2024-09-20 13:01:55 +01:00
85 changed files with 3148 additions and 2443 deletions
+2 -2
View File
@@ -10,14 +10,14 @@ jobs:
options: --security-opt seccomp=unconfined
steps:
- name: Checkout repository
uses: actions/checkout@v3
uses: actions/checkout@main
- name: Generate coverage report
run: |
cargo tarpaulin --out xml --tests --exclude-files benches/* --exclude-files mlua-sys/src/*/*
- name: Upload report to codecov.io
uses: codecov/codecov-action@v3
uses: codecov/codecov-action@v4
with:
token: ${{secrets.CODECOV_TOKEN}}
fail_ci_if_error: false
+51 -25
View File
@@ -7,18 +7,18 @@ jobs:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-22.04, macos-latest, windows-latest]
os: [ubuntu-latest, macos-latest, windows-latest]
rust: [stable]
lua: [lua54, lua53, lua52, lua51, luajit, luau, luau-jit, luau-vector4]
include:
- os: ubuntu-22.04
- os: ubuntu-latest
target: x86_64-unknown-linux-gnu
- os: macos-latest
target: x86_64-apple-darwin
- os: windows-latest
target: x86_64-pc-windows-msvc
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@main
- uses: dtolnay/rust-toolchain@stable
with:
toolchain: ${{ matrix.rust }}
@@ -31,7 +31,7 @@ jobs:
cargo build --features "${{ matrix.lua }},vendored,async,serialize,macros,send"
shell: bash
- name: Build ${{ matrix.lua }} pkg-config
if: ${{ matrix.os == 'ubuntu-22.04' }}
if: ${{ matrix.os == 'ubuntu-latest' }}
run: |
sudo apt-get update
sudo apt-get install -y --no-install-recommends liblua5.4-dev liblua5.3-dev liblua5.2-dev liblua5.1-0-dev libluajit-5.1-dev
@@ -45,7 +45,7 @@ jobs:
matrix:
lua: [lua54, lua53, lua52, lua51, luajit]
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@main
- uses: dtolnay/rust-toolchain@stable
with:
toolchain: stable
@@ -55,13 +55,13 @@ jobs:
build_aarch64_cross_ubuntu:
name: Cross-compile to aarch64-unknown-linux-gnu
runs-on: ubuntu-22.04
runs-on: ubuntu-latest
needs: build
strategy:
matrix:
lua: [lua54, lua53, lua52, lua51, luajit]
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@main
- uses: dtolnay/rust-toolchain@stable
with:
toolchain: stable
@@ -77,13 +77,13 @@ jobs:
build_armv7_cross_ubuntu:
name: Cross-compile to armv7-unknown-linux-gnueabihf
runs-on: ubuntu-22.04
runs-on: ubuntu-latest
needs: build
strategy:
matrix:
lua: [lua54, lua53, lua52, lua51]
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@main
- uses: dtolnay/rust-toolchain@stable
with:
toolchain: stable
@@ -103,18 +103,18 @@ jobs:
needs: build
strategy:
matrix:
os: [ubuntu-22.04, macos-latest, windows-latest]
os: [ubuntu-latest, macos-latest, windows-latest]
rust: [stable, nightly]
lua: [lua54, lua53, lua52, lua51, luajit, luajit52, luau, luau-jit, luau-vector4]
include:
- os: ubuntu-22.04
- os: ubuntu-latest
target: x86_64-unknown-linux-gnu
- os: macos-latest
target: x86_64-apple-darwin
- os: windows-latest
target: x86_64-pc-windows-msvc
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@main
- uses: dtolnay/rust-toolchain@stable
with:
toolchain: ${{ matrix.rust }}
@@ -139,14 +139,14 @@ jobs:
needs: build
strategy:
matrix:
os: [ubuntu-22.04]
os: [ubuntu-latest]
rust: [nightly]
lua: [lua54, lua53, lua52, lua51, luajit, luau, luau-jit, luau-vector4]
include:
- os: ubuntu-22.04
- os: ubuntu-latest
target: x86_64-unknown-linux-gnu
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@main
- uses: dtolnay/rust-toolchain@stable
with:
toolchain: ${{ matrix.rust }}
@@ -160,22 +160,48 @@ jobs:
env:
RUSTFLAGS: -Z sanitizer=address
test_with_memory_limit:
name: Test with memory limit
runs-on: ${{ matrix.os }}
needs: build
strategy:
matrix:
os: [ubuntu-latest]
rust: [nightly]
lua: [lua54, lua53, lua52, lua51, luajit, luau, luau-jit, luau-vector4]
include:
- os: ubuntu-latest
target: x86_64-unknown-linux-gnu
steps:
- uses: actions/checkout@main
- uses: dtolnay/rust-toolchain@stable
with:
toolchain: ${{ matrix.rust }}
target: ${{ matrix.target }}
- uses: Swatinem/rust-cache@v2
- name: Run ${{ matrix.lua }} tests with forced memory limit
run: |
cargo test --tests --features "${{ matrix.lua }},vendored,async,send,serialize,macros"
shell: bash
env:
RUSTFLAGS: --cfg=force_memory_limit
test_modules:
name: Test modules
runs-on: ${{ matrix.os }}
needs: build
strategy:
matrix:
os: [ubuntu-22.04, macos-latest]
os: [ubuntu-latest, macos-latest]
rust: [stable]
lua: [lua54, lua53, lua52, lua51, luajit, luau]
include:
- os: ubuntu-22.04
- os: ubuntu-latest
target: x86_64-unknown-linux-gnu
- os: macos-latest
target: x86_64-apple-darwin
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@main
- uses: dtolnay/rust-toolchain@stable
with:
toolchain: ${{ matrix.rust }}
@@ -199,7 +225,7 @@ jobs:
shell: msys2 {0}
steps:
- uses: msys2/setup-msys2@v2
- uses: actions/checkout@v4
- uses: actions/checkout@main
- name: Install Rust & Lua
run: |
pacman -S --noconfirm mingw-w64-x86_64-rust mingw-w64-x86_64-lua mingw-w64-x86_64-luajit mingw-w64-x86_64-pkg-config
@@ -210,13 +236,13 @@ jobs:
test_wasm32_emscripten:
name: Test on wasm32-unknown-emscripten
runs-on: ubuntu-22.04
runs-on: ubuntu-latest
needs: build
strategy:
matrix:
lua: [lua54, lua53, lua52, lua51, luau]
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@main
- uses: dtolnay/rust-toolchain@stable
with:
toolchain: stable
@@ -232,9 +258,9 @@ jobs:
rustfmt:
name: Rustfmt
runs-on: ubuntu-22.04
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@main
- uses: dtolnay/rust-toolchain@nightly
with:
components: rustfmt
@@ -242,12 +268,12 @@ jobs:
clippy:
name: Clippy
runs-on: ubuntu-22.04
runs-on: ubuntu-latest
strategy:
matrix:
lua: [lua54, lua53, lua52, lua51, luajit, luau, luau-jit, luau-vector4]
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@main
- uses: dtolnay/rust-toolchain@stable
with:
toolchain: nightly
+15
View File
@@ -1,3 +1,18 @@
## v0.10.0-rc.1
- `Lua::scope` is back
- Support yielding from hooks for Lua 5.3+
- Support setting metatable for Lua builtin types (number/string/function/etc)
- Added `LuaNativeFn`/`LuaNativeFnMut`/`LuaNativeAsyncFn` traits for using in `Function::wrap`
- Added `Error::chain` method to return iterator over nested errors
- Added `Lua::exec_raw` helper to execute low-level Lua C API code
- Added `Either<L, R>` enum to combine two types into a single one
- Added a new `Buffer` type for Luau
- Added `Value::is_error` and `Value::as_error` helpers
- Added `Value::Other` variant to represent unknown Lua types (eg LuaJIT CDATA)
- Added (optional) `anyhow` feature to implement `IntoLua` for `anyhow::Error`
- Added `IntoLua`/`FromLua` for `OsString`/`OsStr` and `PathBuf`/`Path`
## v0.10.0-beta.2
- Updated `ThreadStatus` enum to include `Running` and `Finished` variants.
+4 -2
View File
@@ -1,6 +1,6 @@
[package]
name = "mlua"
version = "0.10.0-beta.2" # remember to update mlua_derive
version = "0.10.0-rc.1" # remember to update mlua_derive
authors = ["Aleksandr Orlenko <zxteam@pm.me>", "kyren <catherine@kyju.org>"]
rust-version = "1.79.0"
edition = "2021"
@@ -41,9 +41,10 @@ async = ["dep:futures-util"]
send = ["parking_lot/send_guard"]
serialize = ["dep:serde", "dep:erased-serde", "dep:serde-value"]
macros = ["mlua_derive/macros"]
anyhow = ["dep:anyhow"]
[dependencies]
mlua_derive = { version = "=0.10.0-beta.1", optional = true, path = "mlua_derive" }
mlua_derive = { version = "=0.10.0-rc.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"
@@ -52,6 +53,7 @@ serde = { version = "1.0", optional = true }
erased-serde = { version = "0.4", optional = true }
serde-value = { version = "0.7", optional = true }
parking_lot = { version = "0.12", features = ["arc_lock"] }
anyhow = { version = "1.0", optional = true }
ffi = { package = "mlua-sys", version = "0.6.3", path = "mlua-sys" }
+8 -3
View File
@@ -9,7 +9,7 @@
[docs.rs]: https://docs.rs/mlua
[Coverage Status]: https://codecov.io/gh/mlua-rs/mlua/branch/main/graph/badge.svg?token=99339FS1CG
[codecov.io]: https://codecov.io/gh/mlua-rs/mlua
[MSRV]: https://img.shields.io/badge/rust-1.71+-brightgreen.svg?&logo=rust
[MSRV]: https://img.shields.io/badge/rust-1.79+-brightgreen.svg?&logo=rust
[Guided Tour] | [Benchmarks] | [FAQ]
@@ -19,6 +19,10 @@
# The main branch is the v0.10, development version of `mlua`. Please see the [v0.9](https://github.com/mlua-rs/mlua/tree/v0.9) branch for the stable versions of `mlua`.
> **Note**
>
> See (upcoming) v0.10 [release notes](https://github.com/khvzak/mlua/blob/main/docs/release_notes/v0.10.md).
`mlua` is bindings to [Lua](https://www.lua.org) programming language for Rust with a goal to provide
_safe_ (as far as it's possible), high level, easy to use, practical and flexible API.
@@ -53,6 +57,7 @@ Below is a list of the available feature flags. By default `mlua` does not enabl
* `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!`)
* `anyhow`: enable `anyhow::Error` conversion into Lua
[5.4]: https://www.lua.org/manual/5.4/manual.html
[5.3]: https://www.lua.org/manual/5.3/manual.html
@@ -128,7 +133,7 @@ Add to `Cargo.toml` :
``` toml
[dependencies]
mlua = { version = "0.9.9", features = ["lua54", "vendored"] }
mlua = { version = "0.10.0-rc.1", features = ["lua54", "vendored"] }
```
`main.rs`
@@ -163,7 +168,7 @@ Add to `Cargo.toml` :
crate-type = ["cdylib"]
[dependencies]
mlua = { version = "0.9.9", features = ["lua54", "module"] }
mlua = { version = "0.10.0-rc.1", features = ["lua54", "module"] }
```
`lib.rs` :
+123
View File
@@ -0,0 +1,123 @@
## mlua v0.10 release notes
The v0.10 version of mlua has goal to improve the user experience while keeping the same performance and safety guarantees.
This document highlights the most notable features. For a full list of changes, see the [CHANGELOG].
[CHANGELOG]: https://github.com/khvzak/mlua/blob/main/CHANGELOG.md
### New features
#### `'static` Lua types
In previous mlua versions, it was required to have a `'lua` lifetime attached to every Lua value. v0.9 introduced (experimental) owned types that are `'static` without a lifetime attached, but they kept strong references to the Lua instance.
In v0.10 all Lua types are `'static` and have only weak reference to the Lua instance. It means they are more flexible and can be used in more places without worrying about memory leaks.
#### Truly `send` feature
In this version Lua is `Send + Sync` when the `send` feature flag is enabled (previously was only `Send`). It means Lua instance and their values can be safely shared between threads and used in multi threaded async contexts.
```rust
let lua = Lua::new();
lua.globals().set("i", 0)?;
let func = lua.load("i = i + ...").into_function()?;
std::thread::scope(|s| {
s.spawn(|| {
for i in 0..5 {
func.call::<()>(i).unwrap();
}
});
s.spawn(|| {
for i in 0..5 {
func.call::<()>(i).unwrap();
}
});
});
assert_eq!(lua.globals().get::<i32>("i")?, 20);
```
Under the hood, to synchronize access to the Lua state, mlua uses [`ReentrantMutex`] which can be recursively locked by a single thread. Only one thread can execute Lua code at a time, but it's possible to share Lua values between threads.
This has some performance penalties (about 10-20%) compared to the lock free mode. This flag is disabled by default and does not supported in module mode.
[`ReentrantMutex`]: https://docs.rs/parking_lot/latest/parking_lot/type.ReentrantMutex.html
#### Register Rust functions with variable number of arguments
The new traits `LuaNativeFn`/`LuaNativeFnMut`/`LuaNativeAsyncFn` have been introduced to provide a way to register Rust functions with variable number of arguments in Lua, without needing to pass all arguments as a tuple.
They are used by `Function::wrap`/`Function::wrap_mut`/`Function::wrap_async` methods:
```rust
let add = Function::wrap(|a: i64, b: i64| Ok(a + b));
lua.globals().set("add", add).unwrap();
// Prints 50
lua.load(r#"print(add(5, 45))"#).exec().unwrap();
```
To wrap functions that return direct value (non-`Result`) you can use `Function::wrap_raw` method.
#### Setting metatable for Lua builtin types
For Lua builtin types (like `string`, `function`, `number`, etc.) that have a shared metatable for all instances, it's now possible to set a custom metatable for them.
```rust
let mt = lua.create_table()?;
mt.set("__tostring", lua.create_function(|_, b: bool| Ok(if b { "2" } else { "0" }))?)?;
lua.set_type_metatable::<bool>(Some(mt));
lua.load("assert(tostring(true) == '2')").exec().unwrap();
```
### Improvements
#### New `ObjectLike` trait
The `ObjectLike` trait is a combination of the `AnyUserDataExt` and `TableExt` traits used in previous versions. It provides a unified interface for working with Lua tables and userdata.
#### `Either<L, R>` enum
The `Either<L, R>` enum is a simple enum that can hold either `L` or `R` value. It's useful when you need to return or receive one of two types in a function.
This type implements `IntoLua` and `FromLua` traits and can generate a meaningful error message when conversion fails.
```rust
let func = Function::wrap(|x: Either<i32, String>| Ok(format!("received: {x}")));
lua.globals().set("func", func).unwrap();
// Prints: received: 123
lua.load(r#"print(func(123))"#).exec().unwrap();
// Prints: bad argument #1: error converting Lua table to Either<i32, String>
lua.load(r#"print(pcall(func, {}))"#).exec().unwrap();
```
#### `Lua::exec_raw` helper to execute low-level Lua C API code
For advanced users, it's now possible to execute low-level Lua C API code using the `Lua::exec_raw` method.
```rust
let t = lua.create_sequence_from([1, 2, 3, 4, 5])?;
let sum: i64 = unsafe {
lua.exec_raw(&t, |state| {
// top of the stack: table `t`
let mut sum = 0;
// push nil as the first key
mlua::ffi::lua_pushnil(state);
while mlua::ffi::lua_next(state, -2) != 0 {
sum += mlua::ffi::lua_tointeger(state, -1);
// Remove the value, keep the key for the next iteration
mlua::ffi::lua_pop(state, 1);
}
mlua::ffi::lua_pop(state, 1);
mlua::ffi::lua_pushinteger(state, sum);
// top of the stack: sum
})
}?;
assert_eq!(sum, 15);
```
The `exec_raw` method is longjmp-safe. It's not recommended to move `Drop` types into the closure to avoid possible memory leaks.
+5 -5
View File
@@ -176,7 +176,7 @@ pub unsafe fn lua_rotate(L: *mut lua_State, mut idx: c_int, mut n: c_int) {
#[inline(always)]
pub unsafe fn lua_copy(L: *mut lua_State, fromidx: c_int, toidx: c_int) {
let abs_to = lua_absindex(L, toidx);
luaL_checkstack(L, 1, cstr!("not enough stack slots"));
luaL_checkstack(L, 1, cstr!("not enough stack slots available"));
lua_pushvalue(L, fromidx);
lua_replace(L, abs_to);
}
@@ -314,7 +314,7 @@ pub unsafe fn lua_rawseti(L: *mut lua_State, idx: c_int, n: lua_Integer) {
#[inline(always)]
pub unsafe fn lua_rawsetp(L: *mut lua_State, idx: c_int, p: *const c_void) {
let abs_i = lua_absindex(L, idx);
luaL_checkstack(L, 1, cstr!("not enough stack slots"));
luaL_checkstack(L, 1, cstr!("not enough stack slots available"));
lua_pushlightuserdata(L, p as *mut c_void);
lua_insert(L, -2);
lua_rawset(L, abs_i);
@@ -444,7 +444,7 @@ pub unsafe fn luaL_loadbufferx(
#[inline(always)]
pub unsafe fn luaL_len(L: *mut lua_State, idx: c_int) -> lua_Integer {
let mut isnum = 0;
luaL_checkstack(L, 1, cstr!("not enough stack slots"));
luaL_checkstack(L, 1, cstr!("not enough stack slots available"));
lua_len(L, idx);
let res = lua_tointegerx(L, -1, &mut isnum);
lua_pop(L, 1);
@@ -526,14 +526,14 @@ pub unsafe fn luaL_tolstring(L: *mut lua_State, mut idx: c_int, len: *mut usize)
#[inline(always)]
pub unsafe fn luaL_setmetatable(L: *mut lua_State, tname: *const c_char) {
luaL_checkstack(L, 1, cstr!("not enough stack slots"));
luaL_checkstack(L, 1, cstr!("not enough stack slots available"));
luaL_getmetatable(L, tname);
lua_setmetatable(L, -2);
}
pub unsafe fn luaL_getsubtable(L: *mut lua_State, idx: c_int, fname: *const c_char) -> c_int {
let abs_i = lua_absindex(L, idx);
luaL_checkstack(L, 3, cstr!("not enough stack slots"));
luaL_checkstack(L, 3, cstr!("not enough stack slots available"));
lua_pushstring_(L, fname);
if lua_gettable(L, abs_i) == LUA_TTABLE {
return 1;
+5 -5
View File
@@ -108,7 +108,7 @@ pub unsafe fn lua_rotate(L: *mut lua_State, mut idx: c_int, mut n: c_int) {
#[inline(always)]
pub unsafe fn lua_copy(L: *mut lua_State, fromidx: c_int, toidx: c_int) {
let abs_to = lua_absindex(L, toidx);
luaL_checkstack(L, 1, cstr!("not enough stack slots"));
luaL_checkstack(L, 1, cstr!("not enough stack slots available"));
lua_pushvalue(L, fromidx);
lua_replace(L, abs_to);
}
@@ -217,7 +217,7 @@ pub unsafe fn lua_rawseti(L: *mut lua_State, idx: c_int, n: lua_Integer) {
#[inline(always)]
pub unsafe fn lua_rawsetp(L: *mut lua_State, idx: c_int, p: *const c_void) {
let abs_i = lua_absindex(L, idx);
luaL_checkstack(L, 1, cstr!("not enough stack slots"));
luaL_checkstack(L, 1, cstr!("not enough stack slots available"));
lua_pushlightuserdata(L, p as *mut c_void);
lua_insert(L, -2);
lua_rawset(L, abs_i);
@@ -381,7 +381,7 @@ pub unsafe fn luaL_loadbuffer(
#[inline(always)]
pub unsafe fn luaL_len(L: *mut lua_State, idx: c_int) -> lua_Integer {
let mut isnum = 0;
luaL_checkstack(L, 1, cstr!("not enough stack slots"));
luaL_checkstack(L, 1, cstr!("not enough stack slots available"));
lua_len(L, idx);
let res = lua_tointegerx(L, -1, &mut isnum);
lua_pop(L, 1);
@@ -463,14 +463,14 @@ pub unsafe fn luaL_tolstring(L: *mut lua_State, mut idx: c_int, len: *mut usize)
#[inline(always)]
pub unsafe fn luaL_setmetatable(L: *mut lua_State, tname: *const c_char) {
luaL_checkstack(L, 1, cstr!("not enough stack slots"));
luaL_checkstack(L, 1, cstr!("not enough stack slots available"));
luaL_getmetatable(L, tname);
lua_setmetatable(L, -2);
}
pub unsafe fn luaL_getsubtable(L: *mut lua_State, idx: c_int, fname: *const c_char) -> c_int {
let abs_i = lua_absindex(L, idx);
luaL_checkstack(L, 3, cstr!("not enough stack slots"));
luaL_checkstack(L, 3, cstr!("not enough stack slots available"));
lua_pushstring_(L, fname);
if lua_gettable(L, abs_i) == LUA_TTABLE {
return 1;
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "mlua_derive"
version = "0.10.0-beta.1"
version = "0.10.0-rc.1"
authors = ["Aleksandr Orlenko <zxteam@pm.me>"]
edition = "2021"
description = "Procedural macros for the mlua crate."
+1 -1
View File
@@ -20,7 +20,7 @@ pub fn from_lua(input: TokenStream) -> TokenStream {
::mlua::Value::UserData(ud) => Ok(ud.borrow::<Self>()?.clone()),
_ => Err(::mlua::Error::FromLuaConversionError {
from: value.type_name(),
to: #ident_str,
to: #ident_str.to_string(),
message: None,
}),
}
+86
View File
@@ -0,0 +1,86 @@
#[cfg(feature = "serialize")]
use serde::ser::{Serialize, Serializer};
use crate::types::ValueRef;
/// A Luau buffer type.
///
/// See the buffer [documentation] for more information.
///
/// [documentation]: https://luau.org/library#buffer-library
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
#[derive(Clone, Debug, PartialEq)]
pub struct Buffer(pub(crate) ValueRef);
#[cfg_attr(not(feature = "luau"), allow(unused))]
impl Buffer {
/// Copies the buffer data into a new `Vec<u8>`.
pub fn to_vec(&self) -> Vec<u8> {
unsafe { self.as_slice().to_vec() }
}
/// Returns the length of the buffer.
pub fn len(&self) -> usize {
unsafe { self.as_slice().len() }
}
/// Returns `true` if the buffer is empty.
#[doc(hidden)]
pub fn is_empty(&self) -> bool {
self.len() == 0
}
/// Reads given number of bytes from the buffer at the given offset.
///
/// Offset is 0-based.
#[track_caller]
pub fn read_bytes<const N: usize>(&self, offset: usize) -> [u8; N] {
let data = unsafe { self.as_slice() };
let mut bytes = [0u8; N];
bytes.copy_from_slice(&data[offset..offset + N]);
bytes
}
/// Writes given bytes to the buffer at the given offset.
///
/// Offset is 0-based.
#[track_caller]
pub fn write_bytes(&self, offset: usize, bytes: &[u8]) {
let data = unsafe {
let (buf, size) = self.as_raw_parts();
std::slice::from_raw_parts_mut(buf, size)
};
data[offset..offset + bytes.len()].copy_from_slice(bytes);
}
pub(crate) unsafe fn as_slice(&self) -> &[u8] {
let (buf, size) = self.as_raw_parts();
std::slice::from_raw_parts(buf, size)
}
#[cfg(feature = "luau")]
unsafe fn as_raw_parts(&self) -> (*mut u8, usize) {
let lua = self.0.lua.lock();
let mut size = 0usize;
let buf = ffi::lua_tobuffer(lua.ref_thread(), self.0.index, &mut size);
mlua_assert!(!buf.is_null(), "invalid Luau buffer");
(buf as *mut u8, size)
}
#[cfg(not(feature = "luau"))]
unsafe fn as_raw_parts(&self) -> (*mut u8, usize) {
unreachable!()
}
}
#[cfg(feature = "serialize")]
impl Serialize for Buffer {
fn serialize<S: Serializer>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error> {
serializer.serialize_bytes(unsafe { self.as_slice() })
}
}
#[cfg(feature = "luau")]
impl crate::types::LuaType for Buffer {
const TYPE_ID: std::os::raw::c_int = ffi::LUA_TBUFFER;
}
+3 -3
View File
@@ -9,7 +9,7 @@ use crate::error::{Error, Result};
use crate::function::Function;
use crate::state::{Lua, WeakLua};
use crate::table::Table;
use crate::value::{FromLuaMulti, IntoLuaMulti};
use crate::traits::{FromLuaMulti, IntoLuaMulti};
/// Trait for types [loadable by Lua] and convertible to a [`Chunk`]
///
@@ -481,7 +481,7 @@ impl<'a> Chunk<'a> {
if let Ok(ref source) = self.source {
if self.detect_mode() == ChunkMode::Text {
let lua = self.lua.lock();
if let Some(cache) = lua.app_data_ref::<ChunksCache>() {
if let Some(cache) = lua.app_data_ref_unguarded::<ChunksCache>() {
if let Some(data) = cache.0.get(source.as_ref()) {
self.source = Ok(Cow::Owned(data.clone()));
self.mode = Some(ChunkMode::Binary);
@@ -498,7 +498,7 @@ impl<'a> Chunk<'a> {
if let Ok(ref binary_source) = self.source {
if self.detect_mode() == ChunkMode::Binary {
let lua = self.lua.lock();
if let Some(mut cache) = lua.app_data_mut::<ChunksCache>() {
if let Some(mut cache) = lua.app_data_mut_unguarded::<ChunksCache>() {
cache.0.insert(text_source, binary_source.as_ref().to_vec());
} else {
let mut cache = ChunksCache(HashMap::new());
+135 -39
View File
@@ -1,12 +1,13 @@
use std::borrow::Cow;
use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet};
use std::ffi::{CStr, CString};
use std::ffi::{CStr, CString, OsStr, OsString};
use std::hash::{BuildHasher, Hash};
use std::os::raw::c_int;
use std::path::{Path, PathBuf};
use std::string::String as StdString;
use std::{slice, str};
use bstr::{BStr, BString};
use bstr::{BStr, BString, ByteSlice, ByteVec};
use num_traits::cast;
use crate::error::{Error, Result};
@@ -15,9 +16,10 @@ use crate::state::{Lua, RawLua};
use crate::string::String;
use crate::table::Table;
use crate::thread::Thread;
use crate::traits::{FromLua, IntoLua, ShortTypeName as _};
use crate::types::{LightUserData, MaybeSend, RegistryKey};
use crate::userdata::{AnyUserData, UserData};
use crate::value::{FromLua, IntoLua, Nil, Value};
use crate::value::{Nil, Value};
impl IntoLua for Value {
#[inline]
@@ -72,7 +74,7 @@ impl FromLua for String {
lua.coerce_string(value)?
.ok_or_else(|| Error::FromLuaConversionError {
from: ty,
to: "string",
to: "string".to_string(),
message: Some("expected string or number".to_string()),
})
}
@@ -116,7 +118,7 @@ impl FromLua for Table {
Value::Table(table) => Ok(table),
_ => Err(Error::FromLuaConversionError {
from: value.type_name(),
to: "table",
to: "table".to_string(),
message: None,
}),
}
@@ -150,7 +152,7 @@ impl FromLua for Function {
Value::Function(table) => Ok(table),
_ => Err(Error::FromLuaConversionError {
from: value.type_name(),
to: "function",
to: "function".to_string(),
message: None,
}),
}
@@ -184,7 +186,7 @@ impl FromLua for Thread {
Value::Thread(t) => Ok(t),
_ => Err(Error::FromLuaConversionError {
from: value.type_name(),
to: "thread",
to: "thread".to_string(),
message: None,
}),
}
@@ -218,7 +220,7 @@ impl FromLua for AnyUserData {
Value::UserData(ud) => Ok(ud),
_ => Err(Error::FromLuaConversionError {
from: value.type_name(),
to: "userdata",
to: "userdata".to_string(),
message: None,
}),
}
@@ -253,6 +255,14 @@ impl FromLua for Error {
}
}
#[cfg(feature = "anyhow")]
impl IntoLua for anyhow::Error {
#[inline]
fn into_lua(self, _: &Lua) -> Result<Value> {
Ok(Value::Error(Box::new(Error::from(self))))
}
}
impl IntoLua for RegistryKey {
#[inline]
fn into_lua(self, lua: &Lua) -> Result<Value> {
@@ -336,7 +346,7 @@ impl FromLua for LightUserData {
Value::LightUserData(ud) => Ok(ud),
_ => Err(Error::FromLuaConversionError {
from: value.type_name(),
to: "light userdata",
to: "lightuserdata".to_string(),
message: None,
}),
}
@@ -359,7 +369,44 @@ impl FromLua for crate::types::Vector {
Value::Vector(v) => Ok(v),
_ => Err(Error::FromLuaConversionError {
from: value.type_name(),
to: "vector",
to: "vector".to_string(),
message: None,
}),
}
}
}
#[cfg(feature = "luau")]
impl IntoLua for crate::Buffer {
#[inline]
fn into_lua(self, _: &Lua) -> Result<Value> {
Ok(Value::Buffer(self))
}
}
#[cfg(feature = "luau")]
impl IntoLua for &crate::Buffer {
#[inline]
fn into_lua(self, _: &Lua) -> Result<Value> {
Ok(Value::Buffer(self.clone()))
}
#[inline]
unsafe fn push_into_stack(self, lua: &RawLua) -> Result<()> {
lua.push_ref(&self.0);
Ok(())
}
}
#[cfg(feature = "luau")]
impl FromLua for crate::Buffer {
#[inline]
fn from_lua(value: Value, _: &Lua) -> Result<Self> {
match value {
Value::Buffer(buf) => Ok(buf),
_ => Err(Error::FromLuaConversionError {
from: value.type_name(),
to: "buffer".to_string(),
message: None,
}),
}
@@ -386,7 +433,7 @@ impl FromLua for StdString {
.coerce_string(value)?
.ok_or_else(|| Error::FromLuaConversionError {
from: ty,
to: "String",
to: Self::type_name(),
message: Some("expected string or number".to_string()),
})?
.to_str()?
@@ -405,7 +452,7 @@ impl FromLua for StdString {
.map(|s| s.to_owned())
.map_err(|e| Error::FromLuaConversionError {
from: "string",
to: "String",
to: Self::type_name(),
message: Some(e.to_string()),
});
}
@@ -448,7 +495,7 @@ impl FromLua for Box<str> {
.coerce_string(value)?
.ok_or_else(|| Error::FromLuaConversionError {
from: ty,
to: "Box<str>",
to: Self::type_name(),
message: Some("expected string or number".to_string()),
})?
.to_str()?
@@ -472,7 +519,7 @@ impl FromLua for CString {
.coerce_string(value)?
.ok_or_else(|| Error::FromLuaConversionError {
from: ty,
to: "CString",
to: Self::type_name(),
message: Some("expected string or number".to_string()),
})?;
@@ -480,7 +527,7 @@ impl FromLua for CString {
Ok(s) => Ok(s.into()),
Err(_) => Err(Error::FromLuaConversionError {
from: ty,
to: "CString",
to: Self::type_name(),
message: Some("invalid C-style string".to_string()),
}),
}
@@ -514,18 +561,12 @@ impl FromLua for BString {
match value {
Value::String(s) => Ok((*s.as_bytes()).into()),
#[cfg(feature = "luau")]
Value::UserData(ud) if ud.1 == crate::types::SubtypeId::Buffer => unsafe {
let lua = ud.0.lua.lock();
let mut size = 0usize;
let buf = ffi::lua_tobuffer(lua.ref_thread(), ud.0.index, &mut size);
mlua_assert!(!buf.is_null(), "invalid Luau buffer");
Ok(slice::from_raw_parts(buf as *const u8, size).into())
},
Value::Buffer(buf) => unsafe { Ok(buf.as_slice().into()) },
_ => Ok((*lua
.coerce_string(value)?
.ok_or_else(|| Error::FromLuaConversionError {
from: ty,
to: "BString",
to: Self::type_name(),
message: Some("expected string or number".to_string()),
})?
.as_bytes())
@@ -563,6 +604,61 @@ impl IntoLua for &BStr {
}
}
impl IntoLua for OsString {
#[inline]
fn into_lua(self, lua: &Lua) -> Result<Value> {
self.as_os_str().into_lua(lua)
}
}
impl FromLua for OsString {
#[inline]
fn from_lua(value: Value, lua: &Lua) -> Result<Self> {
let ty = value.type_name();
let bs = BString::from_lua(value, lua)?;
Vec::from(bs)
.into_os_string()
.map_err(|err| Error::FromLuaConversionError {
from: ty,
to: "OsString".into(),
message: Some(err.to_string()),
})
}
}
impl IntoLua for &OsStr {
#[inline]
fn into_lua(self, lua: &Lua) -> Result<Value> {
let s = <[u8]>::from_os_str(self).ok_or_else(|| Error::ToLuaConversionError {
from: "OsStr".into(),
to: "string",
message: Some("invalid utf-8 encoding".into()),
})?;
Ok(Value::String(lua.create_string(s)?))
}
}
impl IntoLua for PathBuf {
#[inline]
fn into_lua(self, lua: &Lua) -> Result<Value> {
self.as_os_str().into_lua(lua)
}
}
impl FromLua for PathBuf {
#[inline]
fn from_lua(value: Value, lua: &Lua) -> Result<Self> {
OsString::from_lua(value, lua).map(PathBuf::from)
}
}
impl IntoLua for &Path {
#[inline]
fn into_lua(self, lua: &Lua) -> Result<Value> {
self.as_os_str().into_lua(lua)
}
}
#[inline]
unsafe fn push_bytes_into_stack<T>(this: T, lua: &RawLua) -> Result<()>
where
@@ -588,7 +684,7 @@ macro_rules! lua_convert_int {
.or_else(|| cast(self).map(Value::Number))
// This is impossible error because conversion to Number never fails
.ok_or_else(|| Error::ToLuaConversionError {
from: stringify!($x),
from: stringify!($x).to_string(),
to: "number",
message: Some("out of range".to_owned()),
})
@@ -619,7 +715,7 @@ macro_rules! lua_convert_int {
lua.coerce_number(value)?
.ok_or_else(|| Error::FromLuaConversionError {
from: ty,
to: stringify!($x),
to: stringify!($x).to_string(),
message: Some(
"expected number or string coercible to number".to_string(),
),
@@ -630,7 +726,7 @@ macro_rules! lua_convert_int {
})
.ok_or_else(|| Error::FromLuaConversionError {
from: ty,
to: stringify!($x),
to: stringify!($x).to_string(),
message: Some("out of range".to_owned()),
})
}
@@ -644,7 +740,7 @@ macro_rules! lua_convert_int {
if ok != 0 {
return cast(i).ok_or_else(|| Error::FromLuaConversionError {
from: "integer",
to: stringify!($x),
to: stringify!($x).to_string(),
message: Some("out of range".to_owned()),
});
}
@@ -676,7 +772,7 @@ macro_rules! lua_convert_float {
fn into_lua(self, _: &Lua) -> Result<Value> {
cast(self)
.ok_or_else(|| Error::ToLuaConversionError {
from: stringify!($x),
from: stringify!($x).to_string(),
to: "number",
message: Some("out of range".to_string()),
})
@@ -691,13 +787,13 @@ macro_rules! lua_convert_float {
lua.coerce_number(value)?
.ok_or_else(|| Error::FromLuaConversionError {
from: ty,
to: stringify!($x),
to: stringify!($x).to_string(),
message: Some("expected number or string coercible to number".to_string()),
})
.and_then(|n| {
cast(n).ok_or_else(|| Error::FromLuaConversionError {
from: ty,
to: stringify!($x),
to: stringify!($x).to_string(),
message: Some("number out of range".to_string()),
})
})
@@ -712,7 +808,7 @@ macro_rules! lua_convert_float {
if ok != 0 {
return cast(i).ok_or_else(|| Error::FromLuaConversionError {
from: "number",
to: stringify!($x),
to: stringify!($x).to_string(),
message: Some("out of range".to_owned()),
});
}
@@ -771,13 +867,13 @@ where
vec.try_into()
.map_err(|vec: Vec<T>| Error::FromLuaConversionError {
from: "table",
to: "Array",
message: Some(format!("expected table of length {}, got {}", N, vec.len())),
to: Self::type_name(),
message: Some(format!("expected table of length {N}, got {}", vec.len())),
})
}
_ => Err(Error::FromLuaConversionError {
from: value.type_name(),
to: "Array",
to: Self::type_name(),
message: Some("expected table".to_string()),
}),
}
@@ -812,7 +908,7 @@ impl<T: FromLua> FromLua for Vec<T> {
Value::Table(table) => table.sequence_values().collect(),
_ => Err(Error::FromLuaConversionError {
from: value.type_name(),
to: "Vec",
to: Self::type_name(),
message: Some("expected table".to_string()),
}),
}
@@ -834,7 +930,7 @@ impl<K: Eq + Hash + FromLua, V: FromLua, S: BuildHasher + Default> FromLua for H
} else {
Err(Error::FromLuaConversionError {
from: value.type_name(),
to: "HashMap",
to: Self::type_name(),
message: Some("expected table".to_string()),
})
}
@@ -856,7 +952,7 @@ impl<K: Ord + FromLua, V: FromLua> FromLua for BTreeMap<K, V> {
} else {
Err(Error::FromLuaConversionError {
from: value.type_name(),
to: "BTreeMap",
to: Self::type_name(),
message: Some("expected table".to_string()),
})
}
@@ -880,7 +976,7 @@ impl<T: Eq + Hash + FromLua, S: BuildHasher + Default> FromLua for HashSet<T, S>
Value::Table(table) => table.pairs::<T, Value>().map(|res| res.map(|(k, _)| k)).collect(),
_ => Err(Error::FromLuaConversionError {
from: value.type_name(),
to: "HashSet",
to: Self::type_name(),
message: Some("expected table".to_string()),
}),
}
@@ -904,7 +1000,7 @@ impl<T: Ord + FromLua> FromLua for BTreeSet<T> {
Value::Table(table) => table.pairs::<T, Value>().map(|res| res.map(|(k, _)| k)).collect(),
_ => Err(Error::FromLuaConversionError {
from: value.type_name(),
to: "BTreeSet",
to: Self::type_name(),
message: Some("expected table".to_string()),
}),
}
+93 -34
View File
@@ -83,7 +83,7 @@ pub enum Error {
/// A Rust value could not be converted to a Lua value.
ToLuaConversionError {
/// Name of the Rust type that could not be converted.
from: &'static str,
from: String,
/// Name of the Lua type that could not be created.
to: &'static str,
/// A message indicating why the conversion failed in more detail.
@@ -94,7 +94,7 @@ pub enum Error {
/// Name of the Lua type that could not be converted.
from: &'static str,
/// Name of the Rust type that could not be created.
to: &'static str,
to: String,
/// A string containing more detailed error information.
message: Option<StdString>,
},
@@ -205,17 +205,17 @@ pub type Result<T> = StdResult<T, Error>;
#[cfg(not(tarpaulin_include))]
impl fmt::Display for Error {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
match *self {
Error::SyntaxError { ref message, .. } => write!(fmt, "syntax error: {message}"),
Error::RuntimeError(ref msg) => write!(fmt, "runtime error: {msg}"),
Error::MemoryError(ref msg) => {
match self {
Error::SyntaxError { message, .. } => write!(fmt, "syntax error: {message}"),
Error::RuntimeError(msg) => write!(fmt, "runtime error: {msg}"),
Error::MemoryError(msg) => {
write!(fmt, "memory error: {msg}")
}
#[cfg(any(feature = "lua53", feature = "lua52"))]
Error::GarbageCollectorError(ref msg) => {
Error::GarbageCollectorError(msg) => {
write!(fmt, "garbage collector error: {msg}")
}
Error::SafetyError(ref msg) => {
Error::SafetyError(msg) => {
write!(fmt, "safety error: {msg}")
},
Error::MemoryLimitNotAvailable => {
@@ -234,7 +234,7 @@ impl fmt::Display for Error {
fmt,
"too many arguments to Function::bind"
),
Error::BadArgument { ref to, pos, ref name, ref cause } => {
Error::BadArgument { to, pos, name, cause } => {
if let Some(name) = name {
write!(fmt, "bad argument `{name}`")?;
} else {
@@ -245,18 +245,18 @@ impl fmt::Display for Error {
}
write!(fmt, ": {cause}")
},
Error::ToLuaConversionError { from, to, ref message } => {
Error::ToLuaConversionError { from, to, message } => {
write!(fmt, "error converting {from} to Lua {to}")?;
match *message {
match message {
None => Ok(()),
Some(ref message) => write!(fmt, " ({message})"),
Some(message) => write!(fmt, " ({message})"),
}
}
Error::FromLuaConversionError { from, to, ref message } => {
Error::FromLuaConversionError { from, to, message } => {
write!(fmt, "error converting Lua {from} to {to}")?;
match *message {
match message {
None => Ok(()),
Some(ref message) => write!(fmt, " ({message})"),
Some(message) => write!(fmt, " ({message})"),
}
}
Error::CoroutineUnresumable => write!(fmt, "coroutine is non-resumable"),
@@ -264,21 +264,21 @@ impl fmt::Display for Error {
Error::UserDataDestructed => write!(fmt, "userdata has been destructed"),
Error::UserDataBorrowError => write!(fmt, "error borrowing userdata"),
Error::UserDataBorrowMutError => write!(fmt, "error mutably borrowing userdata"),
Error::MetaMethodRestricted(ref method) => write!(fmt, "metamethod {method} is restricted"),
Error::MetaMethodTypeError { ref method, type_name, ref message } => {
Error::MetaMethodRestricted(method) => write!(fmt, "metamethod {method} is restricted"),
Error::MetaMethodTypeError { method, type_name, message } => {
write!(fmt, "metamethod {method} has unsupported type {type_name}")?;
match *message {
match message {
None => Ok(()),
Some(ref message) => write!(fmt, " ({message})"),
Some(message) => write!(fmt, " ({message})"),
}
}
Error::MismatchedRegistryKey => {
write!(fmt, "RegistryKey used from different Lua state")
}
Error::CallbackError { ref cause, ref traceback } => {
Error::CallbackError { cause, traceback } => {
// Trace errors down to the root
let (mut cause, mut full_traceback) = (cause, None);
while let Error::CallbackError { cause: ref cause2, traceback: ref traceback2 } = **cause {
while let Error::CallbackError { cause: cause2, traceback: traceback2 } = &**cause {
cause = cause2;
full_traceback = Some(traceback2);
}
@@ -302,15 +302,15 @@ impl fmt::Display for Error {
write!(fmt, "previously resumed panic returned again")
}
#[cfg(feature = "serialize")]
Error::SerializeError(ref err) => {
Error::SerializeError(err) => {
write!(fmt, "serialize error: {err}")
},
#[cfg(feature = "serialize")]
Error::DeserializeError(ref err) => {
Error::DeserializeError(err) => {
write!(fmt, "deserialize error: {err}")
},
Error::ExternalError(ref err) => write!(fmt, "{err}"),
Error::WithContext { ref context, ref cause } => {
Error::ExternalError(err) => write!(fmt, "{err}"),
Error::WithContext { context, cause } => {
writeln!(fmt, "{context}")?;
write!(fmt, "{cause}")
}
@@ -320,15 +320,15 @@ impl fmt::Display for Error {
impl StdError for Error {
fn source(&self) -> Option<&(dyn StdError + 'static)> {
match *self {
match self {
// An error type with a source error should either return that error via source or
// include that source's error message in its own Display output, but never both.
// https://blog.rust-lang.org/inside-rust/2021/07/01/What-the-error-handling-project-group-is-working-towards.html
// Given that we include source to fmt::Display implementation for `CallbackError`, this call
// returns nothing.
Error::CallbackError { .. } => None,
Error::ExternalError(ref err) => err.source(),
Error::WithContext { ref cause, .. } => match cause.as_ref() {
Error::ExternalError(err) => err.source(),
Error::WithContext { cause, .. } => match cause.as_ref() {
Error::ExternalError(err) => err.source(),
_ => None,
},
@@ -365,6 +365,14 @@ impl Error {
}
}
/// An iterator over the chain of nested errors wrapped by this Error.
pub fn chain(&self) -> impl Iterator<Item = &(dyn StdError + 'static)> {
Chain {
root: self,
current: None,
}
}
pub(crate) fn bad_self_argument(to: &str, cause: Error) -> Self {
Error::BadArgument {
to: Some(to.to_string()),
@@ -374,15 +382,15 @@ impl Error {
}
}
pub(crate) fn from_lua_conversion<'a>(
pub(crate) fn from_lua_conversion(
from: &'static str,
to: &'static str,
message: impl Into<Option<&'a str>>,
to: impl ToString,
message: impl Into<Option<String>>,
) -> Self {
Error::FromLuaConversionError {
from,
to,
message: message.into().map(|s| s.into()),
to: to.to_string(),
message: message.into(),
}
}
}
@@ -446,7 +454,7 @@ impl ErrorContext for Error {
}
}
impl<T> ErrorContext for StdResult<T, Error> {
impl<T> ErrorContext for Result<T> {
fn context<C: fmt::Display>(self, context: C) -> Self {
self.map_err(|err| err.context(context))
}
@@ -487,3 +495,54 @@ impl serde::de::Error for Error {
Self::DeserializeError(msg.to_string())
}
}
#[cfg(feature = "anyhow")]
impl From<anyhow::Error> for Error {
fn from(err: anyhow::Error) -> Self {
match err.downcast::<Self>() {
Ok(err) => err,
Err(err) => Error::external(err),
}
}
}
struct Chain<'a> {
root: &'a Error,
current: Option<&'a (dyn StdError + 'static)>,
}
impl<'a> Iterator for Chain<'a> {
type Item = &'a (dyn StdError + 'static);
fn next(&mut self) -> Option<Self::Item> {
loop {
let error: Option<&dyn StdError> = match self.current {
None => {
self.current = Some(self.root);
self.current
}
Some(current) => match current.downcast_ref::<Error>()? {
Error::BadArgument { cause, .. }
| Error::CallbackError { cause, .. }
| Error::WithContext { cause, .. } => {
self.current = Some(&**cause);
self.current
}
Error::ExternalError(err) => {
self.current = Some(&**err);
self.current
}
_ => None,
},
};
// Skip `ExternalError` as it only wraps the underlying error
// without meaningful context
if let Some(Error::ExternalError(_)) = error?.downcast_ref::<Error>() {
continue;
}
return self.current;
}
}
}
+77 -21
View File
@@ -5,20 +5,22 @@ use std::{mem, ptr, slice};
use crate::error::{Error, Result};
use crate::state::Lua;
use crate::table::Table;
use crate::types::{Callback, MaybeSend, ValueRef};
use crate::traits::{FromLuaMulti, IntoLua, IntoLuaMulti, LuaNativeFn, LuaNativeFnMut};
use crate::types::{Callback, LuaType, MaybeSend, ValueRef};
use crate::util::{
assert_stack, check_stack, linenumber_to_usize, pop_error, ptr_to_lossy_str, ptr_to_str, StackGuard,
};
use crate::value::{FromLuaMulti, IntoLua, IntoLuaMulti, Value};
use crate::value::Value;
#[cfg(feature = "async")]
use {
crate::traits::LuaNativeAsyncFn,
crate::types::AsyncCallback,
std::future::{self, Future},
};
/// Handle to an internal Lua function.
#[derive(Clone, Debug)]
#[derive(Clone, Debug, PartialEq)]
pub struct Function(pub(crate) ValueRef);
/// Contains information about a function.
@@ -507,12 +509,6 @@ impl Function {
}
}
impl PartialEq for Function {
fn eq(&self, other: &Self) -> bool {
self.0 == other.0
}
}
pub(crate) struct WrappedFunction(pub(crate) Callback);
#[cfg(feature = "async")]
@@ -522,31 +518,65 @@ impl Function {
/// Wraps a Rust function or closure, returning an opaque type that implements [`IntoLua`]
/// trait.
#[inline]
pub fn wrap<A, R, F>(func: F) -> impl IntoLua
pub fn wrap<F, A, R>(func: F) -> impl IntoLua
where
F: LuaNativeFn<A, Output = Result<R>> + MaybeSend + 'static,
A: FromLuaMulti,
R: IntoLuaMulti,
F: Fn(&Lua, A) -> Result<R> + MaybeSend + 'static,
{
WrappedFunction(Box::new(move |lua, nargs| unsafe {
let args = A::from_stack_args(nargs, 1, None, lua)?;
func(lua.lua(), args)?.push_into_stack_multi(lua)
func.call(args)?.push_into_stack_multi(lua)
}))
}
/// Wraps a Rust mutable closure, returning an opaque type that implements [`IntoLua`] trait.
#[inline]
pub fn wrap_mut<A, R, F>(func: F) -> impl IntoLua
pub fn wrap_mut<F, A, R>(func: F) -> impl IntoLua
where
F: LuaNativeFnMut<A, Output = Result<R>> + MaybeSend + 'static,
A: FromLuaMulti,
R: IntoLuaMulti,
F: FnMut(&Lua, A) -> Result<R> + MaybeSend + 'static,
{
let func = RefCell::new(func);
WrappedFunction(Box::new(move |lua, nargs| unsafe {
let mut func = func.try_borrow_mut().map_err(|_| Error::RecursiveMutCallback)?;
let args = A::from_stack_args(nargs, 1, None, lua)?;
func(lua.lua(), args)?.push_into_stack_multi(lua)
func.call(args)?.push_into_stack_multi(lua)
}))
}
/// Wraps a Rust function or closure, returning an opaque type that implements [`IntoLua`]
/// trait.
///
/// This function is similar to [`Function::wrap`] but any returned `Result` will be converted
/// to a `ok, err` tuple without throwing an exception.
#[inline]
pub fn wrap_raw<F, A>(func: F) -> impl IntoLua
where
F: LuaNativeFn<A> + MaybeSend + 'static,
A: FromLuaMulti,
{
WrappedFunction(Box::new(move |lua, nargs| unsafe {
let args = A::from_stack_args(nargs, 1, None, lua)?;
func.call(args).push_into_stack_multi(lua)
}))
}
/// Wraps a Rust mutable closure, returning an opaque type that implements [`IntoLua`] trait.
///
/// This function is similar to [`Function::wrap_mut`] but any returned `Result` will be
/// converted to a `ok, err` tuple without throwing an exception.
#[inline]
pub fn wrap_raw_mut<F, A>(func: F) -> impl IntoLua
where
F: LuaNativeFnMut<A> + MaybeSend + 'static,
A: FromLuaMulti,
{
let func = RefCell::new(func);
WrappedFunction(Box::new(move |lua, nargs| unsafe {
let mut func = func.try_borrow_mut().map_err(|_| Error::RecursiveMutCallback)?;
let args = A::from_stack_args(nargs, 1, None, lua)?;
func.call(args).push_into_stack_multi(lua)
}))
}
@@ -554,23 +584,45 @@ impl Function {
/// trait.
#[cfg(feature = "async")]
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
pub fn wrap_async<A, R, F, FR>(func: F) -> impl IntoLua
pub fn wrap_async<F, A, R>(func: F) -> impl IntoLua
where
F: LuaNativeAsyncFn<A, Output = Result<R>> + MaybeSend + 'static,
A: FromLuaMulti,
R: IntoLuaMulti,
F: Fn(Lua, A) -> FR + MaybeSend + 'static,
FR: Future<Output = Result<R>> + MaybeSend + 'static,
{
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 lua = rawlua.lua().clone();
let fut = func(lua.clone(), args);
let lua = rawlua.lua();
let fut = func.call(args);
Box::pin(async move { fut.await?.push_into_stack_multi(lua.raw_lua()) })
}))
}
/// Wraps a Rust async function or closure, returning an opaque type that implements [`IntoLua`]
/// trait.
///
/// This function is similar to [`Function::wrap_async`] but any returned `Result` will be
/// converted to a `ok, err` tuple without throwing an exception.
#[cfg(feature = "async")]
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
pub fn wrap_raw_async<F, A>(func: F) -> impl IntoLua
where
F: LuaNativeAsyncFn<A> + MaybeSend + 'static,
A: FromLuaMulti,
{
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 lua = rawlua.lua();
let fut = func.call(args);
Box::pin(async move { fut.await.push_into_stack_multi(lua.raw_lua()) })
}))
}
}
impl IntoLua for WrappedFunction {
@@ -588,6 +640,10 @@ impl IntoLua for WrappedAsyncFunction {
}
}
impl LuaType for Function {
const TYPE_ID: c_int = ffi::LUA_TFUNCTION;
}
#[cfg(test)]
mod assertions {
use super::*;
+13 -12
View File
@@ -78,6 +78,7 @@
#[macro_use]
mod macros;
mod buffer;
mod chunk;
mod conversion;
mod error;
@@ -87,7 +88,7 @@ mod hook;
mod luau;
mod memory;
mod multi;
// mod scope;
mod scope;
mod state;
mod stdlib;
mod string;
@@ -108,34 +109,34 @@ pub use crate::chunk::{AsChunk, Chunk, ChunkMode};
pub use crate::error::{Error, ErrorContext, ExternalError, ExternalResult, Result};
pub use crate::function::{Function, FunctionInfo};
pub use crate::hook::{Debug, DebugEvent, DebugNames, DebugSource, DebugStack};
pub use crate::multi::Variadic;
pub use crate::multi::{MultiValue, Variadic};
pub use crate::scope::Scope;
pub use crate::state::{GCMode, Lua, LuaOptions};
// pub use crate::scope::Scope;
pub use crate::stdlib::StdLib;
pub use crate::string::{BorrowedBytes, BorrowedStr, String};
pub use crate::table::{Table, TablePairs, TableSequence};
pub use crate::thread::{Thread, ThreadStatus};
pub use crate::traits::ObjectLike;
pub use crate::types::{AppDataRef, AppDataRefMut, Integer, LightUserData, MaybeSend, Number, RegistryKey};
pub use crate::traits::{
FromLua, FromLuaMulti, IntoLua, IntoLuaMulti, LuaNativeFn, LuaNativeFnMut, ObjectLike,
};
pub use crate::types::{
AppDataRef, AppDataRefMut, Either, Integer, LightUserData, MaybeSend, Number, RegistryKey, VmState,
};
pub use crate::userdata::{
AnyUserData, MetaMethod, UserData, UserDataFields, UserDataMetatable, UserDataMethods, UserDataRef,
UserDataRefMut, UserDataRegistry,
};
pub use crate::value::{FromLua, FromLuaMulti, IntoLua, IntoLuaMulti, MultiValue, Nil, Value};
pub use crate::value::{Nil, Value};
#[cfg(not(feature = "luau"))]
pub use crate::hook::HookTriggers;
#[cfg(any(feature = "luau", doc))]
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
pub use crate::{
chunk::Compiler,
function::CoverageInfo,
types::{Vector, VmState},
};
pub use crate::{buffer::Buffer, chunk::Compiler, function::CoverageInfo, types::Vector};
#[cfg(feature = "async")]
pub use crate::thread::AsyncThread;
pub use crate::{thread::AsyncThread, traits::LuaNativeAsyncFn};
#[cfg(feature = "serialize")]
#[doc(inline)]
+2 -1
View File
@@ -9,7 +9,8 @@ use crate::chunk::ChunkMode;
use crate::error::Result;
use crate::state::Lua;
use crate::table::Table;
use crate::value::{IntoLua, Value};
use crate::traits::IntoLua;
use crate::value::Value;
#[cfg(unix)]
use {libloading::Library, rustc_hash::FxHashMap};
+88 -1
View File
@@ -1,12 +1,15 @@
use std::collections::{vec_deque, VecDeque};
use std::iter::FromIterator;
use std::mem;
use std::ops::{Deref, DerefMut};
use std::os::raw::c_int;
use std::result::Result as StdResult;
use crate::error::Result;
use crate::state::{Lua, RawLua};
use crate::traits::{FromLua, FromLuaMulti, IntoLua, IntoLuaMulti};
use crate::util::check_stack;
use crate::value::{FromLua, FromLuaMulti, IntoLua, IntoLuaMulti, MultiValue, Nil};
use crate::value::{Nil, Value};
/// Result is convertible to `MultiValue` following the common Lua idiom of returning the result
/// on success, or in the case of an error, returning `nil` and an error message.
@@ -89,6 +92,80 @@ impl<T: FromLua> FromLuaMulti for T {
}
}
/// Multiple Lua values used for both argument passing and also for multiple return values.
#[derive(Default, Debug, Clone)]
pub struct MultiValue(VecDeque<Value>);
impl Deref for MultiValue {
type Target = VecDeque<Value>;
#[inline]
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl DerefMut for MultiValue {
#[inline]
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
impl MultiValue {
/// Creates an empty `MultiValue` containing no values.
#[inline]
pub const fn new() -> MultiValue {
MultiValue(VecDeque::new())
}
/// Creates an empty `MultiValue` container with space for at least `capacity` elements.
pub fn with_capacity(capacity: usize) -> MultiValue {
MultiValue(VecDeque::with_capacity(capacity))
}
#[inline]
pub(crate) fn from_lua_iter<T: IntoLua>(lua: &Lua, iter: impl IntoIterator<Item = T>) -> Result<Self> {
let iter = iter.into_iter();
let mut multi_value = MultiValue::with_capacity(iter.size_hint().0);
for value in iter {
multi_value.push_back(value.into_lua(lua)?);
}
Ok(multi_value)
}
}
impl FromIterator<Value> for MultiValue {
#[inline]
fn from_iter<I: IntoIterator<Item = Value>>(iter: I) -> Self {
let mut multi_value = MultiValue::new();
multi_value.extend(iter);
multi_value
}
}
impl IntoIterator for MultiValue {
type Item = Value;
type IntoIter = vec_deque::IntoIter<Value>;
#[inline]
fn into_iter(mut self) -> Self::IntoIter {
let deque = mem::take(&mut self.0);
mem::forget(self);
deque.into_iter()
}
}
impl<'a> IntoIterator for &'a MultiValue {
type Item = &'a Value;
type IntoIter = vec_deque::Iter<'a, Value>;
#[inline]
fn into_iter(self) -> Self::IntoIter {
self.0.iter()
}
}
impl IntoLuaMulti for MultiValue {
#[inline]
fn into_lua_multi(self, _: &Lua) -> Result<MultiValue> {
@@ -342,3 +419,13 @@ impl_tuple!(A B C D E F G H I J K L M);
impl_tuple!(A B C D E F G H I J K L M N);
impl_tuple!(A B C D E F G H I J K L M N O);
impl_tuple!(A B C D E F G H I J K L M N O P);
#[cfg(test)]
mod assertions {
use super::*;
#[cfg(not(feature = "send"))]
static_assertions::assert_not_impl_any!(MultiValue: Send);
#[cfg(feature = "send")]
static_assertions::assert_impl_all!(MultiValue: Send, Sync);
}
+14 -13
View File
@@ -2,17 +2,18 @@
#[doc(no_inline)]
pub use crate::{
AnyUserData as LuaAnyUserData, Chunk as LuaChunk, Error as LuaError, ErrorContext as LuaErrorContext,
ExternalError as LuaExternalError, ExternalResult as LuaExternalResult, FromLua, FromLuaMulti,
Function as LuaFunction, FunctionInfo as LuaFunctionInfo, GCMode as LuaGCMode, Integer as LuaInteger,
IntoLua, IntoLuaMulti, LightUserData as LuaLightUserData, Lua, LuaOptions, MetaMethod as LuaMetaMethod,
MultiValue as LuaMultiValue, Nil as LuaNil, Number as LuaNumber, ObjectLike as LuaObjectLike,
RegistryKey as LuaRegistryKey, Result as LuaResult, StdLib as LuaStdLib, String as LuaString,
Table as LuaTable, TablePairs as LuaTablePairs, TableSequence as LuaTableSequence, Thread as LuaThread,
ThreadStatus as LuaThreadStatus, UserData as LuaUserData, UserDataFields as LuaUserDataFields,
UserDataMetatable as LuaUserDataMetatable, UserDataMethods as LuaUserDataMethods,
UserDataRef as LuaUserDataRef, UserDataRefMut as LuaUserDataRefMut,
UserDataRegistry as LuaUserDataRegistry, Value as LuaValue,
AnyUserData as LuaAnyUserData, Chunk as LuaChunk, Either as LuaEither, Error as LuaError,
ErrorContext as LuaErrorContext, ExternalError as LuaExternalError, ExternalResult as LuaExternalResult,
FromLua, FromLuaMulti, Function as LuaFunction, FunctionInfo as LuaFunctionInfo, GCMode as LuaGCMode,
Integer as LuaInteger, IntoLua, IntoLuaMulti, LightUserData as LuaLightUserData, Lua, LuaNativeFn,
LuaNativeFnMut, LuaOptions, MetaMethod as LuaMetaMethod, MultiValue as LuaMultiValue, Nil as LuaNil,
Number as LuaNumber, ObjectLike as LuaObjectLike, RegistryKey as LuaRegistryKey, Result as LuaResult,
StdLib as LuaStdLib, String as LuaString, Table as LuaTable, TablePairs as LuaTablePairs,
TableSequence as LuaTableSequence, Thread as LuaThread, ThreadStatus as LuaThreadStatus,
UserData as LuaUserData, UserDataFields as LuaUserDataFields, UserDataMetatable as LuaUserDataMetatable,
UserDataMethods as LuaUserDataMethods, UserDataRef as LuaUserDataRef,
UserDataRefMut as LuaUserDataRefMut, UserDataRegistry as LuaUserDataRegistry, Value as LuaValue,
VmState as LuaVmState,
};
#[cfg(not(feature = "luau"))]
@@ -21,11 +22,11 @@ pub use crate::HookTriggers as LuaHookTriggers;
#[cfg(feature = "luau")]
#[doc(no_inline)]
pub use crate::{CoverageInfo as LuaCoverageInfo, Vector as LuaVector, VmState as LuaVmState};
pub use crate::{CoverageInfo as LuaCoverageInfo, Vector as LuaVector};
#[cfg(feature = "async")]
#[doc(no_inline)]
pub use crate::AsyncThread as LuaAsyncThread;
pub use crate::{AsyncThread as LuaAsyncThread, LuaNativeAsyncFn};
#[cfg(feature = "serialize")]
#[doc(no_inline)]
+112 -790
View File
File diff suppressed because it is too large Load Diff
+4 -10
View File
@@ -145,19 +145,13 @@ impl<'de> serde::Deserializer<'de> for Deserializer {
serde_userdata(ud, |value| value.deserialize_any(visitor))
}
#[cfg(feature = "luau")]
Value::UserData(ud) if ud.1 == crate::types::SubtypeId::Buffer => unsafe {
let lua = ud.0.lua.lock();
let mut size = 0usize;
let buf = ffi::lua_tobuffer(lua.ref_thread(), ud.0.index, &mut size);
mlua_assert!(!buf.is_null(), "invalid Luau buffer");
let buf = std::slice::from_raw_parts(buf as *const u8, size);
visitor.visit_bytes(buf)
},
Value::Buffer(buf) => visitor.visit_bytes(unsafe { buf.as_slice() }),
Value::Function(_)
| Value::Thread(_)
| Value::UserData(_)
| Value::LightUserData(_)
| Value::Error(_) => {
| Value::Error(_)
| Value::Other(_) => {
if self.options.deny_unsupported_types {
let msg = format!("unsupported value type `{}`", self.value.type_name());
Err(de::Error::custom(msg))
@@ -463,7 +457,7 @@ impl<'a> MapPairs<'a> {
pub(crate) fn new(t: &'a Table, sort_keys: bool) -> Result<Self> {
if sort_keys {
let mut pairs = t.pairs::<Value, Value>().collect::<Result<Vec<_>>>()?;
pairs.sort_by(|(a, _), (b, _)| b.cmp(a)); // reverse order as we pop values from the end
pairs.sort_by(|(a, _), (b, _)| b.sort_cmp(a)); // reverse order as we pop values from the end
Ok(MapPairs::Vec(pairs))
} else {
Ok(MapPairs::Iter(t.pairs::<Value, Value>()))
+2 -1
View File
@@ -4,7 +4,8 @@ use super::LuaSerdeExt;
use crate::error::{Error, Result};
use crate::state::Lua;
use crate::table::Table;
use crate::value::{IntoLua, Value};
use crate::traits::IntoLua;
use crate::value::Value;
/// A struct for serializing Rust values into Lua values.
#[derive(Debug)]
+128 -60
View File
@@ -2,7 +2,7 @@ use std::any::TypeId;
use std::cell::RefCell;
use std::marker::PhantomData;
use std::ops::Deref;
use std::os::raw::{c_int, c_void};
use std::os::raw::c_int;
use std::panic::Location;
use std::result::Result as StdResult;
use std::{fmt, mem, ptr};
@@ -12,27 +12,34 @@ use crate::error::{Error, Result};
use crate::function::Function;
use crate::hook::Debug;
use crate::memory::MemoryState;
// use crate::scope::Scope;
use crate::multi::MultiValue;
use crate::scope::Scope;
use crate::stdlib::StdLib;
use crate::string::String;
use crate::table::Table;
use crate::thread::Thread;
use crate::traits::{FromLua, FromLuaMulti, IntoLua, IntoLuaMulti};
use crate::types::{
AppDataRef, AppDataRefMut, ArcReentrantMutexGuard, Integer, LightUserData, MaybeSend, Number,
ReentrantMutex, ReentrantMutexGuard, RegistryKey, XRc, XWeak,
AppDataRef, AppDataRefMut, ArcReentrantMutexGuard, Integer, LuaType, MaybeSend, Number, ReentrantMutex,
ReentrantMutexGuard, RegistryKey, VmState, XRc, XWeak,
};
use crate::userdata::{AnyUserData, UserData, UserDataProxy, UserDataRegistry, UserDataVariant};
use crate::util::{assert_stack, check_stack, push_string, push_table, rawset_field, StackGuard};
use crate::value::{FromLua, FromLuaMulti, IntoLua, IntoLuaMulti, MultiValue, Nil, Value};
use crate::userdata::{AnyUserData, UserData, UserDataProxy, UserDataRegistry, UserDataStorage};
use crate::util::{
assert_stack, check_stack, protect_lua_closure, push_string, push_table, rawset_field, StackGuard,
};
use crate::value::{Nil, Value};
#[cfg(not(feature = "luau"))]
use crate::hook::HookTriggers;
#[cfg(any(feature = "luau", doc))]
use crate::{chunk::Compiler, types::VmState};
use crate::{buffer::Buffer, chunk::Compiler};
#[cfg(feature = "async")]
use std::future::{self, Future};
use {
crate::types::LightUserData,
std::future::{self, Future},
};
#[cfg(feature = "serialize")]
use serde::Serialize;
@@ -276,6 +283,29 @@ impl Lua {
}
}
/// Calls provided function passing a raw lua state.
///
/// The arguments will be pushed onto the stack before calling the function.
///
/// This method ensures that the Lua instance is locked while the function is called
/// and restores Lua stack after the function returns.
#[allow(clippy::missing_safety_doc)]
pub unsafe fn exec_raw<R: FromLuaMulti>(
&self,
args: impl IntoLuaMulti,
f: impl FnOnce(*mut ffi::lua_State),
) -> Result<R> {
let lua = self.lock();
let state = lua.state();
let _sg = StackGuard::new(state);
let stack_start = ffi::lua_gettop(state);
let nargs = args.push_into_stack_multi(&lua)?;
check_stack(state, 3)?;
protect_lua_closure::<_, ()>(state, nargs, ffi::LUA_MULTRET, f)?;
let nresults = ffi::lua_gettop(state) - stack_start;
R::from_stack_multi(nresults, &lua)
}
/// FIXME: Deprecated load_from_std_lib
/// Loads the specified subset of the standard libraries into an existing Lua state.
@@ -471,12 +501,12 @@ impl Lua {
/// Shows each line number of code being executed by the Lua interpreter.
///
/// ```
/// # use mlua::{Lua, HookTriggers, Result};
/// # use mlua::{Lua, HookTriggers, Result, VmState};
/// # fn main() -> Result<()> {
/// let lua = Lua::new();
/// lua.set_hook(HookTriggers::EVERY_LINE, |_lua, debug| {
/// println!("line {}", debug.curr_line());
/// Ok(())
/// Ok(VmState::Continue)
/// });
///
/// lua.load(r#"
@@ -493,7 +523,7 @@ impl Lua {
#[cfg_attr(docsrs, doc(cfg(not(feature = "luau"))))]
pub fn set_hook<F>(&self, triggers: HookTriggers, callback: F)
where
F: Fn(&Lua, Debug) -> Result<()> + MaybeSend + 'static,
F: Fn(&Lua, Debug) -> Result<VmState> + MaybeSend + 'static,
{
let lua = self.lock();
unsafe { lua.set_thread_hook(lua.state(), triggers, callback) };
@@ -624,7 +654,7 @@ impl Lua {
F: Fn(&Lua, &str, bool) -> Result<()> + MaybeSend + 'static,
{
use std::ffi::CStr;
use std::os::raw::c_char;
use std::os::raw::{c_char, c_void};
use std::string::String as StdString;
unsafe extern "C-unwind" fn warn_proc(ud: *mut c_void, msg: *const c_char, tocont: c_int) {
@@ -968,22 +998,21 @@ impl Lua {
/// Requires `feature = "luau"`
///
/// [buffer]: https://luau-lang.org/library#buffer-library
#[cfg(feature = "luau")]
pub fn create_buffer(&self, buf: impl AsRef<[u8]>) -> Result<AnyUserData> {
use crate::types::SubtypeId;
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
#[cfg(any(feature = "luau", doc))]
pub fn create_buffer(&self, buf: impl AsRef<[u8]>) -> Result<Buffer> {
let lua = self.lock();
let state = lua.state();
unsafe {
if lua.unlikely_memory_error() {
crate::util::push_buffer(lua.ref_thread(), buf.as_ref(), false)?;
return Ok(AnyUserData(lua.pop_ref_thread(), SubtypeId::Buffer));
return Ok(Buffer(lua.pop_ref_thread()));
}
let _sg = StackGuard::new(state);
check_stack(state, 4)?;
crate::util::push_buffer(state, buf.as_ref(), true)?;
Ok(AnyUserData(lua.pop_ref(), SubtypeId::Buffer))
Ok(Buffer(lua.pop_ref()))
}
}
@@ -1001,11 +1030,10 @@ impl Lua {
}
/// Creates a table and fills it with values from an iterator.
pub fn create_table_from<K, V, I>(&self, iter: I) -> Result<Table>
pub fn create_table_from<K, V>(&self, iter: impl IntoIterator<Item = (K, V)>) -> Result<Table>
where
K: IntoLua,
V: IntoLua,
I: IntoIterator<Item = (K, V)>,
{
let lua = self.lock();
let state = lua.state();
@@ -1032,10 +1060,9 @@ impl Lua {
}
/// Creates a table from an iterator of values, using `1..` as the keys.
pub fn create_sequence_from<T, I>(&self, iter: I) -> Result<Table>
pub fn create_sequence_from<T>(&self, iter: impl IntoIterator<Item = T>) -> Result<Table>
where
T: IntoLua,
I: IntoIterator<Item = T>,
{
unsafe { self.lock().create_sequence_from(iter) }
}
@@ -1201,7 +1228,7 @@ impl Lua {
where
T: UserData + MaybeSend + 'static,
{
unsafe { self.lock().make_userdata(UserDataVariant::new(data)) }
unsafe { self.lock().make_userdata(UserDataStorage::new(data)) }
}
/// Creates a Lua userdata object from a custom serializable userdata type.
@@ -1214,7 +1241,7 @@ impl Lua {
where
T: UserData + Serialize + MaybeSend + 'static,
{
unsafe { self.lock().make_userdata(UserDataVariant::new_ser(data)) }
unsafe { self.lock().make_userdata(UserDataStorage::new_ser(data)) }
}
/// Creates a Lua userdata object from a custom Rust type.
@@ -1229,7 +1256,7 @@ impl Lua {
where
T: MaybeSend + 'static,
{
unsafe { self.lock().make_any_userdata(UserDataVariant::new(data)) }
unsafe { self.lock().make_any_userdata(UserDataStorage::new(data)) }
}
/// Creates a Lua userdata object from a custom serializable Rust type.
@@ -1244,26 +1271,26 @@ impl Lua {
where
T: Serialize + MaybeSend + 'static,
{
unsafe { (self.lock()).make_any_userdata(UserDataVariant::new_ser(data)) }
unsafe { (self.lock()).make_any_userdata(UserDataStorage::new_ser(data)) }
}
/// Registers a custom Rust type in Lua to use in userdata objects.
///
/// 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 = const { UserDataRegistry::new() };
let type_id = TypeId::of::<T>();
let mut registry = UserDataRegistry::new(type_id);
f(&mut registry);
let lua = self.lock();
unsafe {
// Deregister the type if it already registered
let type_id = TypeId::of::<T>();
if let Some(&table_id) = (*lua.extra.get()).registered_userdata.get(&type_id) {
if let Some(&table_id) = (*lua.extra.get()).registered_userdata_t.get(&type_id) {
ffi::luaL_unref(lua.state(), ffi::LUA_REGISTRYINDEX, table_id);
}
// Register the type
lua.register_userdata_metatable(registry)?;
lua.create_userdata_metatable(registry)?;
}
Ok(())
}
@@ -1306,27 +1333,73 @@ impl Lua {
T: UserData + 'static,
{
let ud = UserDataProxy::<T>(PhantomData);
unsafe { self.lock().make_userdata(UserDataVariant::new(ud)) }
unsafe { self.lock().make_userdata(UserDataStorage::new(ud)) }
}
/// Sets the metatable for a Luau builtin vector type.
#[cfg(any(feature = "luau", doc))]
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
pub fn set_vector_metatable(&self, metatable: Option<Table>) {
/// Sets the metatable for a Lua builtin type.
///
/// The metatable will be shared by all values of the given type.
///
/// # Examples
///
/// Change metatable for Lua boolean type:
///
/// ```
/// # use mlua::{Lua, Result, Function};
/// # fn main() -> Result<()> {
/// # let lua = Lua::new();
/// let mt = lua.create_table()?;
/// mt.set("__tostring", lua.create_function(|_, b: bool| Ok(if b { "2" } else { "0" }))?)?;
/// lua.set_type_metatable::<bool>(Some(mt));
/// lua.load("assert(tostring(true) == '2')").exec()?;
/// # Ok(())
/// # }
/// ```
#[allow(private_bounds)]
pub fn set_type_metatable<T: LuaType>(&self, metatable: Option<Table>) {
let lua = self.lock();
let state = lua.state();
unsafe {
let _sg = StackGuard::new(state);
assert_stack(state, 2);
#[cfg(not(feature = "luau-vector4"))]
ffi::lua_pushvector(state, 0., 0., 0.);
#[cfg(feature = "luau-vector4")]
ffi::lua_pushvector(state, 0., 0., 0., 0.);
match T::TYPE_ID {
ffi::LUA_TBOOLEAN => {
ffi::lua_pushboolean(state, 0);
}
ffi::LUA_TLIGHTUSERDATA => {
ffi::lua_pushlightuserdata(state, ptr::null_mut());
}
ffi::LUA_TNUMBER => {
ffi::lua_pushnumber(state, 0.);
}
#[cfg(feature = "luau")]
ffi::LUA_TVECTOR => {
#[cfg(not(feature = "luau-vector4"))]
ffi::lua_pushvector(state, 0., 0., 0.);
#[cfg(feature = "luau-vector4")]
ffi::lua_pushvector(state, 0., 0., 0., 0.);
}
ffi::LUA_TSTRING => {
ffi::lua_pushstring(state, b"\0" as *const u8 as *const _);
}
ffi::LUA_TFUNCTION => match self.load("function() end").eval::<Function>() {
Ok(func) => lua.push_ref(&func.0),
Err(_) => return,
},
ffi::LUA_TTHREAD => {
ffi::lua_newthread(state);
}
#[cfg(feature = "luau")]
ffi::LUA_TBUFFER => {
ffi::lua_newbuffer(state, 0);
}
_ => return,
}
match metatable {
Some(metatable) => lua.push_ref(&metatable.0),
None => ffi::lua_pushnil(state),
};
}
ffi::lua_setmetatable(state, -2);
}
}
@@ -1380,15 +1453,12 @@ impl Lua {
/// dropped. `Function` types will error when called, and `AnyUserData` will be typeless. It
/// would be impossible to prevent handles to scoped values from escaping anyway, since you
/// would always be able to smuggle them through Lua state.
// pub fn scope<'lua, 'scope, R>(
// &'lua self,
// f: impl FnOnce(&Scope<'lua, 'scope>) -> Result<R>,
// ) -> Result<R>
// where
// 'lua: 'scope,
// {
// f(&Scope::new(self))
// }
pub fn scope<'env, R>(
&self,
f: impl for<'scope> FnOnce(&'scope mut Scope<'scope, 'env>) -> Result<R>,
) -> Result<R> {
f(&mut Scope::new(self.lock_arc()))
}
/// Attempts to coerce a Lua value into a String in a manner consistent with Lua's internal
/// behavior.
@@ -1486,6 +1556,12 @@ impl Lua {
T::from_lua(value, self)
}
/// Converts a value that implements `IntoLua` into a `FromLua` variant.
#[inline]
pub fn convert<U: FromLua>(&self, value: impl IntoLua) -> Result<U> {
U::from_lua(value.into_lua(self)?, self)
}
/// Converts a value that implements `IntoLuaMulti` into a `MultiValue` instance.
#[inline]
pub fn pack_multi(&self, t: impl IntoLuaMulti) -> Result<MultiValue> {
@@ -1789,22 +1865,13 @@ impl Lua {
extra.app_data.remove()
}
/// Pushes a value that implements `IntoLua` onto the Lua stack.
///
/// Uses 2 stack spaces, does not call checkstack.
#[doc(hidden)]
#[inline(always)]
pub unsafe fn push(&self, value: impl IntoLua) -> Result<()> {
self.lock().push(value)
}
/// Returns an internal `Poll::Pending` constant used for executing async callbacks.
#[cfg(feature = "async")]
#[doc(hidden)]
#[inline(always)]
pub fn poll_pending() -> LightUserData {
static ASYNC_POLL_PENDING: u8 = 0;
LightUserData(&ASYNC_POLL_PENDING as *const u8 as *mut c_void)
LightUserData(&ASYNC_POLL_PENDING as *const u8 as *mut std::os::raw::c_void)
}
// Luau version located in `luau/mod.rs`
@@ -1853,6 +1920,7 @@ impl Lua {
/// 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.
#[cfg(feature = "async")]
#[inline(always)]
pub(crate) unsafe fn raw_lua(&self) -> &RawLua {
&*self.raw.data_ptr()
+2 -2
View File
@@ -35,7 +35,7 @@ pub(crate) struct ExtraData {
pub(super) weak: MaybeUninit<WeakLua>,
pub(super) owned: bool,
pub(super) registered_userdata: FxHashMap<TypeId, c_int>,
pub(super) registered_userdata_t: FxHashMap<TypeId, c_int>,
pub(super) registered_userdata_mt: FxHashMap<*const c_void, Option<TypeId>>,
pub(super) last_checked_userdata_mt: (*const c_void, Option<TypeId>),
@@ -144,7 +144,7 @@ impl ExtraData {
lua: MaybeUninit::uninit(),
weak: MaybeUninit::uninit(),
owned,
registered_userdata: FxHashMap::default(),
registered_userdata_t: FxHashMap::default(),
registered_userdata_mt: FxHashMap::default(),
last_checked_userdata_mt: (ptr::null(), None),
registry_unref_list: Arc::new(Mutex::new(Some(Vec::new()))),
+115 -82
View File
@@ -16,18 +16,19 @@ use crate::stdlib::StdLib;
use crate::string::String;
use crate::table::Table;
use crate::thread::Thread;
use crate::traits::IntoLua;
use crate::types::{
AppDataRef, AppDataRefMut, Callback, CallbackUpvalue, DestructedUserdata, Integer, LightUserData,
MaybeSend, ReentrantMutex, RegistryKey, SubtypeId, ValueRef, XRc,
MaybeSend, ReentrantMutex, RegistryKey, ValueRef, XRc,
};
use crate::userdata::{AnyUserData, MetaMethod, UserData, UserDataRegistry, UserDataVariant};
use crate::userdata::{AnyUserData, MetaMethod, UserData, UserDataRegistry, UserDataStorage};
use crate::util::{
assert_stack, check_stack, get_destructed_userdata_metatable, get_internal_userdata, get_main_state,
get_userdata, init_error_registry, init_internal_metatable, init_userdata_metatable, pop_error,
push_internal_userdata, push_string, push_table, rawset_field, safe_pcall, safe_xpcall, short_type_name,
StackGuard, WrappedFailure,
};
use crate::value::{FromLuaMulti, IntoLua, MultiValue, Nil, Value};
use crate::value::{Nil, Value};
use super::extra::ExtraData;
use super::{Lua, LuaOptions, WeakLua};
@@ -37,12 +38,15 @@ use crate::hook::{Debug, HookTriggers};
#[cfg(feature = "async")]
use {
crate::multi::MultiValue,
crate::traits::FromLuaMulti,
crate::types::{AsyncCallback, AsyncCallbackUpvalue, AsyncPollUpvalue},
std::ptr::NonNull,
std::task::{Context, Poll, Waker},
};
/// An inner Lua struct which holds a raw Lua state.
#[doc(hidden)]
pub struct RawLua {
// The state is dynamic and depends on context
pub(super) state: Cell<*mut ffi::lua_State>,
@@ -83,8 +87,11 @@ impl RawLua {
unsafe { (*self.extra.get()).weak() }
}
/// Returns a pointer to the current Lua state.
///
/// The pointer refers to the active Lua coroutine and depends on the context.
#[inline(always)]
pub(crate) fn state(&self) -> *mut ffi::lua_State {
pub fn state(&self) -> *mut ffi::lua_State {
self.state.get()
}
@@ -278,7 +285,7 @@ impl RawLua {
/// See [`Lua::app_data_ref`]
#[track_caller]
#[inline]
pub(crate) fn app_data_ref<T: 'static>(&self) -> Option<AppDataRef<T>> {
pub(crate) fn app_data_ref_unguarded<T: 'static>(&self) -> Option<AppDataRef<T>> {
let extra = unsafe { &*self.extra.get() };
extra.app_data.borrow(None)
}
@@ -286,7 +293,7 @@ impl RawLua {
/// See [`Lua::app_data_mut`]
#[track_caller]
#[inline]
pub(crate) fn app_data_mut<T: 'static>(&self) -> Option<AppDataRefMut<T>> {
pub(crate) fn app_data_mut_unguarded<T: 'static>(&self) -> Option<AppDataRefMut<T>> {
let extra = unsafe { &*self.extra.get() };
extra.app_data.borrow_mut(None)
}
@@ -351,8 +358,11 @@ impl RawLua {
triggers: HookTriggers,
callback: F,
) where
F: Fn(&Lua, Debug) -> Result<()> + MaybeSend + 'static,
F: Fn(&Lua, Debug) -> Result<crate::VmState> + MaybeSend + 'static,
{
use crate::types::VmState;
use std::rc::Rc;
unsafe extern "C-unwind" fn hook_proc(state: *mut ffi::lua_State, ar: *mut ffi::lua_Debug) {
let extra = ExtraData::get(state);
if (*extra).hook_thread != state {
@@ -360,20 +370,37 @@ impl RawLua {
ffi::lua_sethook(state, None, 0, 0);
return;
}
callback_error_ext(state, extra, move |extra, _| {
let result = 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 std::rc::Rc::strong_count(&hook_cb) > 2 {
return Ok(()); // Don't allow recursion
if Rc::strong_count(&hook_cb) > 2 {
return Ok(VmState::Continue); // Don't allow recursion
}
let rawlua = (*extra).raw_lua();
let _guard = StateGuard::new(rawlua, state);
let debug = Debug::new(rawlua, ar);
hook_cb((*extra).lua(), debug)
})
});
match result {
VmState::Continue => {}
VmState::Yield => {
// Only count and line events can yield
if (*ar).event == ffi::LUA_HOOKCOUNT || (*ar).event == ffi::LUA_HOOKLINE {
#[cfg(any(feature = "lua54", feature = "lua53"))]
if ffi::lua_isyieldable(state) != 0 {
ffi::lua_yield(state, 0);
}
#[cfg(any(feature = "lua52", feature = "lua51", feature = "luajit"))]
{
ffi::lua_pushliteral(state, "attempt to yield from a hook");
ffi::lua_error(state);
}
}
}
}
}
(*self.extra.get()).hook_callback = Some(std::rc::Rc::new(callback));
(*self.extra.get()).hook_callback = Some(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());
}
@@ -500,10 +527,9 @@ impl RawLua {
/// Pushes a value that implements `IntoLua` onto the Lua stack.
///
/// Uses 2 stack spaces, does not call checkstack.
#[doc(hidden)]
/// Uses up to 2 stack spaces to push a single value, does not call `checkstack`.
#[inline(always)]
pub unsafe fn push(&self, value: impl IntoLua) -> Result<()> {
pub(crate) unsafe fn push(&self, value: impl IntoLua) -> Result<()> {
value.push_into_stack(self)
}
@@ -530,10 +556,13 @@ impl RawLua {
Value::Function(f) => self.push_ref(&f.0),
Value::Thread(t) => self.push_ref(&t.0),
Value::UserData(ud) => self.push_ref(&ud.0),
#[cfg(feature = "luau")]
Value::Buffer(buf) => self.push_ref(&buf.0),
Value::Error(err) => {
let protect = !self.unlikely_memory_error();
push_internal_userdata(state, WrappedFailure::Error(*err.clone()), protect)?;
}
Value::Other(vref) => self.push_ref(vref),
}
Ok(())
}
@@ -618,7 +647,7 @@ impl RawLua {
}
_ => {
ffi::lua_xpush(state, self.ref_thread(), idx);
Value::UserData(AnyUserData(self.pop_ref_thread(), SubtypeId::None))
Value::UserData(AnyUserData(self.pop_ref_thread()))
}
}
}
@@ -631,19 +660,14 @@ impl RawLua {
#[cfg(feature = "luau")]
ffi::LUA_TBUFFER => {
// Buffer is represented as a userdata type
ffi::lua_xpush(state, self.ref_thread(), idx);
Value::UserData(AnyUserData(self.pop_ref_thread(), SubtypeId::Buffer))
Value::Buffer(crate::Buffer(self.pop_ref_thread()))
}
#[cfg(feature = "luajit")]
ffi::LUA_TCDATA => {
// CData is represented as a userdata type
_ => {
ffi::lua_xpush(state, self.ref_thread(), idx);
Value::UserData(AnyUserData(self.pop_ref_thread(), SubtypeId::CData))
Value::Other(self.pop_ref_thread())
}
_ => mlua_panic!("unexpected value type on stack"),
}
}
@@ -704,6 +728,11 @@ impl RawLua {
#[inline]
pub(crate) unsafe fn unlikely_memory_error(&self) -> bool {
#[cfg(debug_assertions)]
if cfg!(force_memory_limit) {
return false;
}
// 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,
@@ -711,45 +740,45 @@ impl RawLua {
}
}
pub(crate) unsafe fn make_userdata<T>(&self, data: UserDataVariant<T>) -> Result<AnyUserData>
pub(crate) unsafe fn make_userdata<T>(&self, data: UserDataStorage<T>) -> Result<AnyUserData>
where
T: UserData + 'static,
{
self.make_userdata_with_metatable(data, || {
// Check if userdata/metatable is already registered
let type_id = TypeId::of::<T>();
if let Some(&table_id) = (*self.extra.get()).registered_userdata.get(&type_id) {
if let Some(&table_id) = (*self.extra.get()).registered_userdata_t.get(&type_id) {
return Ok(table_id as Integer);
}
// Create a new metatable from `UserData` definition
let mut registry = const { UserDataRegistry::new() };
let mut registry = UserDataRegistry::new(type_id);
T::register(&mut registry);
self.register_userdata_metatable(registry)
self.create_userdata_metatable(registry)
})
}
pub(crate) unsafe fn make_any_userdata<T>(&self, data: UserDataVariant<T>) -> Result<AnyUserData>
pub(crate) unsafe fn make_any_userdata<T>(&self, data: UserDataStorage<T>) -> Result<AnyUserData>
where
T: 'static,
{
self.make_userdata_with_metatable(data, || {
// Check if userdata/metatable is already registered
let type_id = TypeId::of::<T>();
if let Some(&table_id) = (*self.extra.get()).registered_userdata.get(&type_id) {
if let Some(&table_id) = (*self.extra.get()).registered_userdata_t.get(&type_id) {
return Ok(table_id as Integer);
}
// Create an empty metatable
let registry = const { UserDataRegistry::new() };
self.register_userdata_metatable::<T>(registry)
let registry = UserDataRegistry::<T>::new(type_id);
self.create_userdata_metatable(registry)
})
}
unsafe fn make_userdata_with_metatable<T>(
&self,
data: UserDataVariant<T>,
data: UserDataStorage<T>,
get_metatable_id: impl FnOnce() -> Result<Integer>,
) -> Result<AnyUserData> {
let state = self.state();
@@ -760,10 +789,7 @@ impl RawLua {
ffi::lua_pushnil(state);
ffi::lua_rawgeti(state, ffi::LUA_REGISTRYINDEX, get_metatable_id()?);
let protect = !self.unlikely_memory_error();
#[cfg(not(feature = "lua54"))]
crate::util::push_userdata(state, data, protect)?;
#[cfg(feature = "lua54")]
crate::util::push_userdata_uv(state, data, crate::userdata::USER_VALUE_MAXSLOT as c_int, protect)?;
ffi::lua_replace(state, -3);
ffi::lua_setmetatable(state, -2);
@@ -779,15 +805,34 @@ impl RawLua {
ffi::lua_setuservalue(state, -2);
}
Ok(AnyUserData(self.pop_ref(), SubtypeId::None))
Ok(AnyUserData(self.pop_ref()))
}
pub(crate) unsafe fn register_userdata_metatable<T: 'static>(
pub(crate) unsafe fn create_userdata_metatable<T>(
&self,
mut registry: UserDataRegistry<T>,
registry: UserDataRegistry<T>,
) -> Result<Integer> {
let state = self.state();
let _sg = StackGuard::new(state);
let type_id = registry.type_id();
self.push_userdata_metatable(registry)?;
let mt_ptr = ffi::lua_topointer(state, -1);
let id = protect_lua!(state, 1, 0, |state| {
ffi::luaL_ref(state, ffi::LUA_REGISTRYINDEX)
})?;
if let Some(type_id) = type_id {
(*self.extra.get()).registered_userdata_t.insert(type_id, id);
}
self.register_userdata_metatable(mt_ptr, type_id);
Ok(id as Integer)
}
pub(crate) unsafe fn push_userdata_metatable<T>(&self, mut registry: UserDataRegistry<T>) -> Result<()> {
let state = self.state();
let _sg = StackGuard::with_top(state, ffi::lua_gettop(state) + 1);
check_stack(state, 13)?;
// Prepare metatable, add meta methods first and then meta fields
@@ -922,7 +967,7 @@ impl RawLua {
let extra_init = None;
#[cfg(not(feature = "luau"))]
let extra_init: Option<fn(*mut ffi::lua_State) -> Result<()>> = Some(|state| {
ffi::lua_pushcfunction(state, crate::util::userdata_destructor::<UserDataVariant<T>>);
ffi::lua_pushcfunction(state, crate::util::userdata_destructor::<UserDataStorage<T>>);
rawset_field(state, -2, "__gc")
});
@@ -938,44 +983,21 @@ impl RawLua {
// Pop extra tables to get metatable on top of the stack
ffi::lua_pop(state, extra_tables_count);
let mt_ptr = ffi::lua_topointer(state, -1);
let id = protect_lua!(state, 1, 0, |state| {
ffi::luaL_ref(state, ffi::LUA_REGISTRYINDEX)
})?;
let type_id = TypeId::of::<T>();
(*self.extra.get()).registered_userdata.insert(type_id, id);
(*self.extra.get())
.registered_userdata_mt
.insert(mt_ptr, Some(type_id));
Ok(id as Integer)
Ok(())
}
// #[inline]
// pub(crate) unsafe fn register_raw_userdata_metatable(
// &self,
// ptr: *const c_void,
// type_id: Option<TypeId>,
// ) {
// (*self.extra.get())
// .registered_userdata_mt
// .insert(ptr, type_id);
// }
#[inline(always)]
pub(crate) unsafe fn register_userdata_metatable(&self, mt_ptr: *const c_void, type_id: Option<TypeId>) {
(*self.extra.get()).registered_userdata_mt.insert(mt_ptr, type_id);
}
// #[inline]
// pub(crate) unsafe fn deregister_raw_userdata_metatable(&self, ptr: *const c_void) {
// (*self.extra.get()).registered_userdata_mt.remove(&ptr);
// if (*self.extra.get()).last_checked_userdata_mt.0 == ptr {
// (*self.extra.get()).last_checked_userdata_mt = (ptr::null(), None);
// }
// }
// #[inline(always)]
// pub(crate) unsafe fn get_userdata_ref<T: 'static>(&self, idx: c_int) -> Result<UserDataRef<T>> {
// let guard = self.lua().lock_arc();
// (*get_userdata::<UserDataVariant<T>>(self.state(), idx)).try_make_ref(guard)
// }
#[inline(always)]
pub(crate) unsafe fn deregister_userdata_metatable(&self, mt_ptr: *const c_void) {
(*self.extra.get()).registered_userdata_mt.remove(&mt_ptr);
if (*self.extra.get()).last_checked_userdata_mt.0 == mt_ptr {
(*self.extra.get()).last_checked_userdata_mt = (ptr::null(), None);
}
}
// Returns `TypeId` for the userdata ref, checking that it's registered and not destructed.
//
@@ -985,8 +1007,18 @@ impl RawLua {
}
// Same as `get_userdata_ref_type_id` but assumes the userdata is already on the stack.
pub(crate) unsafe fn get_userdata_type_id(&self, idx: c_int) -> Result<Option<TypeId>> {
self.get_userdata_type_id_inner(self.state(), idx)
pub(crate) unsafe fn get_userdata_type_id<T>(&self, idx: c_int) -> Result<Option<TypeId>> {
match self.get_userdata_type_id_inner(self.state(), idx) {
Ok(type_id) => Ok(type_id),
Err(Error::UserDataTypeMismatch) if ffi::lua_type(self.state(), idx) != ffi::LUA_TUSERDATA => {
// Report `FromLuaConversionError` instead
let idx_type_name = CStr::from_ptr(ffi::luaL_typename(self.state(), idx));
let idx_type_name = idx_type_name.to_str().unwrap();
let message = format!("expected userdata of type '{}'", short_type_name::<T>());
Err(Error::from_lua_conversion(idx_type_name, "userdata", message))
}
Err(err) => Err(err),
}
}
unsafe fn get_userdata_type_id_inner(
@@ -1028,8 +1060,6 @@ 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 {
let upvalue = get_userdata::<CallbackUpvalue>(state, ffi::lua_upvalueindex(1));
callback_error_ext(state, (*upvalue).extra.get(), |extra, nargs| {
@@ -1037,8 +1067,10 @@ impl RawLua {
// 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)
match (*upvalue).data {
Some(ref func) => func(rawlua, nargs),
None => Err(Error::CallbackDestructed),
}
})
}
@@ -1047,6 +1079,7 @@ impl RawLua {
let _sg = StackGuard::new(state);
check_stack(state, 4)?;
let func = Some(func);
let extra = XRc::clone(&self.extra);
let protect = !self.unlikely_memory_error();
push_internal_userdata(state, CallbackUpvalue { data: func, extra }, protect)?;
+44 -33
View File
@@ -1,7 +1,7 @@
use std::borrow::Borrow;
use std::hash::{Hash, Hasher};
use std::ops::Deref;
use std::os::raw::c_void;
use std::os::raw::{c_int, c_void};
use std::string::String as StdString;
use std::{cmp, fmt, slice, str};
@@ -13,7 +13,7 @@ use {
use crate::error::{Error, Result};
use crate::state::Lua;
use crate::types::ValueRef;
use crate::types::{LuaType, ValueRef};
/// Handle to an internal Lua string.
///
@@ -45,7 +45,7 @@ impl String {
let BorrowedBytes(bytes, guard) = self.as_bytes();
let s = str::from_utf8(bytes).map_err(|e| Error::FromLuaConversionError {
from: "string",
to: "&str",
to: "&str".to_string(),
message: Some(e.to_string()),
})?;
Ok(BorrowedStr(s, guard))
@@ -105,22 +105,22 @@ impl String {
unsafe fn to_slice(&self) -> (&[u8], Lua) {
let lua = self.0.lua.upgrade();
let rawlua = lua.lock();
let ref_thread = rawlua.ref_thread();
unsafe {
let slice = unsafe {
let rawlua = lua.lock();
let ref_thread = rawlua.ref_thread();
mlua_debug_assert!(
ffi::lua_type(ref_thread, self.0.index) == ffi::LUA_TSTRING,
"string ref is not string type"
);
let mut size = 0;
// This will not trigger a 'm' error, because the reference is guaranteed to be of
// string type
let mut size = 0;
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)
}
slice::from_raw_parts(data as *const u8, size + 1)
};
(slice, lua)
}
/// Converts this string to a generic C pointer.
@@ -143,23 +143,8 @@ impl fmt::Debug for String {
}
// Format as bytes
write!(f, "b\"")?;
for &b in bytes {
// https://doc.rust-lang.org/reference/tokens.html#byte-escapes
match b {
b'\n' => write!(f, "\\n")?,
b'\r' => write!(f, "\\r")?,
b'\t' => write!(f, "\\t")?,
b'\\' | b'"' => write!(f, "\\{}", b as char)?,
b'\0' => write!(f, "\\0")?,
// ASCII printable
0x20..=0x7e => write!(f, "{}", b as char)?,
_ => write!(f, "\\x{b:02x}")?,
}
}
write!(f, "\"")?;
Ok(())
write!(f, "b")?;
<bstr::BStr as fmt::Debug>::fmt(bstr::BStr::new(&bytes), f)
}
}
@@ -179,19 +164,25 @@ where
}
}
impl PartialEq<String> for String {
impl PartialEq for String {
fn eq(&self, other: &String) -> bool {
self.as_bytes() == other.as_bytes()
}
}
impl PartialEq<&String> for String {
fn eq(&self, other: &&String) -> bool {
self.as_bytes() == other.as_bytes()
impl Eq for String {}
impl PartialOrd for String {
fn partial_cmp(&self, other: &String) -> Option<cmp::Ordering> {
Some(self.cmp(other))
}
}
impl Eq for String {}
impl Ord for String {
fn cmp(&self, other: &String) -> cmp::Ordering {
self.as_bytes().cmp(&other.as_bytes())
}
}
impl Hash for String {
fn hash<H: Hasher>(&self, state: &mut H) {
@@ -259,6 +250,8 @@ where
}
}
impl Eq for BorrowedStr<'_> {}
impl<T> PartialOrd<T> for BorrowedStr<'_>
where
T: AsRef<str>,
@@ -268,6 +261,12 @@ where
}
}
impl Ord for BorrowedStr<'_> {
fn cmp(&self, other: &Self) -> cmp::Ordering {
self.0.cmp(other.0)
}
}
/// A borrowed byte slice (`&[u8]`) that holds a strong reference to the Lua state.
pub struct BorrowedBytes<'a>(&'a [u8], #[allow(unused)] Lua);
@@ -309,6 +308,8 @@ where
}
}
impl Eq for BorrowedBytes<'_> {}
impl<T> PartialOrd<T> for BorrowedBytes<'_>
where
T: AsRef<[u8]>,
@@ -318,6 +319,12 @@ where
}
}
impl Ord for BorrowedBytes<'_> {
fn cmp(&self, other: &Self) -> cmp::Ordering {
self.0.cmp(other.0)
}
}
impl<'a> IntoIterator for BorrowedBytes<'a> {
type Item = &'a u8;
type IntoIter = slice::Iter<'a, u8>;
@@ -327,6 +334,10 @@ impl<'a> IntoIterator for BorrowedBytes<'a> {
}
}
impl LuaType for String {
const TYPE_ID: c_int = ffi::LUA_TSTRING;
}
#[cfg(test)]
mod assertions {
use super::*;
+20 -16
View File
@@ -1,7 +1,7 @@
use std::collections::HashSet;
use std::fmt;
use std::marker::PhantomData;
use std::os::raw::c_void;
use std::os::raw::{c_int, c_void};
use std::string::String as StdString;
#[cfg(feature = "serialize")]
@@ -14,16 +14,16 @@ use {
use crate::error::{Error, Result};
use crate::function::Function;
use crate::state::{LuaGuard, RawLua};
use crate::traits::ObjectLike;
use crate::types::{Integer, ValueRef};
use crate::traits::{FromLua, FromLuaMulti, IntoLua, IntoLuaMulti, ObjectLike};
use crate::types::{Integer, LuaType, ValueRef};
use crate::util::{assert_stack, check_stack, StackGuard};
use crate::value::{FromLua, FromLuaMulti, IntoLua, IntoLuaMulti, Nil, Value};
use crate::value::{Nil, Value};
#[cfg(feature = "async")]
use futures_util::future::{self, Either, Future};
/// Handle to an internal Lua table.
#[derive(Clone)]
#[derive(Clone, PartialEq)]
pub struct Table(pub(crate) ValueRef);
impl Table {
@@ -227,12 +227,12 @@ impl Table {
// Compare using __eq metamethod if exists
// First, check the self for the metamethod.
// If self does not define it, then check the other table.
if let Some(mt) = self.get_metatable() {
if let Some(mt) = self.metatable() {
if mt.contains_key("__eq")? {
return mt.get::<Function>("__eq")?.call((self, other));
}
}
if let Some(mt) = other.get_metatable() {
if let Some(mt) = other.metatable() {
if mt.contains_key("__eq")? {
return mt.get::<Function>("__eq")?.call((self, other));
}
@@ -493,7 +493,7 @@ impl Table {
/// Returns a reference to the metatable of this table, or `None` if no metatable is set.
///
/// Unlike the `getmetatable` Lua function, this method ignores the `__metatable` field.
pub fn get_metatable(&self) -> Option<Table> {
pub fn metatable(&self) -> Option<Table> {
let lua = self.0.lua.lock();
let state = lua.state();
unsafe {
@@ -509,6 +509,12 @@ impl Table {
}
}
#[doc(hidden)]
#[deprecated(since = "0.10.0", note = "please use `metatable` instead")]
pub fn get_metatable(&self) -> Option<Table> {
self.metatable()
}
/// Sets or removes the metatable of this table.
///
/// If `metatable` is `None`, the metatable is removed (if no metatable is set, this does
@@ -779,7 +785,7 @@ impl Table {
// Collect key/value pairs into a vector so we can sort them
let mut pairs = self.pairs::<Value, Value>().flatten().collect::<Vec<_>>();
// Sort keys
pairs.sort_by(|(a, _), (b, _)| a.cmp(b));
pairs.sort_by(|(a, _), (b, _)| a.sort_cmp(b));
if pairs.is_empty() {
return write!(fmt, "{{}}");
}
@@ -800,13 +806,7 @@ impl fmt::Debug for Table {
if fmt.alternate() {
return self.fmt_pretty(fmt, 0, &mut HashSet::new());
}
fmt.write_fmt(format_args!("Table({:?})", self.0))
}
}
impl PartialEq for Table {
fn eq(&self, other: &Self) -> bool {
self.0 == other.0
fmt.debug_tuple("Table").field(&self.0).finish()
}
}
@@ -961,6 +961,10 @@ impl Serialize for Table {
}
}
impl LuaType for Table {
const TYPE_ID: c_int = ffi::LUA_TTABLE;
}
#[cfg(feature = "serialize")]
impl<'a> SerializableTable<'a> {
#[inline]
+16 -5
View File
@@ -1,12 +1,13 @@
use std::fmt;
use std::os::raw::{c_int, c_void};
use crate::error::{Error, Result};
#[allow(unused)]
use crate::state::Lua;
use crate::state::RawLua;
use crate::types::ValueRef;
use crate::traits::{FromLuaMulti, IntoLuaMulti};
use crate::types::{LuaType, ValueRef};
use crate::util::{check_stack, error_traceback_thread, pop_error, StackGuard};
use crate::value::{FromLuaMulti, IntoLuaMulti};
#[cfg(not(feature = "luau"))]
use crate::{
@@ -42,7 +43,7 @@ pub enum ThreadStatus {
}
/// Handle to an internal Lua thread (coroutine).
#[derive(Clone, Debug)]
#[derive(Clone)]
pub struct Thread(pub(crate) ValueRef, pub(crate) *mut ffi::lua_State);
#[cfg(feature = "send")]
@@ -143,7 +144,7 @@ impl Thread {
let state = lua.state();
let thread_state = self.state();
let nargs = args.push_into_stack_multi(&lua)?;
let nargs = args.push_into_stack_multi(lua)?;
if nargs > 0 {
check_stack(thread_state, nargs)?;
ffi::lua_xmove(state, thread_state, nargs);
@@ -194,7 +195,7 @@ impl Thread {
#[cfg_attr(docsrs, doc(cfg(not(feature = "luau"))))]
pub fn set_hook<F>(&self, triggers: HookTriggers, callback: F)
where
F: Fn(&Lua, Debug) -> Result<()> + MaybeSend + 'static,
F: Fn(&Lua, Debug) -> Result<crate::VmState> + MaybeSend + 'static,
{
let lua = self.0.lua.lock();
unsafe {
@@ -366,12 +367,22 @@ impl Thread {
}
}
impl fmt::Debug for Thread {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt.debug_tuple("Thread").field(&self.0).finish()
}
}
impl PartialEq for Thread {
fn eq(&self, other: &Self) -> bool {
self.0 == other.0
}
}
impl LuaType for Thread {
const TYPE_ID: c_int = ffi::LUA_TTHREAD;
}
#[cfg(feature = "async")]
impl<A, R> AsyncThread<A, R> {
#[inline]
+238 -2
View File
@@ -1,12 +1,148 @@
use std::os::raw::c_int;
use std::string::String as StdString;
use std::sync::Arc;
use crate::error::Result;
use crate::error::{Error, Result};
use crate::multi::MultiValue;
use crate::private::Sealed;
use crate::value::{FromLua, FromLuaMulti, IntoLua, IntoLuaMulti};
use crate::state::{Lua, RawLua};
use crate::types::MaybeSend;
use crate::util::{check_stack, short_type_name};
use crate::value::Value;
#[cfg(feature = "async")]
use std::future::Future;
/// Trait for types convertible to `Value`.
pub trait IntoLua: Sized {
/// Performs the conversion.
fn into_lua(self, lua: &Lua) -> Result<Value>;
/// Pushes the value into the Lua stack.
///
/// # Safety
/// This method does not check Lua stack space.
#[doc(hidden)]
#[inline]
unsafe fn push_into_stack(self, lua: &RawLua) -> Result<()> {
lua.push_value(&self.into_lua(lua.lua())?)
}
}
/// Trait for types convertible from `Value`.
pub trait FromLua: Sized {
/// Performs the conversion.
fn from_lua(value: Value, lua: &Lua) -> Result<Self>;
/// Performs the conversion for an argument (eg. function argument).
///
/// `i` is the argument index (position),
/// `to` is a function name that received the argument.
#[doc(hidden)]
#[inline]
fn from_lua_arg(arg: Value, i: usize, to: Option<&str>, lua: &Lua) -> Result<Self> {
Self::from_lua(arg, lua).map_err(|err| Error::BadArgument {
to: to.map(|s| s.to_string()),
pos: i,
name: None,
cause: Arc::new(err),
})
}
/// Performs the conversion for a value in the Lua stack at index `idx`.
#[doc(hidden)]
#[inline]
unsafe fn from_stack(idx: c_int, lua: &RawLua) -> Result<Self> {
Self::from_lua(lua.stack_value(idx, None), lua.lua())
}
/// Same as `from_lua_arg` but for a value in the Lua stack at index `idx`.
#[doc(hidden)]
#[inline]
unsafe fn from_stack_arg(idx: c_int, i: usize, to: Option<&str>, lua: &RawLua) -> Result<Self> {
Self::from_stack(idx, lua).map_err(|err| Error::BadArgument {
to: to.map(|s| s.to_string()),
pos: i,
name: None,
cause: Arc::new(err),
})
}
}
/// Trait for types convertible to any number of Lua values.
///
/// This is a generalization of `IntoLua`, allowing any number of resulting Lua values instead of
/// just one. Any type that implements `IntoLua` will automatically implement this trait.
pub trait IntoLuaMulti: Sized {
/// Performs the conversion.
fn into_lua_multi(self, lua: &Lua) -> Result<MultiValue>;
/// Pushes the values into the Lua stack.
///
/// Returns number of pushed values.
#[doc(hidden)]
#[inline]
unsafe fn push_into_stack_multi(self, lua: &RawLua) -> Result<c_int> {
let values = self.into_lua_multi(lua.lua())?;
let len: c_int = values.len().try_into().unwrap();
unsafe {
check_stack(lua.state(), len + 1)?;
for val in &values {
lua.push_value(val)?;
}
}
Ok(len)
}
}
/// Trait for types that can be created from an arbitrary number of Lua values.
///
/// This is a generalization of `FromLua`, allowing an arbitrary number of Lua values to participate
/// in the conversion. Any type that implements `FromLua` will automatically implement this trait.
pub trait FromLuaMulti: Sized {
/// Performs the conversion.
///
/// In case `values` contains more values than needed to perform the conversion, the excess
/// values should be ignored. This reflects the semantics of Lua when calling a function or
/// assigning values. Similarly, if not enough values are given, conversions should assume that
/// any missing values are nil.
fn from_lua_multi(values: MultiValue, lua: &Lua) -> Result<Self>;
/// Performs the conversion for a list of arguments.
///
/// `i` is an index (position) of the first argument,
/// `to` is a function name that received the arguments.
#[doc(hidden)]
#[inline]
fn from_lua_args(args: MultiValue, i: usize, to: Option<&str>, lua: &Lua) -> Result<Self> {
let _ = (i, to);
Self::from_lua_multi(args, lua)
}
/// Performs the conversion for a number of values in the Lua stack.
#[doc(hidden)]
#[inline]
unsafe fn from_stack_multi(nvals: c_int, lua: &RawLua) -> Result<Self> {
let mut values = MultiValue::with_capacity(nvals as usize);
for idx in 0..nvals {
values.push_back(lua.stack_value(-nvals + idx, None));
}
if nvals > 0 {
// It's safe to clear the stack as all references moved to ref thread
ffi::lua_pop(lua.state(), nvals);
}
Self::from_lua_multi(values, lua.lua())
}
/// Same as `from_lua_args` but for a number of values in the Lua stack.
#[doc(hidden)]
#[inline]
unsafe fn from_stack_args(nargs: c_int, i: usize, to: Option<&str>, lua: &RawLua) -> Result<Self> {
let _ = (i, to);
Self::from_stack_multi(nargs, lua)
}
}
/// 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.
@@ -76,3 +212,103 @@ pub trait ObjectLike: Sealed {
/// This might invoke the `__tostring` metamethod.
fn to_string(&self) -> Result<StdString>;
}
/// A trait for types that can be used as Lua functions.
pub trait LuaNativeFn<A: FromLuaMulti> {
type Output: IntoLuaMulti;
fn call(&self, args: A) -> Self::Output;
}
/// A trait for types with mutable state that can be used as Lua functions.
pub trait LuaNativeFnMut<A: FromLuaMulti> {
type Output: IntoLuaMulti;
fn call(&mut self, args: A) -> Self::Output;
}
/// A trait for types that returns a future and can be used as Lua functions.
#[cfg(feature = "async")]
pub trait LuaNativeAsyncFn<A: FromLuaMulti> {
type Output: IntoLuaMulti;
fn call(&self, args: A) -> impl Future<Output = Self::Output> + MaybeSend + 'static;
}
macro_rules! impl_lua_native_fn {
($($A:ident),*) => {
impl<FN, $($A,)* R> LuaNativeFn<($($A,)*)> for FN
where
FN: Fn($($A,)*) -> R + MaybeSend + 'static,
($($A,)*): FromLuaMulti,
R: IntoLuaMulti,
{
type Output = R;
#[allow(non_snake_case)]
fn call(&self, args: ($($A,)*)) -> Self::Output {
let ($($A,)*) = args;
self($($A,)*)
}
}
impl<FN, $($A,)* R> LuaNativeFnMut<($($A,)*)> for FN
where
FN: FnMut($($A,)*) -> R + MaybeSend + 'static,
($($A,)*): FromLuaMulti,
R: IntoLuaMulti,
{
type Output = R;
#[allow(non_snake_case)]
fn call(&mut self, args: ($($A,)*)) -> Self::Output {
let ($($A,)*) = args;
self($($A,)*)
}
}
#[cfg(feature = "async")]
impl<FN, $($A,)* Fut, R> LuaNativeAsyncFn<($($A,)*)> for FN
where
FN: Fn($($A,)*) -> Fut + MaybeSend + 'static,
($($A,)*): FromLuaMulti,
Fut: Future<Output = R> + MaybeSend + 'static,
R: IntoLuaMulti,
{
type Output = R;
#[allow(non_snake_case)]
fn call(&self, args: ($($A,)*)) -> impl Future<Output = Self::Output> + MaybeSend + 'static {
let ($($A,)*) = args;
self($($A,)*)
}
}
};
}
impl_lua_native_fn!();
impl_lua_native_fn!(A);
impl_lua_native_fn!(A, B);
impl_lua_native_fn!(A, B, C);
impl_lua_native_fn!(A, B, C, D);
impl_lua_native_fn!(A, B, C, D, E);
impl_lua_native_fn!(A, B, C, D, E, F);
impl_lua_native_fn!(A, B, C, D, E, F, G);
impl_lua_native_fn!(A, B, C, D, E, F, G, H);
impl_lua_native_fn!(A, B, C, D, E, F, G, H, I);
impl_lua_native_fn!(A, B, C, D, E, F, G, H, I, J);
impl_lua_native_fn!(A, B, C, D, E, F, G, H, I, J, K);
impl_lua_native_fn!(A, B, C, D, E, F, G, H, I, J, K, L);
impl_lua_native_fn!(A, B, C, D, E, F, G, H, I, J, K, L, M);
impl_lua_native_fn!(A, B, C, D, E, F, G, H, I, J, K, L, M, N);
impl_lua_native_fn!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O);
impl_lua_native_fn!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P);
pub(crate) trait ShortTypeName {
#[inline(always)]
fn type_name() -> StdString {
short_type_name::<Self>()
}
}
impl<T> ShortTypeName for T {}
+21 -75
View File
@@ -1,12 +1,11 @@
use std::cell::UnsafeCell;
use std::fmt;
use std::os::raw::{c_int, c_void};
use std::rc::Rc;
use crate::error::Result;
#[cfg(not(feature = "luau"))]
use crate::hook::Debug;
use crate::state::{ExtraData, Lua, RawLua, WeakLua};
use crate::state::{ExtraData, Lua, RawLua};
// Re-export mutex wrappers
pub(crate) use sync::{ArcReentrantMutexGuard, ReentrantMutex, ReentrantMutexGuard, XRc, XWeak};
@@ -18,7 +17,9 @@ pub(crate) type BoxFuture<'a, T> = futures_util::future::BoxFuture<'a, T>;
pub(crate) type BoxFuture<'a, T> = futures_util::future::LocalBoxFuture<'a, T>;
pub use app_data::{AppData, AppDataRef, AppDataRefMut};
pub use either::Either;
pub use registry_key::RegistryKey;
pub(crate) use value_ref::ValueRef;
#[cfg(any(feature = "luau", doc))]
pub use vector::Vector;
@@ -27,16 +28,6 @@ pub type Integer = ffi::lua_Integer;
/// Type of Lua floating point numbers.
pub type Number = ffi::lua_Number;
// Represents different subtypes wrapped to AnyUserData
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub(crate) enum SubtypeId {
None,
#[cfg(feature = "luau")]
Buffer,
#[cfg(feature = "luajit")]
CData,
}
/// A "light" userdata value. Equivalent to an unmanaged raw pointer.
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub struct LightUserData(pub *mut c_void);
@@ -52,12 +43,14 @@ pub(crate) type Callback = Box<dyn Fn(&RawLua, c_int) -> Result<c_int> + Send +
#[cfg(not(feature = "send"))]
pub(crate) type Callback = Box<dyn Fn(&RawLua, c_int) -> Result<c_int> + 'static>;
pub(crate) type ScopedCallback<'s> = Box<dyn Fn(&RawLua, c_int) -> Result<c_int> + 's>;
pub(crate) struct Upvalue<T> {
pub(crate) data: T,
pub(crate) extra: XRc<UnsafeCell<ExtraData>>,
}
pub(crate) type CallbackUpvalue = Upvalue<Callback>;
pub(crate) type CallbackUpvalue = Upvalue<Option<Callback>>;
#[cfg(all(feature = "async", feature = "send"))]
pub(crate) type AsyncCallback =
@@ -74,18 +67,19 @@ pub(crate) type AsyncCallbackUpvalue = Upvalue<AsyncCallback>;
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))]
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
pub enum VmState {
Continue,
/// Yield the current thread.
///
/// Supported by Lua 5.3+ and Luau.
Yield,
}
#[cfg(all(feature = "send", not(feature = "luau")))]
pub(crate) type HookCallback = Rc<dyn Fn(&Lua, Debug) -> Result<()> + Send>;
pub(crate) type HookCallback = Rc<dyn Fn(&Lua, Debug) -> Result<VmState> + Send>;
#[cfg(all(not(feature = "send"), not(feature = "luau")))]
pub(crate) type HookCallback = Rc<dyn Fn(&Lua, Debug) -> Result<()>>;
pub(crate) type HookCallback = Rc<dyn Fn(&Lua, Debug) -> Result<VmState>>;
#[cfg(all(feature = "send", feature = "luau"))]
pub(crate) type InterruptCallback = Rc<dyn Fn(&Lua) -> Result<VmState> + Send>;
@@ -112,75 +106,27 @@ impl<T> MaybeSend for T {}
pub(crate) struct DestructedUserdata;
pub(crate) struct ValueRef {
pub(crate) lua: WeakLua,
pub(crate) index: c_int,
pub(crate) drop: bool,
pub(crate) trait LuaType {
const TYPE_ID: c_int;
}
impl ValueRef {
#[inline]
pub(crate) fn new(lua: &RawLua, index: c_int) -> Self {
ValueRef {
lua: lua.weak().clone(),
index,
drop: true,
}
}
#[inline]
pub(crate) fn to_pointer(&self) -> *const c_void {
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 LuaType for bool {
const TYPE_ID: c_int = ffi::LUA_TBOOLEAN;
}
impl fmt::Debug for ValueRef {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Ref({:p})", self.to_pointer())
}
impl LuaType for Number {
const TYPE_ID: c_int = ffi::LUA_TNUMBER;
}
impl Clone for ValueRef {
fn clone(&self) -> Self {
unsafe { self.lua.lock().clone_ref(self) }
}
}
impl Drop for ValueRef {
fn drop(&mut self) {
if self.drop {
if let Some(lua) = self.lua.try_lock() {
unsafe { lua.drop_ref(self) };
}
}
}
}
impl PartialEq for ValueRef {
fn eq(&self, other: &Self) -> bool {
assert!(
self.lua == other.lua,
"Lua instance passed Value created from a different main Lua state"
);
let lua = self.lua.lock();
unsafe { ffi::lua_rawequal(lua.ref_thread(), self.index, other.index) == 1 }
}
impl LuaType for LightUserData {
const TYPE_ID: c_int = ffi::LUA_TLIGHTUSERDATA;
}
mod app_data;
mod either;
mod registry_key;
mod sync;
mod value_ref;
#[cfg(any(feature = "luau", doc))]
mod vector;
+135
View File
@@ -0,0 +1,135 @@
use std::ffi::CStr;
use std::fmt;
use std::hash::Hash;
use std::os::raw::c_int;
use crate::error::{Error, Result};
use crate::state::{Lua, RawLua};
use crate::traits::{FromLua, IntoLua, ShortTypeName as _};
use crate::value::Value;
/// Combination of two types into a single one.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum Either<L, R> {
Left(L),
Right(R),
}
impl<L, R> Either<L, R> {
/// Return true if the value is the Left variant.
#[inline]
pub fn is_left(&self) -> bool {
matches!(self, Either::Left(_))
}
/// Return true if the value is the Right variant.
#[inline]
pub fn is_right(&self) -> bool {
matches!(self, Either::Right(_))
}
/// Convert the left side of `Either<L, R>` to an `Option<L>`.
#[inline]
pub fn left(self) -> Option<L> {
match self {
Either::Left(l) => Some(l),
_ => None,
}
}
/// Convert the right side of `Either<L, R>` to an `Option<R>`.
#[inline]
pub fn right(self) -> Option<R> {
match self {
Either::Right(r) => Some(r),
_ => None,
}
}
/// Convert `&Either<L, R>` to `Either<&L, &R>`.
#[inline]
pub fn as_ref(&self) -> Either<&L, &R> {
match self {
Either::Left(l) => Either::Left(l),
Either::Right(r) => Either::Right(r),
}
}
/// Convert `&mut Either<L, R>` to `Either<&mut L, &mut R>`.
#[inline]
pub fn as_mut(&mut self) -> Either<&mut L, &mut R> {
match self {
Either::Left(l) => Either::Left(l),
Either::Right(r) => Either::Right(r),
}
}
}
impl<L, R> fmt::Display for Either<L, R>
where
L: fmt::Display,
R: fmt::Display,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Either::Left(a) => a.fmt(f),
Either::Right(b) => b.fmt(f),
}
}
}
impl<L: IntoLua, R: IntoLua> IntoLua for Either<L, R> {
#[inline]
fn into_lua(self, lua: &Lua) -> Result<Value> {
match self {
Either::Left(l) => l.into_lua(lua),
Either::Right(r) => r.into_lua(lua),
}
}
#[inline]
unsafe fn push_into_stack(self, lua: &RawLua) -> Result<()> {
match self {
Either::Left(l) => l.push_into_stack(lua),
Either::Right(r) => r.push_into_stack(lua),
}
}
}
impl<L: FromLua, R: FromLua> FromLua for Either<L, R> {
#[inline]
fn from_lua(value: Value, lua: &Lua) -> Result<Self> {
let value_type_name = value.type_name();
// Try the left type first
match L::from_lua(value.clone(), lua) {
Ok(l) => Ok(Either::Left(l)),
// Try the right type
Err(_) => match R::from_lua(value, lua).map(Either::Right) {
Ok(r) => Ok(r),
Err(_) => Err(Error::FromLuaConversionError {
from: value_type_name,
to: Self::type_name(),
message: None,
}),
},
}
}
#[inline]
unsafe fn from_stack(idx: c_int, lua: &RawLua) -> Result<Self> {
match L::from_stack(idx, lua) {
Ok(l) => Ok(Either::Left(l)),
Err(_) => match R::from_stack(idx, lua).map(Either::Right) {
Ok(r) => Ok(r),
Err(_) => {
let value_type_name = CStr::from_ptr(ffi::luaL_typename(lua.state(), idx));
Err(Error::FromLuaConversionError {
from: value_type_name.to_str().unwrap(),
to: Self::type_name(),
message: None,
})
}
},
}
}
}
+71
View File
@@ -0,0 +1,71 @@
use std::fmt;
use std::os::raw::{c_int, c_void};
use crate::state::{RawLua, WeakLua};
/// A reference to a Lua (complex) value stored in the Lua auxiliary thread.
pub(crate) struct ValueRef {
pub(crate) lua: WeakLua,
pub(crate) index: c_int,
pub(crate) drop: bool,
}
impl ValueRef {
#[inline]
pub(crate) fn new(lua: &RawLua, index: c_int) -> Self {
ValueRef {
lua: lua.weak().clone(),
index,
drop: true,
}
}
#[inline]
pub(crate) fn to_pointer(&self) -> *const c_void {
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 {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Ref({:p})", self.to_pointer())
}
}
impl Clone for ValueRef {
fn clone(&self) -> Self {
unsafe { self.lua.lock().clone_ref(self) }
}
}
impl Drop for ValueRef {
fn drop(&mut self) {
if self.drop {
if let Some(lua) = self.lua.try_lock() {
unsafe { lua.drop_ref(self) };
}
}
}
}
impl PartialEq for ValueRef {
fn eq(&self, other: &Self) -> bool {
assert!(
self.lua == other.lua,
"Lua instance passed Value created from a different main Lua state"
);
let lua = self.lua.lock();
unsafe { ffi::lua_rawequal(lua.ref_thread(), self.index, other.index) == 1 }
}
}
+12 -1
View File
@@ -3,12 +3,14 @@ use std::fmt;
#[cfg(all(any(feature = "luau", doc), feature = "serialize"))]
use serde::ser::{Serialize, SerializeTupleStruct, Serializer};
use super::LuaType;
/// 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)]
#[derive(Debug, Default, Clone, Copy, PartialEq, PartialOrd)]
pub struct Vector(pub(crate) [f32; Self::SIZE]);
impl fmt::Display for Vector {
@@ -84,3 +86,12 @@ impl PartialEq<[f32; Self::SIZE]> for Vector {
self.0 == *other
}
}
impl LuaType for Vector {
#[cfg(feature = "luau")]
const TYPE_ID: i32 = ffi::LUA_TVECTOR;
// This is a dummy value, as `Vector` is supported only by Luau
#[cfg(not(feature = "luau"))]
const TYPE_ID: i32 = ffi::LUA_TNONE;
}
+61 -95
View File
@@ -2,7 +2,7 @@ use std::any::TypeId;
use std::ffi::CStr;
use std::fmt;
use std::hash::Hash;
use std::os::raw::{c_char, c_int, c_void};
use std::os::raw::{c_char, c_void};
use std::string::String as StdString;
#[cfg(feature = "async")]
@@ -16,22 +16,20 @@ use {
use crate::error::{Error, Result};
use crate::function::Function;
use crate::state::{Lua, LuaGuard};
use crate::state::Lua;
use crate::string::String;
use crate::table::{Table, TablePairs};
use crate::types::{MaybeSend, SubtypeId, ValueRef};
use crate::traits::{FromLua, FromLuaMulti, IntoLua, IntoLuaMulti};
use crate::types::{MaybeSend, ValueRef};
use crate::util::{check_stack, get_userdata, take_userdata, StackGuard};
use crate::value::{FromLua, FromLuaMulti, IntoLua, IntoLuaMulti, Value};
use crate::value::Value;
// Re-export for convenience
pub(crate) use cell::UserDataVariant;
pub(crate) use cell::UserDataStorage;
pub use cell::{UserDataRef, UserDataRefMut};
pub(crate) use registry::UserDataProxy;
pub use registry::UserDataRegistry;
#[cfg(feature = "lua54")]
pub(crate) const USER_VALUE_MAXSLOT: usize = 8;
/// Kinds of metamethods that can be overridden.
///
/// Currently, this mechanism does not allow overriding the `__gc` metamethod, since there is
@@ -131,7 +129,7 @@ pub enum MetaMethod {
///
/// Executed when a variable, that marked as to-be-closed, goes out of scope.
///
/// More information about to-be-closed variabled can be found in the Lua 5.4
/// More information about to-be-closed variables can be found in the Lua 5.4
/// [documentation][lua_doc].
///
/// Requires `feature = "lua54"`
@@ -645,13 +643,14 @@ pub trait UserData: Sized {
/// [`UserData`]: crate::UserData
/// [`is`]: crate::AnyUserData::is
/// [`borrow`]: crate::AnyUserData::borrow
#[derive(Clone, Debug)]
pub struct AnyUserData(pub(crate) ValueRef, pub(crate) SubtypeId);
#[derive(Clone, Debug, PartialEq)]
pub struct AnyUserData(pub(crate) ValueRef);
impl AnyUserData {
/// Checks whether the type of this userdata is `T`.
#[inline]
pub fn is<T: 'static>(&self) -> bool {
self.inspect::<T, _, _>(|_, _| Ok(())).is_ok()
self.inspect::<T, _, _>(|_| Ok(())).is_ok()
}
/// Borrow this userdata immutably if it is of type `T`.
@@ -659,10 +658,18 @@ impl AnyUserData {
/// # Errors
///
/// Returns a `UserDataBorrowError` if the userdata is already mutably borrowed. Returns a
/// `UserDataTypeMismatch` if the userdata is not of type `T`.
/// `UserDataTypeMismatch` if the userdata is not of type `T` or if it's scoped.
#[inline]
pub fn borrow<T: 'static>(&self) -> Result<UserDataRef<T>> {
self.inspect(|variant, _| variant.try_borrow_owned())
self.inspect(|ud| ud.try_borrow_owned())
}
/// Borrow this userdata immutably if it is of type `T`, passing the borrowed value
/// to the closure.
///
/// This method is the only way to borrow scoped userdata (created inside [`Lua::scope`]).
pub fn borrow_scoped<T: 'static, R>(&self, f: impl FnOnce(&T) -> R) -> Result<R> {
self.inspect(|ud| ud.try_borrow_scoped(|ud| f(ud)))
}
/// Borrow this userdata mutably if it is of type `T`.
@@ -670,10 +677,18 @@ impl AnyUserData {
/// # Errors
///
/// Returns a `UserDataBorrowMutError` if the userdata cannot be mutably borrowed.
/// Returns a `UserDataTypeMismatch` if the userdata is not of type `T`.
/// Returns a `UserDataTypeMismatch` if the userdata is not of type `T` or if it's scoped.
#[inline]
pub fn borrow_mut<T: 'static>(&self) -> Result<UserDataRefMut<T>> {
self.inspect(|variant, _| variant.try_borrow_owned_mut())
self.inspect(|ud| ud.try_borrow_owned_mut())
}
/// Borrow this userdata mutably if it is of type `T`, passing the borrowed value
/// to the closure.
///
/// This method is the only way to borrow scoped userdata (created inside [`Lua::scope`]).
pub fn borrow_mut_scoped<T: 'static, R>(&self, f: impl FnOnce(&mut T) -> R) -> Result<R> {
self.inspect(|ud| ud.try_borrow_scoped_mut(|ud| f(ud)))
}
/// Takes the value out of this userdata.
@@ -692,8 +707,8 @@ impl AnyUserData {
match type_id {
Some(type_id) if type_id == TypeId::of::<T>() => {
// Try to borrow userdata exclusively
let _ = (*get_userdata::<UserDataVariant<T>>(state, -1)).try_borrow_mut()?;
take_userdata::<UserDataVariant<T>>(state).into_inner()
let _ = (*get_userdata::<UserDataStorage<T>>(state, -1)).try_borrow_mut()?;
take_userdata::<UserDataStorage<T>>(state).into_inner()
}
_ => Err(Error::UserDataTypeMismatch),
}
@@ -754,29 +769,16 @@ impl AnyUserData {
lua.push_userdata_ref(&self.0)?;
lua.push(v)?;
#[cfg(feature = "lua54")]
if n < USER_VALUE_MAXSLOT {
ffi::lua_setiuservalue(state, -2, n as c_int);
return Ok(());
}
// Multiple (extra) user values are emulated by storing them in a table
protect_lua!(state, 2, 0, |state| {
if getuservalue_table(state, -2) != ffi::LUA_TTABLE {
if ffi::lua_getuservalue(state, -2) != ffi::LUA_TTABLE {
// Create a new table to use as uservalue
ffi::lua_pop(state, 1);
ffi::lua_newtable(state);
ffi::lua_pushvalue(state, -1);
#[cfg(feature = "lua54")]
ffi::lua_setiuservalue(state, -4, USER_VALUE_MAXSLOT as c_int);
#[cfg(not(feature = "lua54"))]
ffi::lua_setuservalue(state, -4);
}
ffi::lua_pushvalue(state, -2);
#[cfg(feature = "lua54")]
ffi::lua_rawseti(state, -2, (n - USER_VALUE_MAXSLOT + 1) as ffi::lua_Integer);
#[cfg(not(feature = "lua54"))]
ffi::lua_rawseti(state, -2, n as ffi::lua_Integer);
})?;
@@ -806,21 +808,12 @@ impl AnyUserData {
lua.push_userdata_ref(&self.0)?;
#[cfg(feature = "lua54")]
if n < USER_VALUE_MAXSLOT {
ffi::lua_getiuservalue(state, -1, n as c_int);
return V::from_lua(lua.pop_value(), lua.lua());
}
// Multiple (extra) user values are emulated by storing them in a table
protect_lua!(state, 1, 1, |state| {
if getuservalue_table(state, -1) != ffi::LUA_TTABLE {
if ffi::lua_getuservalue(state, -1) != ffi::LUA_TTABLE {
ffi::lua_pushnil(state);
return;
}
#[cfg(feature = "lua54")]
ffi::lua_rawgeti(state, -1, (n - USER_VALUE_MAXSLOT + 1) as ffi::lua_Integer);
#[cfg(not(feature = "lua54"))]
ffi::lua_rawgeti(state, -1, n as ffi::lua_Integer);
})?;
@@ -851,15 +844,11 @@ impl AnyUserData {
// Multiple (extra) user values are emulated by storing them in a table
protect_lua!(state, 2, 0, |state| {
if getuservalue_table(state, -2) != ffi::LUA_TTABLE {
if ffi::lua_getuservalue(state, -2) != ffi::LUA_TTABLE {
// Create a new table to use as uservalue
ffi::lua_pop(state, 1);
ffi::lua_newtable(state);
ffi::lua_pushvalue(state, -1);
#[cfg(feature = "lua54")]
ffi::lua_setiuservalue(state, -4, USER_VALUE_MAXSLOT as c_int);
#[cfg(not(feature = "lua54"))]
ffi::lua_setuservalue(state, -4);
}
ffi::lua_pushlstring(state, name.as_ptr() as *const c_char, name.len());
@@ -885,7 +874,7 @@ impl AnyUserData {
// Multiple (extra) user values are emulated by storing them in a table
protect_lua!(state, 1, 1, |state| {
if getuservalue_table(state, -1) != ffi::LUA_TTABLE {
if ffi::lua_getuservalue(state, -1) != ffi::LUA_TTABLE {
ffi::lua_pushnil(state);
return;
}
@@ -912,10 +901,16 @@ impl AnyUserData {
///
/// [`UserDataMetatable`]: crate::UserDataMetatable
#[inline]
pub fn get_metatable(&self) -> Result<UserDataMetatable> {
pub fn metatable(&self) -> Result<UserDataMetatable> {
self.get_raw_metatable().map(UserDataMetatable)
}
#[doc(hidden)]
#[deprecated(since = "0.10.0", note = "please use `metatable` instead")]
pub fn get_metatable(&self) -> Result<UserDataMetatable> {
self.metatable()
}
fn get_raw_metatable(&self) -> Result<Table> {
let lua = self.0.lua.lock();
let state = lua.state();
@@ -941,14 +936,6 @@ impl AnyUserData {
/// Returns a type name of this `UserData` (from a metatable field).
pub(crate) fn type_name(&self) -> Result<Option<StdString>> {
match self.1 {
SubtypeId::None => {}
#[cfg(feature = "luau")]
SubtypeId::Buffer => return Ok(Some("buffer".to_owned())),
#[cfg(feature = "luajit")]
SubtypeId::CData => return Ok(Some("cdata".to_owned())),
}
let lua = self.0.lua.lock();
let state = lua.state();
unsafe {
@@ -998,29 +985,24 @@ impl AnyUserData {
let is_serializable = || unsafe {
// Userdata must be registered and not destructed
let _ = lua.get_userdata_ref_type_id(&self.0)?;
let ud = &*get_userdata::<UserDataVariant<()>>(lua.ref_thread(), self.0.index);
match ud {
UserDataVariant::Serializable(..) => Result::Ok(true),
_ => Result::Ok(false),
}
let ud = &*get_userdata::<UserDataStorage<()>>(lua.ref_thread(), self.0.index);
Ok::<_, Error>((*ud).is_serializable())
};
is_serializable().unwrap_or(false)
}
pub(crate) fn inspect<'a, T, F, R>(&'a self, func: F) -> Result<R>
pub(crate) fn inspect<T, F, R>(&self, func: F) -> Result<R>
where
T: 'static,
F: FnOnce(&'a UserDataVariant<T>, LuaGuard) -> Result<R>,
F: FnOnce(&UserDataStorage<T>) -> Result<R>,
{
let lua = self.0.lua.lock();
unsafe {
let type_id = lua.get_userdata_ref_type_id(&self.0)?;
match type_id {
Some(type_id) if type_id == TypeId::of::<T>() => {
let ref_thread = lua.ref_thread();
let ud = get_userdata::<UserDataVariant<T>>(ref_thread, self.0.index);
func(&*ud, lua)
let ud = get_userdata::<UserDataStorage<T>>(lua.ref_thread(), self.0.index);
func(&*ud)
}
_ => Err(Error::UserDataTypeMismatch),
}
@@ -1028,12 +1010,6 @@ impl AnyUserData {
}
}
impl PartialEq for AnyUserData {
fn eq(&self, other: &Self) -> bool {
self.0 == other.0
}
}
impl AsRef<AnyUserData> for AnyUserData {
#[inline]
fn as_ref(&self) -> &Self {
@@ -1041,13 +1017,6 @@ impl AsRef<AnyUserData> for AnyUserData {
}
}
unsafe fn getuservalue_table(state: *mut ffi::lua_State, idx: c_int) -> c_int {
#[cfg(feature = "lua54")]
return ffi::lua_getiuservalue(state, idx, USER_VALUE_MAXSLOT as c_int);
#[cfg(not(feature = "lua54"))]
return ffi::lua_getuservalue(state, idx);
}
/// Handle to a `UserData` metatable.
#[derive(Clone, Debug)]
pub struct UserDataMetatable(pub(crate) Table);
@@ -1129,24 +1098,11 @@ impl Serialize for AnyUserData {
S: Serializer,
{
let lua = self.0.lua.lock();
// Special case for Luau buffer type
#[cfg(feature = "luau")]
if self.1 == SubtypeId::Buffer {
let buf = unsafe {
let mut size = 0usize;
let buf = ffi::lua_tobuffer(lua.ref_thread(), self.0.index, &mut size);
mlua_assert!(!buf.is_null(), "invalid Luau buffer");
std::slice::from_raw_parts(buf as *const u8, size)
};
return serializer.serialize_bytes(buf);
}
unsafe {
let _ = lua
.get_userdata_ref_type_id(&self.0)
.map_err(ser::Error::custom)?;
let ud = &*get_userdata::<UserDataVariant<()>>(lua.ref_thread(), self.0.index);
let ud = &*get_userdata::<UserDataStorage<()>>(lua.ref_thread(), self.0.index);
ud.serialize(serializer)
}
}
@@ -1161,6 +1117,16 @@ impl AnyUserData {
pub fn wrap<T: MaybeSend + 'static>(data: T) -> impl IntoLua {
WrappedUserdata(move |lua| lua.create_any_userdata(data))
}
/// Wraps any Rust type that implements [`Serialize`], returning an opaque type that implements
/// [`IntoLua`] trait.
///
/// This function uses [`Lua::create_ser_any_userdata()`] under the hood.
#[cfg(feature = "serialize")]
#[cfg_attr(docsrs, doc(cfg(feature = "serialize")))]
pub fn wrap_ser<T: Serialize + MaybeSend + 'static>(data: T) -> impl IntoLua {
WrappedUserdata(move |lua| lua.create_ser_any_userdata(data))
}
}
impl<F> IntoLua for WrappedUserdata<F>
+150 -31
View File
@@ -1,5 +1,5 @@
use std::any::{type_name, TypeId};
use std::cell::UnsafeCell;
use std::cell::{RefCell, UnsafeCell};
use std::fmt;
use std::ops::{Deref, DerefMut};
use std::os::raw::c_int;
@@ -9,10 +9,11 @@ use serde::ser::{Serialize, Serializer};
use crate::error::{Error, Result};
use crate::state::{Lua, RawLua};
use crate::types::{MaybeSend, XRc};
use crate::traits::FromLua;
use crate::types::XRc;
use crate::userdata::AnyUserData;
use crate::util::get_userdata;
use crate::value::{FromLua, Value};
use crate::value::Value;
use super::lock::{RawLock, UserDataLock};
@@ -22,6 +23,11 @@ type DynSerialize = dyn erased_serde::Serialize;
#[cfg(all(feature = "serialize", feature = "send"))]
type DynSerialize = dyn erased_serde::Serialize + Send;
pub(crate) enum UserDataStorage<T> {
Owned(UserDataVariant<T>),
Scoped(ScopedUserDataVariant<T>),
}
// A enum for storing userdata values.
// It's stored inside a Lua VM and protected by the outer `ReentrantMutex`.
pub(crate) enum UserDataVariant<T> {
@@ -42,39 +48,34 @@ impl<T> Clone for UserDataVariant<T> {
}
impl<T> UserDataVariant<T> {
#[inline(always)]
pub(crate) fn new(data: T) -> Self {
Self::Default(XRc::new(UserDataCell::new(data)))
}
// Immutably borrows the wrapped value in-place.
#[inline(always)]
pub(crate) fn try_borrow(&self) -> Result<UserDataBorrowRef<T>> {
fn try_borrow(&self) -> Result<UserDataBorrowRef<T>> {
UserDataBorrowRef::try_from(self)
}
// Immutably borrows the wrapped value and returns an owned reference.
#[inline(always)]
pub(crate) fn try_borrow_owned(&self) -> Result<UserDataRef<T>> {
fn try_borrow_owned(&self) -> Result<UserDataRef<T>> {
UserDataRef::try_from(self.clone())
}
// Mutably borrows the wrapped value in-place.
#[inline(always)]
pub(crate) fn try_borrow_mut(&self) -> Result<UserDataBorrowMut<T>> {
fn try_borrow_mut(&self) -> Result<UserDataBorrowMut<T>> {
UserDataBorrowMut::try_from(self)
}
// Mutably borrows the wrapped value and returns an owned reference.
#[inline(always)]
pub(crate) fn try_borrow_owned_mut(&self) -> Result<UserDataRefMut<T>> {
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> {
fn into_inner(self) -> Result<T> {
if !self.raw_lock().try_lock_exclusive() {
return Err(Error::UserDataBorrowMutError);
}
@@ -108,20 +109,10 @@ impl<T> UserDataVariant<T> {
}
#[cfg(feature = "serialize")]
impl<T: Serialize + MaybeSend + 'static> UserDataVariant<T> {
#[inline(always)]
pub(crate) fn new_ser(data: T) -> Self {
let data = Box::new(data) as Box<DynSerialize>;
Self::Serializable(XRc::new(UserDataCell::new(data)))
}
}
#[cfg(feature = "serialize")]
impl Serialize for UserDataVariant<()> {
impl Serialize for UserDataStorage<()> {
fn serialize<S: Serializer>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error> {
match self {
Self::Default(_) => Err(serde::ser::Error::custom("cannot serialize <userdata>")),
Self::Serializable(inner) => unsafe {
Self::Owned(UserDataVariant::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)?;
@@ -130,6 +121,7 @@ impl Serialize for UserDataVariant<()> {
let _guard = self.try_borrow().map_err(serde::ser::Error::custom)?;
(*inner.value.get()).serialize(serializer)
},
_ => Err(serde::ser::Error::custom("cannot serialize <userdata>")),
}
}
}
@@ -145,7 +137,7 @@ unsafe impl<T: Send> Sync for UserDataCell<T> {}
impl<T> UserDataCell<T> {
#[inline(always)]
pub fn new(value: T) -> Self {
fn new(value: T) -> Self {
UserDataCell {
raw_lock: RawLock::INIT,
value: UnsafeCell::new(value),
@@ -204,10 +196,10 @@ impl<T: 'static> FromLua for UserDataRef<T> {
}
unsafe fn from_stack(idx: c_int, lua: &RawLua) -> Result<Self> {
let type_id = lua.get_userdata_type_id(idx)?;
let type_id = lua.get_userdata_type_id::<T>(idx)?;
match type_id {
Some(type_id) if type_id == TypeId::of::<T>() => {
(*get_userdata::<UserDataVariant<T>>(lua.state(), idx)).try_borrow_owned()
(*get_userdata::<UserDataStorage<T>>(lua.state(), idx)).try_borrow_owned()
}
_ => Err(Error::UserDataTypeMismatch),
}
@@ -272,10 +264,10 @@ impl<T: 'static> FromLua for UserDataRefMut<T> {
}
unsafe fn from_stack(idx: c_int, lua: &RawLua) -> Result<Self> {
let type_id = lua.get_userdata_type_id(idx)?;
let type_id = lua.get_userdata_type_id::<T>(idx)?;
match type_id {
Some(type_id) if type_id == TypeId::of::<T>() => {
(*get_userdata::<UserDataVariant<T>>(lua.state(), idx)).try_borrow_owned_mut()
(*get_userdata::<UserDataStorage<T>>(lua.state(), idx)).try_borrow_owned_mut()
}
_ => Err(Error::UserDataTypeMismatch),
}
@@ -357,12 +349,139 @@ fn try_value_to_userdata<T>(value: Value) -> Result<AnyUserData> {
Value::UserData(ud) => Ok(ud),
_ => Err(Error::FromLuaConversionError {
from: value.type_name(),
to: "userdata",
to: "userdata".to_string(),
message: Some(format!("expected userdata of type {}", type_name::<T>())),
}),
}
}
pub(crate) enum ScopedUserDataVariant<T> {
Ref(*const T),
RefMut(RefCell<*mut T>),
Boxed(RefCell<*mut T>),
}
impl<T> Drop for ScopedUserDataVariant<T> {
#[inline]
fn drop(&mut self) {
if let Self::Boxed(value) = self {
if let Ok(value) = value.try_borrow_mut() {
unsafe { drop(Box::from_raw(*value)) };
}
}
}
}
impl<T: 'static> UserDataStorage<T> {
#[inline(always)]
pub(crate) fn new(data: T) -> Self {
Self::Owned(UserDataVariant::Default(XRc::new(UserDataCell::new(data))))
}
#[inline(always)]
pub(crate) fn new_ref(data: &T) -> Self {
Self::Scoped(ScopedUserDataVariant::Ref(data))
}
#[inline(always)]
pub(crate) fn new_ref_mut(data: &mut T) -> Self {
Self::Scoped(ScopedUserDataVariant::RefMut(RefCell::new(data)))
}
#[cfg(feature = "serialize")]
#[inline(always)]
pub(crate) fn new_ser(data: T) -> Self
where
T: Serialize + crate::types::MaybeSend,
{
let data = Box::new(data) as Box<DynSerialize>;
Self::Owned(UserDataVariant::Serializable(XRc::new(UserDataCell::new(data))))
}
#[cfg(feature = "serialize")]
#[inline(always)]
pub(crate) fn is_serializable(&self) -> bool {
matches!(self, Self::Owned(UserDataVariant::Serializable(_)))
}
// Immutably borrows the wrapped value and returns an owned reference.
#[inline(always)]
pub(crate) fn try_borrow_owned(&self) -> Result<UserDataRef<T>> {
match self {
Self::Owned(data) => data.try_borrow_owned(),
Self::Scoped(_) => Err(Error::UserDataTypeMismatch),
}
}
#[allow(unused)]
#[inline(always)]
pub(crate) fn try_borrow(&self) -> Result<UserDataBorrowRef<T>> {
match self {
Self::Owned(data) => data.try_borrow(),
Self::Scoped(_) => Err(Error::UserDataTypeMismatch),
}
}
#[inline(always)]
pub(crate) fn try_borrow_mut(&self) -> Result<UserDataBorrowMut<T>> {
match self {
Self::Owned(data) => data.try_borrow_mut(),
Self::Scoped(_) => Err(Error::UserDataTypeMismatch),
}
}
// Mutably borrows the wrapped value and returns an owned reference.
#[inline(always)]
pub(crate) fn try_borrow_owned_mut(&self) -> Result<UserDataRefMut<T>> {
match self {
Self::Owned(data) => data.try_borrow_owned_mut(),
Self::Scoped(_) => Err(Error::UserDataTypeMismatch),
}
}
#[inline(always)]
pub(crate) fn into_inner(self) -> Result<T> {
match self {
Self::Owned(data) => data.into_inner(),
Self::Scoped(_) => Err(Error::UserDataTypeMismatch),
}
}
}
impl<T> UserDataStorage<T> {
#[inline(always)]
pub(crate) fn new_scoped(data: T) -> Self {
let data = Box::into_raw(Box::new(data));
Self::Scoped(ScopedUserDataVariant::Boxed(RefCell::new(data)))
}
#[inline]
pub(crate) fn try_borrow_scoped<R>(&self, f: impl FnOnce(&T) -> R) -> Result<R> {
match self {
Self::Owned(data) => Ok(f(&*data.try_borrow()?)),
Self::Scoped(ScopedUserDataVariant::Ref(value)) => Ok(f(unsafe { &**value })),
Self::Scoped(ScopedUserDataVariant::RefMut(value) | ScopedUserDataVariant::Boxed(value)) => {
let t = value.try_borrow().map_err(|_| Error::UserDataBorrowError)?;
Ok(f(unsafe { &**t }))
}
}
}
#[inline]
pub(crate) fn try_borrow_scoped_mut<R>(&self, f: impl FnOnce(&mut T) -> R) -> Result<R> {
match self {
Self::Owned(data) => Ok(f(&mut *data.try_borrow_mut()?)),
Self::Scoped(ScopedUserDataVariant::Ref(_)) => Err(Error::UserDataBorrowMutError),
Self::Scoped(ScopedUserDataVariant::RefMut(value) | ScopedUserDataVariant::Boxed(value)) => {
let mut t = value
.try_borrow_mut()
.map_err(|_| Error::UserDataBorrowMutError)?;
Ok(f(unsafe { &mut **t }))
}
}
}
}
#[cfg(test)]
mod assertions {
use super::*;
+3 -3
View File
@@ -2,9 +2,9 @@ use std::string::String as StdString;
use crate::error::{Error, Result};
use crate::table::Table;
use crate::traits::ObjectLike;
use crate::traits::{FromLua, FromLuaMulti, IntoLua, IntoLuaMulti, ObjectLike};
use crate::userdata::AnyUserData;
use crate::value::{FromLua, FromLuaMulti, IntoLua, IntoLuaMulti, Value};
use crate::value::Value;
use crate::Function;
#[cfg(feature = "async")]
@@ -88,6 +88,6 @@ impl ObjectLike for AnyUserData {
#[inline]
fn to_string(&self) -> Result<StdString> {
Value::UserData(AnyUserData(self.0.copy(), self.1)).to_string()
Value::UserData(AnyUserData(self.0.copy())).to_string()
}
}
+124 -68
View File
@@ -3,30 +3,34 @@
use std::any::TypeId;
use std::cell::RefCell;
use std::marker::PhantomData;
use std::os::raw::c_int;
use std::os::raw::c_void;
use std::string::String as StdString;
use crate::error::{Error, Result};
use crate::state::{Lua, RawLua};
use crate::traits::{FromLua, FromLuaMulti, IntoLua, IntoLuaMulti};
use crate::types::{Callback, MaybeSend};
use crate::userdata::{
AnyUserData, MetaMethod, UserData, UserDataFields, UserDataMethods, UserDataRef, UserDataRefMut,
};
use crate::userdata::{AnyUserData, MetaMethod, UserData, UserDataFields, UserDataMethods, UserDataStorage};
use crate::util::{get_userdata, short_type_name};
use crate::value::{FromLua, FromLuaMulti, IntoLua, IntoLuaMulti, Value};
use super::cell::{UserDataBorrowMut, UserDataBorrowRef, UserDataVariant};
use crate::value::Value;
#[cfg(feature = "async")]
use {
crate::types::AsyncCallback,
crate::userdata::{UserDataRef, UserDataRefMut},
std::future::{self, Future},
};
type StaticFieldCallback = Box<dyn FnOnce(&RawLua) -> Result<()> + 'static>;
#[derive(Clone, Copy)]
pub(crate) enum UserDataTypeId {
Shared(TypeId),
Unique(usize),
}
/// Handle to registry for userdata methods and metamethods.
pub struct UserDataRegistry<T: 'static> {
pub struct UserDataRegistry<T> {
// Fields
pub(crate) fields: Vec<(String, StaticFieldCallback)>,
pub(crate) field_getters: Vec<(String, Callback)>,
@@ -41,11 +45,13 @@ pub struct UserDataRegistry<T: 'static> {
#[cfg(feature = "async")]
pub(crate) async_meta_methods: Vec<(String, AsyncCallback)>,
pub(crate) type_id: UserDataTypeId,
_type: PhantomData<T>,
}
impl<T: 'static> UserDataRegistry<T> {
pub(crate) const fn new() -> Self {
impl<T> UserDataRegistry<T> {
#[inline]
pub(crate) fn new(type_id: TypeId) -> Self {
UserDataRegistry {
fields: Vec::new(),
field_getters: Vec::new(),
@@ -57,11 +63,38 @@ impl<T: 'static> UserDataRegistry<T> {
meta_methods: Vec::new(),
#[cfg(feature = "async")]
async_meta_methods: Vec::new(),
type_id: UserDataTypeId::Shared(type_id),
_type: PhantomData,
}
}
fn box_method<M, A, R>(name: &str, method: M) -> Callback
#[inline]
pub(crate) fn new_unique(ud_ptr: *const c_void) -> Self {
UserDataRegistry {
fields: Vec::new(),
field_getters: Vec::new(),
field_setters: Vec::new(),
meta_fields: Vec::new(),
methods: Vec::new(),
#[cfg(feature = "async")]
async_methods: Vec::new(),
meta_methods: Vec::new(),
#[cfg(feature = "async")]
async_meta_methods: Vec::new(),
type_id: UserDataTypeId::Unique(ud_ptr as usize),
_type: PhantomData,
}
}
#[inline]
pub(crate) fn type_id(&self) -> Option<TypeId> {
match self.type_id {
UserDataTypeId::Shared(type_id) => Some(type_id),
UserDataTypeId::Unique(_) => None,
}
}
fn box_method<M, A, R>(&self, name: &str, method: M) -> Callback
where
M: Fn(&Lua, &T, A) -> Result<R> + MaybeSend + 'static,
A: FromLuaMulti,
@@ -74,6 +107,7 @@ impl<T: 'static> UserDataRegistry<T> {
};
}
let target_type_id = self.type_id;
Box::new(move |rawlua, nargs| unsafe {
if nargs == 0 {
let err = Error::from_lua_conversion("missing argument", "userdata", None);
@@ -85,17 +119,34 @@ impl<T: 'static> UserDataRegistry<T> {
// Self was at position 1, so we pass 2 here
let args = A::from_stack_args(nargs - 1, 2, Some(&name), rawlua);
match try_self_arg!(rawlua.get_userdata_type_id(self_index)) {
Some(id) if id == TypeId::of::<T>() => {
let ud = try_self_arg!(borrow_userdata_ref::<T>(state, self_index));
method(rawlua.lua(), &ud, args?)?.push_into_stack_multi(rawlua)
match target_type_id {
// This branch is for `'static` userdata that share type metatable
UserDataTypeId::Shared(target_type_id) => {
match try_self_arg!(rawlua.get_userdata_type_id::<T>(self_index)) {
Some(self_type_id) if self_type_id == target_type_id => {
let ud = get_userdata::<UserDataStorage<T>>(state, self_index);
try_self_arg!((*ud).try_borrow_scoped(|ud| {
method(rawlua.lua(), ud, args?)?.push_into_stack_multi(rawlua)
}))
}
_ => Err(Error::bad_self_argument(&name, Error::UserDataTypeMismatch)),
}
}
UserDataTypeId::Unique(target_ptr) => {
match get_userdata::<UserDataStorage<T>>(state, self_index) {
ud if ud as usize == target_ptr => {
try_self_arg!((*ud).try_borrow_scoped(|ud| {
method(rawlua.lua(), ud, args?)?.push_into_stack_multi(rawlua)
}))
}
_ => Err(Error::bad_self_argument(&name, Error::UserDataTypeMismatch)),
}
}
_ => Err(Error::bad_self_argument(&name, Error::UserDataTypeMismatch)),
}
})
}
fn box_method_mut<M, A, R>(name: &str, method: M) -> Callback
fn box_method_mut<M, A, R>(&self, name: &str, method: M) -> Callback
where
M: FnMut(&Lua, &mut T, A) -> Result<R> + MaybeSend + 'static,
A: FromLuaMulti,
@@ -109,6 +160,7 @@ impl<T: 'static> UserDataRegistry<T> {
}
let method = RefCell::new(method);
let target_type_id = self.type_id;
Box::new(move |rawlua, nargs| unsafe {
let mut method = method.try_borrow_mut().map_err(|_| Error::RecursiveMutCallback)?;
if nargs == 0 {
@@ -121,19 +173,37 @@ impl<T: 'static> UserDataRegistry<T> {
// Self was at position 1, so we pass 2 here
let args = A::from_stack_args(nargs - 1, 2, Some(&name), rawlua);
match try_self_arg!(rawlua.get_userdata_type_id(self_index)) {
Some(id) if id == TypeId::of::<T>() => {
let mut ud = try_self_arg!(borrow_userdata_mut::<T>(state, self_index));
method(rawlua.lua(), &mut ud, args?)?.push_into_stack_multi(rawlua)
match target_type_id {
// This branch is for `'static` userdata that share type metatable
UserDataTypeId::Shared(target_type_id) => {
match try_self_arg!(rawlua.get_userdata_type_id::<T>(self_index)) {
Some(self_type_id) if self_type_id == target_type_id => {
let ud = get_userdata::<UserDataStorage<T>>(state, self_index);
try_self_arg!((*ud).try_borrow_scoped_mut(|ud| {
method(rawlua.lua(), ud, args?)?.push_into_stack_multi(rawlua)
}))
}
_ => Err(Error::bad_self_argument(&name, Error::UserDataTypeMismatch)),
}
}
UserDataTypeId::Unique(target_ptr) => {
match get_userdata::<UserDataStorage<T>>(state, self_index) {
ud if ud as usize == target_ptr => {
try_self_arg!((*ud).try_borrow_scoped_mut(|ud| {
method(rawlua.lua(), ud, args?)?.push_into_stack_multi(rawlua)
}))
}
_ => Err(Error::bad_self_argument(&name, Error::UserDataTypeMismatch)),
}
}
_ => Err(Error::bad_self_argument(&name, Error::UserDataTypeMismatch)),
}
})
}
#[cfg(feature = "async")]
fn box_async_method<M, A, MR, R>(name: &str, method: M) -> AsyncCallback
fn box_async_method<M, A, MR, R>(&self, name: &str, method: M) -> AsyncCallback
where
T: 'static,
M: Fn(Lua, UserDataRef<T>, A) -> MR + MaybeSend + 'static,
A: FromLuaMulti,
MR: Future<Output = Result<R>> + MaybeSend + 'static,
@@ -171,8 +241,9 @@ impl<T: 'static> UserDataRegistry<T> {
}
#[cfg(feature = "async")]
fn box_async_method_mut<M, A, MR, R>(name: &str, method: M) -> AsyncCallback
fn box_async_method_mut<M, A, MR, R>(&self, name: &str, method: M) -> AsyncCallback
where
T: 'static,
M: Fn(Lua, UserDataRefMut<T>, A) -> MR + MaybeSend + 'static,
A: FromLuaMulti,
MR: Future<Output = Result<R>> + MaybeSend + 'static,
@@ -209,7 +280,7 @@ impl<T: 'static> UserDataRegistry<T> {
})
}
fn box_function<F, A, R>(name: &str, function: F) -> Callback
fn box_function<F, A, R>(&self, name: &str, function: F) -> Callback
where
F: Fn(&Lua, A) -> Result<R> + MaybeSend + 'static,
A: FromLuaMulti,
@@ -222,7 +293,7 @@ impl<T: 'static> UserDataRegistry<T> {
})
}
fn box_function_mut<F, A, R>(name: &str, function: F) -> Callback
fn box_function_mut<F, A, R>(&self, name: &str, function: F) -> Callback
where
F: FnMut(&Lua, A) -> Result<R> + MaybeSend + 'static,
A: FromLuaMulti,
@@ -240,7 +311,7 @@ impl<T: 'static> UserDataRegistry<T> {
}
#[cfg(feature = "async")]
fn box_async_function<F, A, FR, R>(name: &str, function: F) -> AsyncCallback
fn box_async_function<F, A, FR, R>(&self, name: &str, function: F) -> AsyncCallback
where
F: Fn(Lua, A) -> FR + MaybeSend + 'static,
A: FromLuaMulti,
@@ -282,7 +353,7 @@ fn get_function_name<T>(name: &str) -> StdString {
format!("{}.{name}", short_type_name::<T>())
}
impl<T: 'static> UserDataFields<T> for UserDataRegistry<T> {
impl<T> UserDataFields<T> for UserDataRegistry<T> {
fn add_field<V>(&mut self, name: impl ToString, value: V)
where
V: IntoLua + 'static,
@@ -300,7 +371,7 @@ impl<T: 'static> UserDataFields<T> for UserDataRegistry<T> {
R: IntoLua,
{
let name = name.to_string();
let callback = Self::box_method(&name, move |lua, data, ()| method(lua, data));
let callback = self.box_method(&name, move |lua, data, ()| method(lua, data));
self.field_getters.push((name, callback));
}
@@ -310,7 +381,7 @@ impl<T: 'static> UserDataFields<T> for UserDataRegistry<T> {
A: FromLua,
{
let name = name.to_string();
let callback = Self::box_method_mut(&name, method);
let callback = self.box_method_mut(&name, method);
self.field_setters.push((name, callback));
}
@@ -320,7 +391,7 @@ impl<T: 'static> UserDataFields<T> for UserDataRegistry<T> {
R: IntoLua,
{
let name = name.to_string();
let callback = Self::box_function(&name, function);
let callback = self.box_function(&name, function);
self.field_getters.push((name, callback));
}
@@ -330,7 +401,7 @@ impl<T: 'static> UserDataFields<T> for UserDataRegistry<T> {
A: FromLua,
{
let name = name.to_string();
let callback = Self::box_function_mut(&name, move |lua, (data, val)| function(lua, data, val));
let callback = self.box_function_mut(&name, move |lua, (data, val)| function(lua, data, val));
self.field_setters.push((name, callback));
}
@@ -363,7 +434,7 @@ impl<T: 'static> UserDataFields<T> for UserDataRegistry<T> {
}
}
impl<T: 'static> UserDataMethods<T> for UserDataRegistry<T> {
impl<T> UserDataMethods<T> for UserDataRegistry<T> {
fn add_method<M, A, R>(&mut self, name: impl ToString, method: M)
where
M: Fn(&Lua, &T, A) -> Result<R> + MaybeSend + 'static,
@@ -371,7 +442,7 @@ impl<T: 'static> UserDataMethods<T> for UserDataRegistry<T> {
R: IntoLuaMulti,
{
let name = name.to_string();
let callback = Self::box_method(&name, method);
let callback = self.box_method(&name, method);
self.methods.push((name, callback));
}
@@ -382,33 +453,35 @@ impl<T: 'static> UserDataMethods<T> for UserDataRegistry<T> {
R: IntoLuaMulti,
{
let name = name.to_string();
let callback = Self::box_method_mut(&name, method);
let callback = self.box_method_mut(&name, method);
self.methods.push((name, callback));
}
#[cfg(feature = "async")]
fn add_async_method<M, A, MR, R>(&mut self, name: impl ToString, method: M)
where
T: 'static,
M: Fn(Lua, UserDataRef<T>, A) -> MR + MaybeSend + 'static,
A: FromLuaMulti,
MR: Future<Output = Result<R>> + MaybeSend + 'static,
R: IntoLuaMulti,
{
let name = name.to_string();
let callback = Self::box_async_method(&name, method);
let callback = self.box_async_method(&name, method);
self.async_methods.push((name, callback));
}
#[cfg(feature = "async")]
fn add_async_method_mut<M, A, MR, R>(&mut self, name: impl ToString, method: M)
where
T: 'static,
M: Fn(Lua, UserDataRefMut<T>, A) -> MR + MaybeSend + 'static,
A: FromLuaMulti,
MR: Future<Output = Result<R>> + MaybeSend + 'static,
R: IntoLuaMulti,
{
let name = name.to_string();
let callback = Self::box_async_method_mut(&name, method);
let callback = self.box_async_method_mut(&name, method);
self.async_methods.push((name, callback));
}
@@ -419,7 +492,7 @@ impl<T: 'static> UserDataMethods<T> for UserDataRegistry<T> {
R: IntoLuaMulti,
{
let name = name.to_string();
let callback = Self::box_function(&name, function);
let callback = self.box_function(&name, function);
self.methods.push((name, callback));
}
@@ -430,7 +503,7 @@ impl<T: 'static> UserDataMethods<T> for UserDataRegistry<T> {
R: IntoLuaMulti,
{
let name = name.to_string();
let callback = Self::box_function_mut(&name, function);
let callback = self.box_function_mut(&name, function);
self.methods.push((name, callback));
}
@@ -443,7 +516,7 @@ impl<T: 'static> UserDataMethods<T> for UserDataRegistry<T> {
R: IntoLuaMulti,
{
let name = name.to_string();
let callback = Self::box_async_function(&name, function);
let callback = self.box_async_function(&name, function);
self.async_methods.push((name, callback));
}
@@ -454,7 +527,7 @@ impl<T: 'static> UserDataMethods<T> for UserDataRegistry<T> {
R: IntoLuaMulti,
{
let name = name.to_string();
let callback = Self::box_method(&name, method);
let callback = self.box_method(&name, method);
self.meta_methods.push((name, callback));
}
@@ -465,33 +538,35 @@ impl<T: 'static> UserDataMethods<T> for UserDataRegistry<T> {
R: IntoLuaMulti,
{
let name = name.to_string();
let callback = Self::box_method_mut(&name, method);
let callback = self.box_method_mut(&name, method);
self.meta_methods.push((name, callback));
}
#[cfg(all(feature = "async", not(any(feature = "lua51", feature = "luau"))))]
fn add_async_meta_method<M, A, MR, R>(&mut self, name: impl ToString, method: M)
where
T: 'static,
M: Fn(Lua, UserDataRef<T>, A) -> MR + MaybeSend + 'static,
A: FromLuaMulti,
MR: Future<Output = Result<R>> + MaybeSend + 'static,
R: IntoLuaMulti,
{
let name = name.to_string();
let callback = Self::box_async_method(&name, method);
let callback = self.box_async_method(&name, method);
self.async_meta_methods.push((name, callback));
}
#[cfg(all(feature = "async", not(any(feature = "lua51", feature = "luau"))))]
fn add_async_meta_method_mut<M, A, MR, R>(&mut self, name: impl ToString, method: M)
where
T: 'static,
M: Fn(Lua, UserDataRefMut<T>, A) -> MR + MaybeSend + 'static,
A: FromLuaMulti,
MR: Future<Output = Result<R>> + MaybeSend + 'static,
R: IntoLuaMulti,
{
let name = name.to_string();
let callback = Self::box_async_method_mut(&name, method);
let callback = self.box_async_method_mut(&name, method);
self.async_meta_methods.push((name, callback));
}
@@ -502,7 +577,7 @@ impl<T: 'static> UserDataMethods<T> for UserDataRegistry<T> {
R: IntoLuaMulti,
{
let name = name.to_string();
let callback = Self::box_function(&name, function);
let callback = self.box_function(&name, function);
self.meta_methods.push((name, callback));
}
@@ -513,7 +588,7 @@ impl<T: 'static> UserDataMethods<T> for UserDataRegistry<T> {
R: IntoLuaMulti,
{
let name = name.to_string();
let callback = Self::box_function_mut(&name, function);
let callback = self.box_function_mut(&name, function);
self.meta_methods.push((name, callback));
}
@@ -526,36 +601,17 @@ impl<T: 'static> UserDataMethods<T> for UserDataRegistry<T> {
R: IntoLuaMulti,
{
let name = name.to_string();
let callback = Self::box_async_function(&name, function);
let callback = self.box_async_function(&name, function);
self.async_meta_methods.push((name, callback));
}
}
// Borrow the userdata in-place from the Lua stack
#[inline(always)]
unsafe fn borrow_userdata_ref<'a, T>(
state: *mut ffi::lua_State,
index: c_int,
) -> Result<UserDataBorrowRef<'a, T>> {
let ud = get_userdata::<UserDataVariant<T>>(state, index);
(*ud).try_borrow()
}
// Borrow the userdata mutably in-place from the Lua stack
#[inline(always)]
unsafe fn borrow_userdata_mut<'a, T>(
state: *mut ffi::lua_State,
index: c_int,
) -> Result<UserDataBorrowMut<'a, T>> {
let ud = get_userdata::<UserDataVariant<T>>(state, index);
(*ud).try_borrow_mut()
}
macro_rules! lua_userdata_impl {
($type:ty) => {
impl<T: UserData + 'static> UserData for $type {
fn register(registry: &mut UserDataRegistry<Self>) {
let mut orig_registry = UserDataRegistry::new();
let type_id = TypeId::of::<T>();
let mut orig_registry = UserDataRegistry::new(type_id);
T::register(&mut orig_registry);
// Copy all fields, methods, etc. from the original registry
+7 -6
View File
@@ -204,24 +204,25 @@ pub(crate) unsafe fn protect_lua_closure<F, R>(
f: F,
) -> Result<R>
where
F: Fn(*mut ffi::lua_State) -> R,
F: FnOnce(*mut ffi::lua_State) -> R,
R: Copy,
{
struct Params<F, R: Copy> {
function: F,
function: Option<F>,
result: MaybeUninit<R>,
nresults: c_int,
}
unsafe extern "C-unwind" fn do_call<F, R>(state: *mut ffi::lua_State) -> c_int
where
F: Fn(*mut ffi::lua_State) -> R,
F: FnOnce(*mut ffi::lua_State) -> R,
R: Copy,
{
let params = ffi::lua_touserdata(state, -1) as *mut Params<F, R>;
ffi::lua_pop(state, 1);
(*params).result.write(((*params).function)(state));
let f = (*params).function.take().unwrap();
(*params).result.write(f(state));
if (*params).nresults == ffi::LUA_MULTRET {
ffi::lua_gettop(state)
@@ -241,7 +242,7 @@ where
}
let mut params = Params {
function: f,
function: Some(f),
result: MaybeUninit::uninit(),
nresults,
};
@@ -365,7 +366,7 @@ pub(crate) unsafe fn init_error_registry(state: *mut ffi::lua_State) -> Result<(
// Create destructed userdata metatable
unsafe extern "C-unwind" fn destructed_error(state: *mut ffi::lua_State) -> c_int {
callback_error(state, |_| Err(Error::CallbackDestructed))
callback_error(state, |_| Err(Error::UserDataDestructed))
}
push_table(state, 0, 26, true)?;
+7 -6
View File
@@ -17,10 +17,9 @@ pub(crate) use userdata::{
DESTRUCTED_USERDATA_METATABLE,
};
#[cfg(not(feature = "lua54"))]
#[cfg(not(feature = "luau"))]
pub(crate) use userdata::push_uninit_userdata;
pub(crate) use userdata::push_userdata;
#[cfg(feature = "lua54")]
pub(crate) use userdata::push_userdata_uv;
#[cfg(not(feature = "luau"))]
pub(crate) use userdata::userdata_destructor;
@@ -70,6 +69,7 @@ impl StackGuard {
}
impl Drop for StackGuard {
#[track_caller]
fn drop(&mut self) {
unsafe {
let top = ffi::lua_gettop(self.state);
@@ -275,9 +275,10 @@ pub(crate) unsafe fn to_string(state: *mut ffi::lua_State, index: c_int) -> Stri
ffi::LUA_TTHREAD => format!("<thread {:?}>", ffi::lua_topointer(state, index)),
#[cfg(feature = "luau")]
ffi::LUA_TBUFFER => format!("<buffer {:?}>", ffi::lua_topointer(state, index)),
#[cfg(feature = "luajit")]
ffi::LUA_TCDATA => format!("<cdata {:?}>", ffi::lua_topointer(state, index)),
_ => "<unknown>".to_string(),
type_id => {
let type_name = CStr::from_ptr(ffi::lua_typename(state, type_id)).to_string_lossy();
format!("<{type_name} {:?}>", ffi::lua_topointer(state, index))
}
}
}
+17 -29
View File
@@ -83,46 +83,34 @@ pub(crate) unsafe fn get_internal_userdata<T: TypeKey>(
// Internally uses 3 stack spaces, does not call checkstack.
#[inline]
pub(crate) unsafe fn push_userdata<T>(state: *mut ffi::lua_State, t: T, protect: bool) -> Result<()> {
#[cfg(not(feature = "luau"))]
let ud = if protect {
#[cfg(not(feature = "luau"))]
pub(crate) unsafe fn push_uninit_userdata<T>(state: *mut ffi::lua_State, protect: bool) -> Result<*mut T> {
if protect {
protect_lua!(state, 0, 1, |state| {
ffi::lua_newuserdata(state, std::mem::size_of::<T>()) as *mut T
})?
})
} else {
ffi::lua_newuserdata(state, std::mem::size_of::<T>()) as *mut T
};
Ok(ffi::lua_newuserdata(state, std::mem::size_of::<T>()) as *mut T)
}
}
// Internally uses 3 stack spaces, does not call checkstack.
#[inline]
pub(crate) unsafe fn push_userdata<T>(state: *mut ffi::lua_State, t: T, protect: bool) -> Result<*mut T> {
#[cfg(not(feature = "luau"))]
let ud_ptr = push_uninit_userdata(state, protect)?;
#[cfg(feature = "luau")]
let ud = if protect {
let ud_ptr = if protect {
protect_lua!(state, 0, 1, |state| { ffi::lua_newuserdata_t::<T>(state) })?
} else {
ffi::lua_newuserdata_t::<T>(state)
};
ptr::write(ud, t);
Ok(())
}
// Internally uses 3 stack spaces, does not call checkstack.
#[cfg(feature = "lua54")]
#[inline]
pub(crate) unsafe fn push_userdata_uv<T>(
state: *mut ffi::lua_State,
t: T,
nuvalue: c_int,
protect: bool,
) -> Result<()> {
let ud = if protect {
protect_lua!(state, 0, 1, |state| {
ffi::lua_newuserdatauv(state, std::mem::size_of::<T>(), nuvalue) as *mut T
})?
} else {
ffi::lua_newuserdatauv(state, std::mem::size_of::<T>(), nuvalue) as *mut T
};
ptr::write(ud, t);
Ok(())
ptr::write(ud_ptr, t);
Ok(ud_ptr)
}
#[inline]
#[track_caller]
pub(crate) unsafe fn get_userdata<T>(state: *mut ffi::lua_State, index: c_int) -> *mut T {
let ud = ffi::lua_touserdata(state, index) as *mut T;
mlua_debug_assert!(!ud.is_null(), "userdata pointer is null");
+100 -266
View File
@@ -1,21 +1,17 @@
use std::cell::RefCell;
use std::cmp::Ordering;
use std::collections::{vec_deque, HashSet, VecDeque};
use std::ops::{Deref, DerefMut};
use std::os::raw::{c_int, c_void};
use std::collections::HashSet;
use std::os::raw::c_void;
use std::string::String as StdString;
use std::sync::Arc;
use std::{fmt, mem, ptr, str};
use std::{fmt, ptr, str};
use num_traits::FromPrimitive;
use crate::error::{Error, Result};
use crate::function::Function;
use crate::state::{Lua, RawLua};
use crate::string::{BorrowedStr, String};
use crate::table::Table;
use crate::thread::Thread;
use crate::types::{Integer, LightUserData, Number, SubtypeId};
use crate::types::{Integer, LightUserData, Number, ValueRef};
use crate::userdata::AnyUserData;
use crate::util::{check_stack, StackGuard};
@@ -24,12 +20,14 @@ use {
crate::table::SerializableTable,
rustc_hash::FxHashSet,
serde::ser::{self, Serialize, Serializer},
std::{rc::Rc, result::Result as StdResult},
std::{cell::RefCell, rc::Rc, result::Result as StdResult},
};
/// A dynamically typed Lua value. The `String`, `Table`, `Function`, `Thread`, and `UserData`
/// variants contain handle types into the internal Lua state. It is a logic error to mix handle
/// types between separate `Lua` instances, and doing so will result in a panic.
/// A dynamically typed Lua value.
///
/// The non-primitive variants (eg. string/table/function/thread/userdata) contain handle types
/// into the internal Lua state. It is a logic error to mix handle types between separate
/// `Lua` instances, and doing so will result in a panic.
#[derive(Clone)]
pub enum Value {
/// The Lua value `nil`.
@@ -47,7 +45,7 @@ pub enum Value {
/// A Luau vector.
#[cfg(any(feature = "luau", doc))]
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
Vector(crate::types::Vector),
Vector(crate::Vector),
/// An interned string, managed by Lua.
///
/// Unlike Rust strings, Lua strings may not be valid UTF-8.
@@ -59,10 +57,18 @@ pub enum Value {
/// Reference to a Lua thread (or coroutine).
Thread(Thread),
/// Reference to a userdata object that holds a custom type which implements `UserData`.
///
/// Special builtin userdata types will be represented as other `Value` variants.
UserData(AnyUserData),
/// A Luau buffer.
#[cfg(any(feature = "luau", doc))]
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
Buffer(crate::Buffer),
/// `Error` is a special builtin userdata type. When received from Lua it is implicitly cloned.
Error(Box<Error>),
/// Any other value not known to mlua (eg. LuaJIT CData).
#[allow(private_interfaces)]
Other(ValueRef),
}
pub use self::Value::Nil;
@@ -74,7 +80,7 @@ impl Value {
pub const NULL: Value = Value::LightUserData(LightUserData(ptr::null_mut()));
/// Returns type name of this value.
pub const fn type_name(&self) -> &'static str {
pub fn type_name(&self) -> &'static str {
match *self {
Value::Nil => "nil",
Value::Boolean(_) => "boolean",
@@ -87,12 +93,11 @@ impl Value {
Value::Table(_) => "table",
Value::Function(_) => "function",
Value::Thread(_) => "thread",
Value::UserData(AnyUserData(_, SubtypeId::None)) => "userdata",
Value::UserData(_) => "userdata",
#[cfg(feature = "luau")]
Value::UserData(AnyUserData(_, SubtypeId::Buffer)) => "buffer",
#[cfg(feature = "luajit")]
Value::UserData(AnyUserData(_, SubtypeId::CData)) => "cdata",
Value::Buffer(_) => "buffer",
Value::Error(_) => "error",
Value::Other(_) => "other",
}
}
@@ -125,20 +130,36 @@ impl Value {
pub fn to_pointer(&self) -> *const c_void {
match self {
Value::LightUserData(ud) => ud.0,
Value::String(String(r))
| Value::Table(Table(r))
| Value::Function(Function(r))
| Value::Thread(Thread(r, ..))
| Value::UserData(AnyUserData(r, ..)) => r.to_pointer(),
Value::String(String(vref))
| Value::Table(Table(vref))
| Value::Function(Function(vref))
| Value::Thread(Thread(vref, ..))
| Value::UserData(AnyUserData(vref))
| Value::Other(vref) => vref.to_pointer(),
#[cfg(feature = "luau")]
Value::Buffer(crate::Buffer(vref)) => vref.to_pointer(),
_ => ptr::null(),
}
}
/// Converts the value to a string.
///
/// If the value has a metatable with a `__tostring` method, then it will be called to get the
/// result.
/// This might invoke the `__tostring` metamethod for non-primitive types (eg. tables,
/// functions).
pub fn to_string(&self) -> Result<StdString> {
unsafe fn invoke_to_string(vref: &ValueRef) -> Result<StdString> {
let lua = vref.lua.lock();
let state = lua.state();
let _guard = StackGuard::new(state);
check_stack(state, 3)?;
lua.push_ref(vref);
protect_lua!(state, 1, 1, fn(state) {
ffi::luaL_tolstring(state, -1, ptr::null_mut());
})?;
Ok(String(lua.pop_ref()).to_str()?.to_string())
}
match self {
Value::Nil => Ok("nil".to_string()),
Value::Boolean(b) => Ok(b.to_string()),
@@ -149,21 +170,13 @@ impl Value {
#[cfg(feature = "luau")]
Value::Vector(v) => Ok(v.to_string()),
Value::String(s) => Ok(s.to_str()?.to_string()),
Value::Table(Table(r))
| Value::Function(Function(r))
| Value::Thread(Thread(r, ..))
| Value::UserData(AnyUserData(r, ..)) => unsafe {
let lua = r.lua.lock();
let state = lua.state();
let _guard = StackGuard::new(state);
check_stack(state, 3)?;
lua.push_ref(r);
protect_lua!(state, 1, 1, fn(state) {
ffi::luaL_tolstring(state, -1, ptr::null_mut());
})?;
Ok(String(lua.pop_ref()).to_str()?.to_string())
},
Value::Table(Table(vref))
| Value::Function(Function(vref))
| Value::Thread(Thread(vref, ..))
| Value::UserData(AnyUserData(vref))
| Value::Other(vref) => unsafe { invoke_to_string(vref) },
#[cfg(feature = "luau")]
Value::Buffer(crate::Buffer(vref)) => unsafe { invoke_to_string(vref) },
Value::Error(err) => Ok(err.to_string()),
}
}
@@ -415,26 +428,41 @@ impl Value {
}
}
/// Returns `true` if the value is a Buffer wrapped in [`AnyUserData`].
/// Cast the value to a `Buffer`.
///
/// If the value is `Buffer`, returns it or `None` otherwise.
#[cfg(any(feature = "luau", doc))]
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
#[doc(hidden)]
#[inline]
pub fn is_buffer(&self) -> bool {
self.as_userdata()
.map(|ud| ud.1 == SubtypeId::Buffer)
.unwrap_or_default()
pub fn as_buffer(&self) -> Option<&crate::Buffer> {
match self {
Value::Buffer(b) => Some(b),
_ => None,
}
}
/// Returns `true` if the value is a CData wrapped in [`AnyUserData`].
#[cfg(any(feature = "luajit", doc))]
#[cfg_attr(docsrs, doc(cfg(feature = "luajit")))]
#[doc(hidden)]
/// Returns `true` if the value is a `Buffer`.
#[cfg(any(feature = "luau", doc))]
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
#[inline]
pub fn is_cdata(&self) -> bool {
self.as_userdata()
.map(|ud| ud.1 == SubtypeId::CData)
.unwrap_or_default()
pub fn is_buffer(&self) -> bool {
self.as_buffer().is_some()
}
/// Returns `true` if the value is an [`Error`].
#[inline]
pub fn is_error(&self) -> bool {
self.as_error().is_some()
}
/// Cast the value to [`Error`].
///
/// If the value is an [`Error`], returns it or `None` otherwise.
pub fn as_error(&self) -> Option<&Error> {
match self {
Value::Error(e) => Some(e),
_ => None,
}
}
/// Wrap reference to this Value into [`SerializableValue`].
@@ -449,7 +477,7 @@ impl Value {
// Compares two values.
// Used to sort values for Debug printing.
pub(crate) fn cmp(&self, other: &Self) -> Ordering {
pub(crate) fn sort_cmp(&self, other: &Self) -> Ordering {
fn cmp_num(a: Number, b: Number) -> Ordering {
match (a, b) {
_ if a < b => Ordering::Less,
@@ -478,11 +506,14 @@ impl Value {
(&Value::Number(a), &Value::Number(b)) => cmp_num(a, b),
(Value::Integer(_) | Value::Number(_), _) => Ordering::Less,
(_, Value::Integer(_) | Value::Number(_)) => Ordering::Greater,
// Vector (Luau)
#[cfg(feature = "luau")]
(Value::Vector(a), Value::Vector(b)) => a.partial_cmp(b).unwrap_or(Ordering::Equal),
// String
(Value::String(a), Value::String(b)) => a.as_bytes().cmp(&b.as_bytes()),
(Value::String(_), _) => Ordering::Less,
(_, Value::String(_)) => Ordering::Greater,
// Other variants can be randomly ordered
// Other variants can be ordered by their pointer
(a, b) => a.to_pointer().cmp(&b.to_pointer()),
}
}
@@ -519,8 +550,11 @@ impl Value {
.unwrap_or_else(|| format!("userdata: {:?}", u.to_pointer()));
write!(fmt, "{s}")
}
#[cfg(feature = "luau")]
buf @ Value::Buffer(_) => write!(fmt, "buffer: {:?}", buf.to_pointer()),
Value::Error(e) if recursive => write!(fmt, "{e:?}"),
Value::Error(_) => write!(fmt, "error"),
Value::Other(v) => write!(fmt, "other: {:?}", v.to_pointer()),
}
}
}
@@ -530,6 +564,7 @@ impl fmt::Debug for Value {
if fmt.alternate() {
return self.fmt_pretty(fmt, true, 0, &mut HashSet::new());
}
match self {
Value::Nil => write!(fmt, "Nil"),
Value::Boolean(b) => write!(fmt, "Boolean({b})"),
@@ -543,7 +578,10 @@ impl fmt::Debug for Value {
Value::Function(f) => write!(fmt, "{f:?}"),
Value::Thread(t) => write!(fmt, "{t:?}"),
Value::UserData(ud) => write!(fmt, "{ud:?}"),
#[cfg(feature = "luau")]
Value::Buffer(buf) => write!(fmt, "{buf:?}"),
Value::Error(e) => write!(fmt, "Error({e:?})"),
Value::Other(v) => write!(fmt, "Other({v:?})"),
}
}
}
@@ -565,6 +603,8 @@ impl PartialEq for Value {
(Value::Function(a), Value::Function(b)) => a == b,
(Value::Thread(a), Value::Thread(b)) => a == b,
(Value::UserData(a), Value::UserData(b)) => a == b,
#[cfg(feature = "luau")]
(Value::Buffer(a), Value::Buffer(b)) => a == b,
_ => false,
}
}
@@ -673,11 +713,14 @@ impl<'a> Serialize for SerializableValue<'a> {
Value::UserData(ud) if ud.is_serializable() || self.options.deny_unsupported_types => {
ud.serialize(serializer)
}
#[cfg(feature = "luau")]
Value::Buffer(buf) => buf.serialize(serializer),
Value::Function(_)
| Value::Thread(_)
| Value::UserData(_)
| Value::LightUserData(_)
| Value::Error(_) => {
| Value::Error(_)
| Value::Other(_) => {
if self.options.deny_unsupported_types {
let msg = format!("cannot serialize <{}>", self.value.type_name());
Err(ser::Error::custom(msg))
@@ -689,221 +732,12 @@ impl<'a> Serialize for SerializableValue<'a> {
}
}
/// Trait for types convertible to `Value`.
pub trait IntoLua: Sized {
/// Performs the conversion.
fn into_lua(self, lua: &Lua) -> Result<Value>;
/// Pushes the value into the Lua stack.
///
/// # Safety
/// This method does not check Lua stack space.
#[doc(hidden)]
#[inline]
unsafe fn push_into_stack(self, lua: &RawLua) -> Result<()> {
lua.push_value(&self.into_lua(lua.lua())?)
}
}
/// Trait for types convertible from `Value`.
pub trait FromLua: Sized {
/// Performs the conversion.
fn from_lua(value: Value, lua: &Lua) -> Result<Self>;
/// Performs the conversion for an argument (eg. function argument).
///
/// `i` is the argument index (position),
/// `to` is a function name that received the argument.
#[doc(hidden)]
#[inline]
fn from_lua_arg(arg: Value, i: usize, to: Option<&str>, lua: &Lua) -> Result<Self> {
Self::from_lua(arg, lua).map_err(|err| Error::BadArgument {
to: to.map(|s| s.to_string()),
pos: i,
name: None,
cause: Arc::new(err),
})
}
/// Performs the conversion for a value in the Lua stack at index `idx`.
#[doc(hidden)]
#[inline]
unsafe fn from_stack(idx: c_int, lua: &RawLua) -> Result<Self> {
Self::from_lua(lua.stack_value(idx, None), lua.lua())
}
/// Same as `from_lua_arg` but for a value in the Lua stack at index `idx`.
#[doc(hidden)]
#[inline]
unsafe fn from_stack_arg(idx: c_int, i: usize, to: Option<&str>, lua: &RawLua) -> Result<Self> {
Self::from_stack(idx, lua).map_err(|err| Error::BadArgument {
to: to.map(|s| s.to_string()),
pos: i,
name: None,
cause: Arc::new(err),
})
}
}
/// Multiple Lua values used for both argument passing and also for multiple return values.
#[derive(Default, Debug, Clone)]
pub struct MultiValue(VecDeque<Value>);
impl Deref for MultiValue {
type Target = VecDeque<Value>;
#[inline]
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl DerefMut for MultiValue {
#[inline]
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
impl MultiValue {
/// Creates an empty `MultiValue` containing no values.
#[inline]
pub const fn new() -> MultiValue {
MultiValue(VecDeque::new())
}
/// Creates an empty `MultiValue` container with space for at least `capacity` elements.
pub fn with_capacity(capacity: usize) -> MultiValue {
MultiValue(VecDeque::with_capacity(capacity))
}
#[inline]
pub(crate) fn from_lua_iter<T: IntoLua>(lua: &Lua, iter: impl IntoIterator<Item = T>) -> Result<Self> {
let iter = iter.into_iter();
let mut multi_value = MultiValue::with_capacity(iter.size_hint().0);
for value in iter {
multi_value.push_back(value.into_lua(lua)?);
}
Ok(multi_value)
}
}
impl FromIterator<Value> for MultiValue {
#[inline]
fn from_iter<I: IntoIterator<Item = Value>>(iter: I) -> Self {
let mut multi_value = MultiValue::new();
multi_value.extend(iter);
multi_value
}
}
impl IntoIterator for MultiValue {
type Item = Value;
type IntoIter = vec_deque::IntoIter<Value>;
#[inline]
fn into_iter(mut self) -> Self::IntoIter {
let deque = mem::take(&mut self.0);
mem::forget(self);
deque.into_iter()
}
}
impl<'a> IntoIterator for &'a MultiValue {
type Item = &'a Value;
type IntoIter = vec_deque::Iter<'a, Value>;
#[inline]
fn into_iter(self) -> Self::IntoIter {
self.0.iter()
}
}
/// Trait for types convertible to any number of Lua values.
///
/// This is a generalization of `IntoLua`, allowing any number of resulting Lua values instead of
/// just one. Any type that implements `IntoLua` will automatically implement this trait.
pub trait IntoLuaMulti: Sized {
/// Performs the conversion.
fn into_lua_multi(self, lua: &Lua) -> Result<MultiValue>;
/// Pushes the values into the Lua stack.
///
/// Returns number of pushed values.
#[doc(hidden)]
#[inline]
unsafe fn push_into_stack_multi(self, lua: &RawLua) -> Result<c_int> {
let values = self.into_lua_multi(lua.lua())?;
let len: c_int = values.len().try_into().unwrap();
unsafe {
check_stack(lua.state(), len + 1)?;
for val in &values {
lua.push_value(val)?;
}
}
Ok(len)
}
}
/// Trait for types that can be created from an arbitrary number of Lua values.
///
/// This is a generalization of `FromLua`, allowing an arbitrary number of Lua values to participate
/// in the conversion. Any type that implements `FromLua` will automatically implement this trait.
pub trait FromLuaMulti: Sized {
/// Performs the conversion.
///
/// In case `values` contains more values than needed to perform the conversion, the excess
/// values should be ignored. This reflects the semantics of Lua when calling a function or
/// assigning values. Similarly, if not enough values are given, conversions should assume that
/// any missing values are nil.
fn from_lua_multi(values: MultiValue, lua: &Lua) -> Result<Self>;
/// Performs the conversion for a list of arguments.
///
/// `i` is an index (position) of the first argument,
/// `to` is a function name that received the arguments.
#[doc(hidden)]
#[inline]
fn from_lua_args(args: MultiValue, i: usize, to: Option<&str>, lua: &Lua) -> Result<Self> {
let _ = (i, to);
Self::from_lua_multi(args, lua)
}
/// Performs the conversion for a number of values in the Lua stack.
#[doc(hidden)]
#[inline]
unsafe fn from_stack_multi(nvals: c_int, lua: &RawLua) -> Result<Self> {
let mut values = MultiValue::with_capacity(nvals as usize);
for idx in 0..nvals {
values.push_back(lua.stack_value(-nvals + idx, None));
}
if nvals > 0 {
// It's safe to clear the stack as all references moved to ref thread
ffi::lua_pop(lua.state(), nvals);
}
Self::from_lua_multi(values, lua.lua())
}
/// Same as `from_lua_args` but for a number of values in the Lua stack.
#[doc(hidden)]
#[inline]
unsafe fn from_stack_args(nargs: c_int, i: usize, to: Option<&str>, lua: &RawLua) -> Result<Self> {
let _ = (i, to);
Self::from_stack_multi(nargs, lua)
}
}
#[cfg(test)]
mod assertions {
use super::*;
#[cfg(not(feature = "send"))]
static_assertions::assert_not_impl_any!(Value: Send);
#[cfg(not(feature = "send"))]
static_assertions::assert_not_impl_any!(MultiValue: Send);
#[cfg(feature = "send")]
static_assertions::assert_impl_all!(Value: Send, Sync);
#[cfg(feature = "send")]
static_assertions::assert_impl_all!(MultiValue: Send, Sync);
}
+15 -3
View File
@@ -1,8 +1,20 @@
[lua54_coverage]
features = "lua54,vendored,async,serialize,macros"
features = "lua54,vendored,async,send,serialize,macros"
[lua54_with_memory_limit_coverage]
features = "lua54,vendored,async,send,serialize,macros"
rustflags = "--cfg force_memory_limit"
[lua51_coverage]
features = "lua51,vendored,async,serialize,macros"
features = "lua51,vendored,async,send,serialize,macros"
[lua51_with_memory_limit_coverage]
features = "lua51,vendored,async,send,serialize,macros"
rustflags = "--cfg force_memory_limit"
[luau_coverage]
features = "luau,async,serialize,macros"
features = "luau,async,send,serialize,macros"
[luau_with_memory_limit_coverage]
features = "luau,async,send,serialize,macros"
rustflags = "--cfg force_memory_limit"
+29 -2
View File
@@ -1,5 +1,6 @@
#![cfg(feature = "async")]
use std::string::String as StdString;
use std::sync::Arc;
use std::time::Duration;
@@ -39,15 +40,41 @@ async fn test_async_function() -> Result<()> {
async fn test_async_function_wrap() -> Result<()> {
let lua = Lua::new();
let f = Function::wrap_async(|_, s: String| async move { Ok(s) });
let f = Function::wrap_async(|s: StdString| async move {
tokio::task::yield_now().await;
Ok(s)
});
lua.globals().set("f", f)?;
let res: String = lua.load(r#"f("hello")"#).eval_async().await?;
assert_eq!(res, "hello");
Ok(())
}
#[tokio::test]
async fn test_async_function_wrap_raw() -> Result<()> {
let lua = Lua::new();
let f = Function::wrap_raw_async(|s: StdString| async move {
tokio::task::yield_now().await;
s
});
lua.globals().set("f", f)?;
let res: String = lua.load(r#"f("hello")"#).eval_async().await?;
assert_eq!(res, "hello");
// Return error
let ferr = Function::wrap_raw_async(|| async move {
tokio::task::yield_now().await;
Err::<(), _>("some error")
});
lua.globals().set("ferr", ferr)?;
let (_, err): (Value, String) = lua.load(r#"ferr()"#).eval_async().await?;
assert_eq!(err, "some error");
Ok(())
}
#[tokio::test]
async fn test_async_sleep() -> Result<()> {
let lua = Lua::new();
+56
View File
@@ -0,0 +1,56 @@
#![cfg(feature = "luau")]
use mlua::{Lua, Result, Value};
#[test]
fn test_buffer() -> Result<()> {
let lua = Lua::new();
let buf1 = lua
.load(
r#"
local buf = buffer.fromstring("hello")
assert(buffer.len(buf) == 5)
return buf
"#,
)
.eval::<Value>()?;
assert!(buf1.is_buffer());
assert_eq!(buf1.type_name(), "buffer");
let buf2 = lua.load("buffer.fromstring('hello')").eval::<Value>()?;
assert_ne!(buf1, buf2);
// Check that we can pass buffer type to Lua
let buf1 = buf1.as_buffer().unwrap();
let func = lua.create_function(|_, buf: Value| return buf.to_string())?;
assert!(func.call::<String>(buf1)?.starts_with("buffer:"));
// Check buffer methods
assert_eq!(buf1.len(), 5);
assert_eq!(buf1.to_vec(), b"hello");
assert_eq!(buf1.read_bytes::<3>(1), [b'e', b'l', b'l']);
buf1.write_bytes(1, b"i");
assert_eq!(buf1.to_vec(), b"hillo");
let buf3 = lua.create_buffer(b"")?;
assert!(buf3.is_empty());
Ok(())
}
#[test]
#[should_panic(expected = "range end index 14 out of range for slice of length 13")]
fn test_buffer_out_of_bounds_read() {
let lua = Lua::new();
let buf = lua.create_buffer(b"hello, world!").unwrap();
_ = buf.read_bytes::<1>(13);
}
#[test]
#[should_panic(expected = "range end index 16 out of range for slice of length 13")]
fn test_buffer_out_of_bounds_write() {
let lua = Lua::new();
let buf = lua.create_buffer(b"hello, world!").unwrap();
buf.write_bytes(14, b"!!");
}
+1 -1
View File
@@ -42,7 +42,7 @@ fn test_chunk_macro() -> Result<()> {
data.raw_set("num", 1)?;
let ud = mlua::AnyUserData::wrap("hello");
let f = mlua::Function::wrap(|_lua, ()| Ok(()));
let f = mlua::Function::wrap(|| Ok(()));
lua.globals().set("g", 123)?;
-3
View File
@@ -7,8 +7,6 @@ fn test_compilation() {
t.compile_fail("tests/compile/lua_norefunwindsafe.rs");
t.compile_fail("tests/compile/ref_nounwindsafe.rs");
t.compile_fail("tests/compile/scope_callback_capture.rs");
t.compile_fail("tests/compile/scope_callback_inner.rs");
t.compile_fail("tests/compile/scope_callback_outer.rs");
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");
@@ -17,7 +15,6 @@ fn test_compilation() {
{
t.compile_fail("tests/compile/async_any_userdata_method.rs");
t.compile_fail("tests/compile/async_nonstatic_userdata.rs");
t.compile_fail("tests/compile/async_userdata_method.rs");
}
#[cfg(feature = "send")]
+5 -4
View File
@@ -1,4 +1,4 @@
use mlua::{UserDataMethods, Lua};
use mlua::{Lua, UserDataMethods};
fn main() {
let lua = Lua::new();
@@ -6,9 +6,10 @@ fn main() {
lua.register_userdata_type::<String>(|reg| {
let s = String::new();
let mut s = &s;
reg.add_async_method("t", |_, this: &String, ()| async {
s = this;
reg.add_async_method("t", |_, this, ()| async {
s = &*this;
Ok(())
});
}).unwrap();
})
.unwrap();
}
+42 -42
View File
@@ -1,20 +1,42 @@
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
--> tests/compile/async_any_userdata_method.rs:9:49
|
9 | reg.add_async_method("t", |_, this: &String, ()| async {
| ^^^^^ cannot borrow as mutable
10 | s = this;
9 | reg.add_async_method("t", |_, this, ()| 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
error[E0373]: async block may outlive the current function, but it borrows `this`, which is owned by the current function
--> tests/compile/async_any_userdata_method.rs:9:49
|
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: 9:63}` contains a lifetime `'2`
9 | reg.add_async_method("t", |_, this, ()| async {
| ^^^^^ may outlive borrowed value `this`
10 | s = &*this;
| ---- `this` is borrowed here
|
note: async block is returned here
--> tests/compile/async_any_userdata_method.rs:9:49
|
9 | reg.add_async_method("t", |_, this, ()| async {
| _________________________________________________^
10 | | s = &*this;
11 | | Ok(())
12 | | });
| |_________^
help: to force the async block to take ownership of `this` (and any other referenced variables), use the `move` keyword
|
9 | reg.add_async_method("t", |_, this, ()| async move {
| ++++
error: lifetime may not live long enough
--> tests/compile/async_any_userdata_method.rs:9:49
|
9 | reg.add_async_method("t", |_, this, ()| async {
| ___________________________________-------------_^
| | | |
| | | return type of closure `{async block@$DIR/tests/compile/async_any_userdata_method.rs:9:49: 9:54}` contains a lifetime `'2`
| | lifetime `'1` represents this closure's body
10 | | s = this;
10 | | s = &*this;
11 | | Ok(())
12 | | });
| |_________^ returning this value requires that `'1` must outlive `'2`
@@ -28,53 +50,31 @@ error[E0597]: `s` does not live long enough
| - binding `s` declared here
8 | let mut s = &s;
| ^^ borrowed value does not live long enough
9 | / reg.add_async_method("t", |_, this: &String, ()| async {
10 | | s = this;
9 | / reg.add_async_method("t", |_, this, ()| async {
10 | | s = &*this;
11 | | Ok(())
12 | | });
| |__________- argument requires that `s` is borrowed for `'static`
13 | }).unwrap();
13 | })
| - `s` dropped here while still borrowed
error[E0521]: borrowed data escapes outside of closure
--> tests/compile/async_any_userdata_method.rs:9:9
|
6 | lua.register_userdata_type::<String>(|reg| {
| ---
| |
| `reg` is a reference that is only valid in the closure body
| has type `&mut LuaUserDataRegistry<'1, std::string::String>`
...
9 | / reg.add_async_method("t", |_, this: &String, ()| async {
10 | | s = this;
11 | | Ok(())
12 | | });
| | ^
| | |
| |__________`reg` escapes the closure body here
| argument requires that `'1` must outlive `'static`
|
= note: requirement occurs because of a mutable reference to `LuaUserDataRegistry<'_, std::string::String>`
= note: mutable references are invariant over their type parameter
= help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance
error[E0373]: closure may outlive the current function, but it borrows `s`, which is owned by the current function
--> tests/compile/async_any_userdata_method.rs:9:35
|
9 | reg.add_async_method("t", |_, this: &String, ()| async {
| ^^^^^^^^^^^^^^^^^^^^^^ may outlive borrowed value `s`
10 | s = this;
9 | reg.add_async_method("t", |_, this, ()| async {
| ^^^^^^^^^^^^^ may outlive borrowed value `s`
10 | s = &*this;
| - `s` is borrowed here
|
note: function requires argument type to outlive `'static`
--> tests/compile/async_any_userdata_method.rs:9:9
|
9 | / reg.add_async_method("t", |_, this: &String, ()| async {
10 | | s = this;
9 | / reg.add_async_method("t", |_, this, ()| async {
10 | | s = &*this;
11 | | Ok(())
12 | | });
| |__________^
help: to force the closure to take ownership of `s` (and any other referenced variables), use the `move` keyword
|
9 | reg.add_async_method("t", move |_, this: &String, ()| async {
9 | reg.add_async_method("t", move |_, this, ()| async {
| ++++
+2 -2
View File
@@ -4,8 +4,8 @@ fn main() {
#[derive(Clone)]
struct MyUserData<'a>(&'a i64);
impl<'a> UserData for MyUserData<'a> {
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
impl UserData for MyUserData<'_> {
fn add_methods<M: UserDataMethods<Self>>(methods: &mut M) {
methods.add_async_method("print", |_, data, ()| async move {
println!("{}", data.0);
Ok(())
@@ -1,11 +1,11 @@
error: lifetime may not live long enough
--> tests/compile/async_nonstatic_userdata.rs:9:13
|
7 | impl<'a> UserData for MyUserData<'a> {
| -- lifetime `'a` defined here
8 | fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
7 | impl UserData for MyUserData<'_> {
| -- lifetime `'1` appears in the `impl`'s self type
8 | fn add_methods<M: UserDataMethods<Self>>(methods: &mut M) {
9 | / methods.add_async_method("print", |_, data, ()| async move {
10 | | println!("{}", data.0);
11 | | Ok(())
12 | | });
| |______________^ requires that `'a` must outlive `'static`
| |______________^ requires that `'1` must outlive `'static`
-14
View File
@@ -1,14 +0,0 @@
use mlua::{UserData, UserDataMethods};
struct MyUserData;
impl UserData for MyUserData {
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
methods.add_async_method("method", |_, this: &'static Self, ()| async {
Ok(())
});
// ^ lifetime may not live long enough
}
}
fn main() {}
@@ -1,17 +0,0 @@
warning: unused variable: `this`
--> tests/compile/async_userdata_method.rs:7:48
|
7 | methods.add_async_method("method", |_, this: &'static Self, ()| async {
| ^^^^ help: if this is intentional, prefix it with an underscore: `_this`
|
= note: `#[warn(unused_variables)]` on by default
error: lifetime may not live long enough
--> tests/compile/async_userdata_method.rs:7:9
|
6 | fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
| ---- lifetime `'lua` defined here
7 | / methods.add_async_method("method", |_, this: &'static Self, ()| async {
8 | | Ok(())
9 | | });
| |__________^ argument requires that `'lua` must outlive `'static`
+1 -3
View File
@@ -6,7 +6,5 @@ fn main() {
let test = Test(0);
let lua = Lua::new();
let _ = lua.create_function(|_, ()| -> Result<i32> {
Ok(test.0)
});
let _ = lua.create_function(|_, ()| -> Result<i32> { Ok(test.0) });
}
+14 -17
View File
@@ -1,20 +1,17 @@
error[E0373]: closure may outlive the current function, but it borrows `test.0`, which is owned by the current function
--> tests/compile/function_borrow.rs:9:33
|
9 | let _ = lua.create_function(|_, ()| -> Result<i32> {
| ^^^^^^^^^^^^^^^^^^^^^^ may outlive borrowed value `test.0`
10 | Ok(test.0)
| ------ `test.0` is borrowed here
|
--> tests/compile/function_borrow.rs:9:33
|
9 | let _ = lua.create_function(|_, ()| -> Result<i32> { Ok(test.0) });
| ^^^^^^^^^^^^^^^^^^^^^^ ------ `test.0` is borrowed here
| |
| may outlive borrowed value `test.0`
|
note: function requires argument type to outlive `'static`
--> tests/compile/function_borrow.rs:9:13
|
9 | let _ = lua.create_function(|_, ()| -> Result<i32> {
| _____________^
10 | | Ok(test.0)
11 | | });
| |______^
--> tests/compile/function_borrow.rs:9:13
|
9 | let _ = lua.create_function(|_, ()| -> Result<i32> { Ok(test.0) });
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
help: to force the closure to take ownership of `test.0` (and any other referenced variables), use the `move` keyword
|
9 | let _ = lua.create_function(move |_, ()| -> Result<i32> {
| ++++
|
9 | let _ = lua.create_function(move |_, ()| -> Result<i32> { Ok(test.0) });
| ++++
+53 -31
View File
@@ -1,32 +1,36 @@
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
error[E0277]: the type `UnsafeCell<mlua::state::raw::RawLua>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
--> tests/compile/lua_norefunwindsafe.rs:7:18
|
7 | catch_unwind(|| lua.create_table().unwrap());
| ------------ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `UnsafeCell<*mut lua_State>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
| ------------ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `UnsafeCell<mlua::state::raw::RawLua>` 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 `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
= help: within `Lua`, the trait `RefUnwindSafe` is not implemented for `UnsafeCell<mlua::state::raw::RawLua>`, which is required by `{closure@$DIR/tests/compile/lua_norefunwindsafe.rs:7:18: 7:20}: UnwindSafe`
note: required because it appears within the type `lock_api::remutex::ReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId, mlua::state::raw::RawLua>`
--> $CARGO/lock_api-0.4.12/src/remutex.rs
|
| pub struct Cell<T: ?Sized> {
| ^^^^
note: required because it appears within the type `mlua::state::raw::RawLua`
--> src/state/raw.rs
| pub struct ReentrantMutex<R, G, T: ?Sized> {
| ^^^^^^^^^^^^^^
note: required because it appears within the type `alloc::sync::ArcInner<lock_api::remutex::ReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId, mlua::state::raw::RawLua>>`
--> $RUST/alloc/src/sync.rs
|
| pub struct RawLua {
| ^^^^^^
note: required because it appears within the type `mlua::types::sync::inner::ReentrantMutex<mlua::state::raw::RawLua>`
--> src/types/sync.rs
| struct ArcInner<T: ?Sized> {
| ^^^^^^^^
note: required because it appears within the type `PhantomData<alloc::sync::ArcInner<lock_api::remutex::ReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId, mlua::state::raw::RawLua>>>`
--> $RUST/core/src/marker.rs
|
| pub(crate) struct ReentrantMutex<T>(T);
| ^^^^^^^^^^^^^^
= note: required for `Rc<mlua::types::sync::inner::ReentrantMutex<mlua::state::raw::RawLua>>` to implement `RefUnwindSafe`
| pub struct PhantomData<T: ?Sized>;
| ^^^^^^^^^^^
note: required because it appears within the type `Arc<lock_api::remutex::ReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId, mlua::state::raw::RawLua>>`
--> $RUST/alloc/src/sync.rs
|
| pub struct Arc<
| ^^^
note: required because it appears within the type `Lua`
--> src/state.rs
|
| pub struct Lua(XRc<ReentrantMutex<RawLua>>);
| pub struct Lua {
| ^^^
= note: required for `&Lua` to implement `UnwindSafe`
note: required because it's used within this closure
@@ -40,31 +44,49 @@ note: required by a bound in `std::panic::catch_unwind`
| pub fn catch_unwind<F: FnOnce() -> R + UnwindSafe, R>(f: F) -> Result<R> {
| ^^^^^^^^^^ required by this bound in `catch_unwind`
error[E0277]: the type `UnsafeCell<mlua::state::extra::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/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
| ------------ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `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: 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
= help: within `Lua`, the trait `RefUnwindSafe` is not implemented for `UnsafeCell<usize>`, which is required by `{closure@$DIR/tests/compile/lua_norefunwindsafe.rs:7:18: 7:20}: UnwindSafe`
note: required because it appears within the type `Cell<usize>`
--> $RUST/core/src/cell.rs
|
| 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 Cell<T: ?Sized> {
| ^^^^
note: required because it appears within the type `lock_api::remutex::RawReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId>`
--> $CARGO/lock_api-0.4.12/src/remutex.rs
|
| pub(crate) struct ReentrantMutex<T>(T);
| ^^^^^^^^^^^^^^
= note: required for `Rc<mlua::types::sync::inner::ReentrantMutex<mlua::state::raw::RawLua>>` to implement `RefUnwindSafe`
| pub struct RawReentrantMutex<R, G> {
| ^^^^^^^^^^^^^^^^^
note: required because it appears within the type `lock_api::remutex::ReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId, mlua::state::raw::RawLua>`
--> $CARGO/lock_api-0.4.12/src/remutex.rs
|
| pub struct ReentrantMutex<R, G, T: ?Sized> {
| ^^^^^^^^^^^^^^
note: required because it appears within the type `alloc::sync::ArcInner<lock_api::remutex::ReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId, mlua::state::raw::RawLua>>`
--> $RUST/alloc/src/sync.rs
|
| struct ArcInner<T: ?Sized> {
| ^^^^^^^^
note: required because it appears within the type `PhantomData<alloc::sync::ArcInner<lock_api::remutex::ReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId, mlua::state::raw::RawLua>>>`
--> $RUST/core/src/marker.rs
|
| pub struct PhantomData<T: ?Sized>;
| ^^^^^^^^^^^
note: required because it appears within the type `Arc<lock_api::remutex::ReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId, mlua::state::raw::RawLua>>`
--> $RUST/alloc/src/sync.rs
|
| pub struct Arc<
| ^^^
note: required because it appears within the type `Lua`
--> src/state.rs
|
| pub struct Lua(XRc<ReentrantMutex<RawLua>>);
| pub struct Lua {
| ^^^
= note: required for `&Lua` to implement `UnwindSafe`
note: required because it's used within this closure
+2 -4
View File
@@ -8,10 +8,8 @@ fn main() -> Result<()> {
let data = Rc::new(Cell::new(0));
lua.create_function(move |_, ()| {
Ok(data.get())
})?
.call::<i32>(())?;
lua.create_function(move |_, ()| Ok(data.get()))?
.call::<i32>(())?;
Ok(())
}
+14 -17
View File
@@ -1,28 +1,25 @@
error[E0277]: `Rc<Cell<i32>>` cannot be sent between threads safely
--> tests/compile/non_send.rs:11:25
|
11 | lua.create_function(move |_, ()| {
| --------------- ^-----------
| | |
| _________|_______________within this `{closure@$DIR/tests/compile/non_send.rs:11:25: 11:37}`
| | |
| | required by a bound introduced by this call
12 | | Ok(data.get())
13 | | })?
| |_____^ `Rc<Cell<i32>>` cannot be sent between threads safely
11 | lua.create_function(move |_, ()| Ok(data.get()))?
| --------------- ------------^^^^^^^^^^^^^^^
| | |
| | `Rc<Cell<i32>>` cannot be sent between threads safely
| | within this `{closure@$DIR/tests/compile/non_send.rs:11:25: 11:37}`
| required by a bound introduced by this call
|
= help: within `{closure@$DIR/tests/compile/non_send.rs:11:25: 11:37}`, the trait `Send` is not implemented for `Rc<Cell<i32>>`
= help: within `{closure@$DIR/tests/compile/non_send.rs:11:25: 11:37}`, the trait `Send` is not implemented for `Rc<Cell<i32>>`, which is required by `{closure@$DIR/tests/compile/non_send.rs:11:25: 11:37}: MaybeSend`
note: required because it's used within this closure
--> tests/compile/non_send.rs:11:25
|
11 | lua.create_function(move |_, ()| {
11 | lua.create_function(move |_, ()| Ok(data.get()))?
| ^^^^^^^^^^^^
= note: required for `{closure@$DIR/tests/compile/non_send.rs:11:25: 11:37}` to implement `mlua::types::MaybeSend`
= note: required for `{closure@$DIR/tests/compile/non_send.rs:11:25: 11:37}` to implement `MaybeSend`
note: required by a bound in `Lua::create_function`
--> src/lua.rs
--> src/state.rs
|
| pub fn create_function<'lua, A, R, F>(&'lua self, func: F) -> Result<Function<'lua>>
| pub fn create_function<F, A, R>(&self, func: F) -> Result<Function>
| --------------- required by a bound in this associated function
...
| F: Fn(&'lua Lua, A) -> Result<R> + MaybeSend + 'static,
| ^^^^^^^^^ required by this bound in `Lua::create_function`
| where
| F: Fn(&Lua, A) -> Result<R> + MaybeSend + 'static,
| ^^^^^^^^^ required by this bound in `Lua::create_function`
+69 -126
View File
@@ -1,3 +1,54 @@
error[E0277]: the type `UnsafeCell<mlua::state::raw::RawLua>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
--> tests/compile/ref_nounwindsafe.rs:8:18
|
8 | catch_unwind(move || table.set("a", "b").unwrap());
| ------------ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `UnsafeCell<mlua::state::raw::RawLua>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
| |
| required by a bound introduced by this call
|
= help: within `alloc::sync::ArcInner<lock_api::remutex::ReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId, mlua::state::raw::RawLua>>`, the trait `RefUnwindSafe` is not implemented for `UnsafeCell<mlua::state::raw::RawLua>`, which is required by `{closure@$DIR/tests/compile/ref_nounwindsafe.rs:8:18: 8:25}: UnwindSafe`
note: required because it appears within the type `lock_api::remutex::ReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId, mlua::state::raw::RawLua>`
--> $CARGO/lock_api-0.4.12/src/remutex.rs
|
| pub struct ReentrantMutex<R, G, T: ?Sized> {
| ^^^^^^^^^^^^^^
note: required because it appears within the type `alloc::sync::ArcInner<lock_api::remutex::ReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId, mlua::state::raw::RawLua>>`
--> $RUST/alloc/src/sync.rs
|
| struct ArcInner<T: ?Sized> {
| ^^^^^^^^
= note: required for `NonNull<alloc::sync::ArcInner<lock_api::remutex::ReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId, mlua::state::raw::RawLua>>>` to implement `UnwindSafe`
note: required because it appears within the type `std::sync::Weak<lock_api::remutex::ReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId, mlua::state::raw::RawLua>>`
--> $RUST/alloc/src/sync.rs
|
| 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<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
|
@@ -6,138 +57,30 @@ error[E0277]: the type `UnsafeCell<usize>` may contain interior mutability and a
| |
| 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<usize>`, which is required by `{closure@$DIR/tests/compile/ref_nounwindsafe.rs:8:18: 8:25}: UnwindSafe`
= help: within `alloc::sync::ArcInner<lock_api::remutex::ReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId, mlua::state::raw::RawLua>>`, the trait `RefUnwindSafe` is not implemented for `UnsafeCell<usize>`, which is required by `{closure@$DIR/tests/compile/ref_nounwindsafe.rs:8:18: 8:25}: UnwindSafe`
note: required because it appears within the type `Cell<usize>`
--> $RUST/core/src/cell.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
note: required because it appears within the type `lock_api::remutex::RawReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId>`
--> $CARGO/lock_api-0.4.12/src/remutex.rs
|
| 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<*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 RawReentrantMutex<R, G> {
| ^^^^^^^^^^^^^^^^^
note: required because it appears within the type `lock_api::remutex::ReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId, mlua::state::raw::RawLua>`
--> $CARGO/lock_api-0.4.12/src/remutex.rs
|
| pub struct ReentrantMutex<R, G, T: ?Sized> {
| ^^^^^^^^^^^^^^
note: required because it appears within the type `alloc::sync::ArcInner<lock_api::remutex::ReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId, mlua::state::raw::RawLua>>`
--> $RUST/alloc/src/sync.rs
|
| struct ArcInner<T: ?Sized> {
| ^^^^^^^^
= note: required for `NonNull<alloc::sync::ArcInner<lock_api::remutex::ReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId, mlua::state::raw::RawLua>>>` to implement `UnwindSafe`
note: required because it appears within the type `std::sync::Weak<lock_api::remutex::ReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId, mlua::state::raw::RawLua>>`
--> $RUST/alloc/src/sync.rs
|
| pub struct Weak<
| ^^^^
+4 -8
View File
@@ -4,14 +4,10 @@ fn main() {
let lua = Lua::new();
lua.scope(|scope| {
let mut inner: Option<Table> = None;
let f = scope
.create_function_mut(move |_, t: Table| {
if let Some(old) = inner.take() {
// Access old callback `Lua`.
}
inner = Some(t);
Ok(())
})?;
let f = scope.create_function_mut(|_, t: Table| {
inner = Some(t);
Ok(())
})?;
f.call::<()>(lua.create_table()?)?;
Ok(())
});
+24 -5
View File
@@ -1,5 +1,24 @@
error[E0599]: no method named `scope` found for struct `Lua` in the current scope
--> tests/compile/scope_callback_capture.rs:5:9
|
5 | lua.scope(|scope| {
| ----^^^^^ method not found in `Lua`
error[E0373]: closure may outlive the current function, but it borrows `inner`, which is owned by the current function
--> tests/compile/scope_callback_capture.rs:7:43
|
5 | lua.scope(|scope| {
| ----- has type `&'1 mut mlua::scope::Scope<'1, '_>`
6 | let mut inner: Option<Table> = None;
7 | let f = scope.create_function_mut(|_, t: Table| {
| ^^^^^^^^^^^^^ may outlive borrowed value `inner`
8 | inner = Some(t);
| ----- `inner` is borrowed here
|
note: function requires argument type to outlive `'1`
--> tests/compile/scope_callback_capture.rs:7:17
|
7 | let f = scope.create_function_mut(|_, t: Table| {
| _________________^
8 | | inner = Some(t);
9 | | Ok(())
10 | | })?;
| |__________^
help: to force the closure to take ownership of `inner` (and any other referenced variables), use the `move` keyword
|
7 | let f = scope.create_function_mut(move |_, t: Table| {
| ++++
-15
View File
@@ -1,15 +0,0 @@
use mlua::{Lua, Table};
fn main() {
let lua = Lua::new();
lua.scope(|scope| {
let mut inner: Option<Table> = None;
let f = scope
.create_function_mut(|_, t: Table| {
inner = Some(t);
Ok(())
})?;
f.call::<()>(lua.create_table()?)?;
Ok(())
});
}
@@ -1,5 +0,0 @@
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`
-15
View File
@@ -1,15 +0,0 @@
use mlua::{Lua, Table};
fn main() {
let lua = Lua::new();
let mut outer: Option<Table> = None;
lua.scope(|scope| {
let f = scope
.create_function_mut(|_, t: Table| {
outer = Some(t);
Ok(())
})?;
f.call::<()>(lua.create_table()?)?;
Ok(())
});
}
@@ -1,5 +0,0 @@
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`
+5 -6
View File
@@ -10,12 +10,11 @@ fn main() {
let f = {
let mut test = Test { field: 0 };
scope
.create_function_mut(|_, ()| {
test.field = 42;
//~^ error: `test` does not live long enough
Ok(())
})?
scope.create_function_mut(|_, ()| {
test.field = 42;
//~^ error: `test` does not live long enough
Ok(())
})?
};
f.call::<()>(())
+24 -5
View File
@@ -1,5 +1,24 @@
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`
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:13:39
|
9 | lua.scope(|scope| {
| ----- has type `&'1 mut mlua::scope::Scope<'1, '_>`
...
13 | scope.create_function_mut(|_, ()| {
| ^^^^^^^ may outlive borrowed value `test.field`
14 | 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.create_function_mut(|_, ()| {
14 | | test.field = 42;
15 | | //~^ error: `test` does not live long enough
16 | | Ok(())
17 | | })?
| |______________^
help: to force the closure to take ownership of `test.field` (and any other referenced variables), use the `move` keyword
|
13 | scope.create_function_mut(move |_, ()| {
| ++++
+3 -3
View File
@@ -2,14 +2,14 @@ use mlua::{Lua, UserData};
fn main() {
struct MyUserData<'a>(&'a mut i32);
impl<'a> UserData for MyUserData<'a> {}
impl UserData for MyUserData<'_> {}
let mut i = 1;
let lua = Lua::new();
lua.scope(|scope| {
let _a = scope.create_nonstatic_userdata(MyUserData(&mut i)).unwrap();
let _b = scope.create_nonstatic_userdata(MyUserData(&mut i)).unwrap();
let _a = scope.create_userdata(MyUserData(&mut i)).unwrap();
let _b = scope.create_userdata(MyUserData(&mut i)).unwrap();
Ok(())
});
}
+10 -3
View File
@@ -1,5 +1,12 @@
error[E0599]: no method named `scope` found for struct `Lua` in the current scope
--> tests/compile/scope_mutable_aliasing.rs:10:9
error[E0499]: cannot borrow `i` as mutable more than once at a time
--> tests/compile/scope_mutable_aliasing.rs:12:51
|
10 | lua.scope(|scope| {
| ----^^^^^ method not found in `Lua`
| ----- has type `&mut mlua::scope::Scope<'_, '1>`
11 | let _a = scope.create_userdata(MyUserData(&mut i)).unwrap();
| -----------------------------------------
| | |
| | first mutable borrow occurs here
| argument requires that `i` is borrowed for `'1`
12 | let _b = scope.create_userdata(MyUserData(&mut i)).unwrap();
| ^^^^^^ second mutable borrow occurs here
+3 -3
View File
@@ -3,16 +3,16 @@ use mlua::{Lua, UserData};
fn main() {
// Should not allow userdata borrow to outlive lifetime of AnyUserData handle
struct MyUserData<'a>(&'a i32);
impl<'a> UserData for MyUserData<'a> {}
impl UserData for MyUserData<'_> {}
let igood = 1;
let lua = Lua::new();
lua.scope(|scope| {
let _ugood = scope.create_nonstatic_userdata(MyUserData(&igood)).unwrap();
let _ugood = scope.create_userdata(MyUserData(&igood)).unwrap();
let _ubad = {
let ibad = 42;
scope.create_nonstatic_userdata(MyUserData(&ibad)).unwrap();
scope.create_userdata(MyUserData(&ibad)).unwrap();
};
Ok(())
});
+13 -3
View File
@@ -1,5 +1,15 @@
error[E0599]: no method named `scope` found for struct `Lua` in the current scope
--> tests/compile/scope_userdata_borrow.rs:11:9
error[E0597]: `ibad` does not live long enough
--> tests/compile/scope_userdata_borrow.rs:15:46
|
11 | lua.scope(|scope| {
| ----^^^^^ method not found in `Lua`
| ----- has type `&mut mlua::scope::Scope<'_, '1>`
...
14 | let ibad = 42;
| ---- binding `ibad` declared here
15 | scope.create_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
-19
View File
@@ -1,19 +0,0 @@
use mlua::{AnyUserData, Lua, Table, UserData, Result};
fn main() -> Result<()> {
let lua = Lua::new();
let globals = lua.globals();
// Should not allow userdata borrow to outlive lifetime of AnyUserData handle
struct MyUserData;
impl UserData for MyUserData {};
let _userdata_ref;
{
let touter = globals.get::<Table>("touter")?;
touter.set("userdata", lua.create_userdata(MyUserData)?)?;
let userdata = touter.get::<AnyUserData>("userdata")?;
_userdata_ref = userdata.borrow::<MyUserData>();
//~^ error: `userdata` does not live long enough
}
Ok(())
}
-13
View File
@@ -1,13 +0,0 @@
error[E0597]: `userdata` does not live long enough
--> $DIR/userdata_borrow.rs:15:25
|
15 | _userdata_ref = userdata.borrow::<MyUserData>();
| ^^^^^^^^ borrowed value does not live long enough
16 | //~^ error: `userdata` does not live long enough
17 | }
| - `userdata` dropped here while still borrowed
18 | Ok(())
19 | }
| - borrow might be used here, when `_userdata_ref` is dropped and runs the destructor for type `std::result::Result<std::cell::Ref<'_, main::MyUserData>, mlua::error::Error>`
|
= note: values in a scope are dropped in the opposite order they are defined
+138 -5
View File
@@ -1,11 +1,13 @@
use std::borrow::Cow;
use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet};
use std::ffi::{CStr, CString};
use std::ffi::{CStr, CString, OsString};
use std::path::PathBuf;
use bstr::BString;
use maplit::{btreemap, btreeset, hashmap, hashset};
use mlua::{
AnyUserData, Error, Function, IntoLua, Lua, RegistryKey, Result, Table, Thread, UserDataRef, Value,
AnyUserData, Either, Error, Function, IntoLua, Lua, RegistryKey, Result, Table, Thread, UserDataRef,
Value,
};
#[test]
@@ -32,7 +34,7 @@ fn test_string_into_lua() -> Result<()> {
// Direct conversion
let s = lua.create_string("hello, world!")?;
let s2 = (&s).into_lua(&lua)?;
assert_eq!(s, s2.as_string().unwrap());
assert_eq!(s, *s2.as_string().unwrap());
// Push into stack
let table = lua.create_table()?;
@@ -383,8 +385,8 @@ fn test_bstring_from_lua() -> Result<()> {
fn test_bstring_from_lua_buffer() -> Result<()> {
let lua = Lua::new();
let b = lua.create_buffer("hello, world")?;
let bstr = lua.unpack::<BString>(Value::UserData(b))?;
let buf = lua.create_buffer("hello, world")?;
let bstr = lua.convert::<BString>(buf)?;
assert_eq!(bstr, "hello, world");
// Test from stack
@@ -396,6 +398,55 @@ fn test_bstring_from_lua_buffer() -> Result<()> {
Ok(())
}
#[test]
fn test_osstring_into_from_lua() -> Result<()> {
let lua = Lua::new();
let s = OsString::from("hello, world");
let v = lua.pack(s.as_os_str())?;
assert!(v.is_string());
assert_eq!(v.as_str().unwrap(), "hello, world");
let v = lua.pack(s)?;
assert!(v.is_string());
assert_eq!(v.as_str().unwrap(), "hello, world");
let s = lua.create_string("hello, world")?;
let bstr = lua.unpack::<OsString>(Value::String(s))?;
assert_eq!(bstr, "hello, world");
let bstr = lua.unpack::<OsString>(Value::Integer(123))?;
assert_eq!(bstr, "123");
let bstr = lua.unpack::<OsString>(Value::Number(-123.55))?;
assert_eq!(bstr, "-123.55");
Ok(())
}
#[test]
fn test_pathbuf_into_from_lua() -> Result<()> {
let lua = Lua::new();
let pb = PathBuf::from(env!("CARGO_TARGET_TMPDIR"));
let pb_str = pb.to_str().unwrap();
let v = lua.pack(pb.as_path())?;
assert!(v.is_string());
assert_eq!(v.as_str().unwrap(), pb_str);
let v = lua.pack(pb.clone())?;
assert!(v.is_string());
assert_eq!(v.as_str().unwrap(), pb_str);
let s = lua.create_string(pb_str)?;
let bstr = lua.unpack::<PathBuf>(Value::String(s))?;
assert_eq!(bstr, pb);
Ok(())
}
#[test]
fn test_option_into_from_lua() -> Result<()> {
let lua = Lua::new();
@@ -413,3 +464,85 @@ fn test_option_into_from_lua() -> Result<()> {
Ok(())
}
#[test]
fn test_either_enum() -> Result<()> {
// Left
let mut either = Either::<_, String>::Left(42);
assert!(either.is_left());
assert_eq!(*either.as_ref().left().unwrap(), 42);
*either.as_mut().left().unwrap() = 44;
assert_eq!(*either.as_ref().left().unwrap(), 44);
assert_eq!(format!("{either}"), "44");
// Right
either = Either::Right("hello".to_string());
assert!(either.is_right());
assert_eq!(*either.as_ref().right().unwrap(), "hello");
*either.as_mut().right().unwrap() = "world".to_string();
assert_eq!(*either.as_ref().right().unwrap(), "world");
assert_eq!(format!("{either}"), "world");
Ok(())
}
#[test]
fn test_either_into_lua() -> Result<()> {
let lua = Lua::new();
// Direct conversion
let mut either = Either::<i32, &Table>::Left(42);
let value = either.into_lua(&lua)?;
assert_eq!(value, Value::Integer(42));
// Push into stack
let f =
lua.create_function(|_, either: Either<i32, Table>| either.right().unwrap().set("hello", "world"))?;
let t = lua.create_table()?;
either = Either::Right(&t);
f.call::<()>(either)?;
assert_eq!(t.get::<String>("hello")?, "world");
let f = lua.create_function(|_, either: Either<i32, Table>| Ok(either.left().unwrap() + 1))?;
either = Either::Left(42);
assert_eq!(f.call::<i32>(either)?, 43);
Ok(())
}
#[test]
fn test_either_from_lua() -> Result<()> {
let lua = Lua::new();
// From stack
let f = lua.create_function(|_, either: Either<i32, Table>| Ok(either))?;
let either = f.call::<Either<i32, Table>>(42)?;
assert!(either.is_left());
assert_eq!(*either.as_ref().left().unwrap(), 42);
let either = f.call::<Either<i32, Table>>([5; 5])?;
assert!(either.is_right());
assert_eq!(either.as_ref().right().unwrap(), &[5; 5]);
// Check error message
match f.call::<Value>("hello") {
Ok(_) => panic!("expected error, got Ok"),
Err(ref err @ Error::CallbackError { ref cause, .. }) => {
match cause.as_ref() {
Error::BadArgument { cause, .. } => match cause.as_ref() {
Error::FromLuaConversionError { to, .. } => {
assert_eq!(to, "Either<i32, Table>")
}
err => panic!("expected `Error::FromLuaConversionError`, got {err:?}"),
},
err => panic!("expected `Error::BadArgument`, got {err:?}"),
}
assert!(err
.to_string()
.starts_with("bad argument #1: error converting Lua string to Either<i32, Table>"),);
}
err => panic!("expected `Error::CallbackError`, got {err:?}"),
}
Ok(())
}
+49
View File
@@ -46,3 +46,52 @@ fn test_error_context() -> Result<()> {
Ok(())
}
#[test]
fn test_error_chain() -> Result<()> {
let lua = Lua::new();
// Check that `Error::ExternalError` creates a chain with a single element
let io_err = io::Error::new(io::ErrorKind::Other, "other");
assert_eq!(Error::external(io_err).chain().count(), 1);
let func = lua.create_function(|_, ()| {
let err = Error::external(io::Error::new(io::ErrorKind::Other, "other")).context("io error");
Err::<(), _>(err)
})?;
let err = func.call::<()>(()).err().unwrap();
assert_eq!(err.chain().count(), 3);
for (i, err) in err.chain().enumerate() {
match i {
0 => assert!(matches!(err.downcast_ref(), Some(Error::CallbackError { .. }))),
1 => assert!(matches!(err.downcast_ref(), Some(Error::WithContext { .. }))),
2 => assert!(matches!(err.downcast_ref(), Some(io::Error { .. }))),
_ => unreachable!(),
}
}
Ok(())
}
#[cfg(feature = "anyhow")]
#[test]
fn test_error_anyhow() -> Result<()> {
use mlua::IntoLua;
let lua = Lua::new();
let err = anyhow::Error::msg("anyhow error");
let val = err.into_lua(&lua)?;
assert!(val.is_error());
assert_eq!(val.as_error().unwrap().to_string(), "anyhow error");
// Try Error -> anyhow::Error -> Error roundtrip
let err = Error::runtime("runtime error");
let err = anyhow::Error::new(err);
let err = err.into_lua(&lua)?;
assert!(err.is_error());
let err = err.as_error().unwrap();
assert!(matches!(err, Error::RuntimeError(msg) if msg == "runtime error"));
Ok(())
}
+91 -21
View File
@@ -1,4 +1,4 @@
use mlua::{Function, Lua, Result, String, Table};
use mlua::{Error, Function, Lua, Result, String, Table};
#[test]
fn test_function() -> Result<()> {
@@ -271,31 +271,101 @@ fn test_function_deep_clone() -> Result<()> {
#[test]
fn test_function_wrap() -> Result<()> {
use mlua::Error;
let lua = Lua::new();
lua.globals().set("f", Function::wrap(|_, s: String| Ok(s)))?;
lua.load(r#"assert(f("hello") == "hello")"#).exec().unwrap();
let f = Function::wrap(|s: String, n| Ok(s.to_str().unwrap().repeat(n)));
lua.globals().set("f", f)?;
lua.load(r#"assert(f("hello", 2) == "hellohello")"#)
.exec()
.unwrap();
let mut _i = false;
lua.globals().set(
"f",
Function::wrap_mut(move |lua, ()| {
_i = true;
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 { .. } => {}
ref other => panic!("incorrect result: {other:?}"),
},
ref other => panic!("incorrect result: {other:?}"),
// Return error
let ferr = Function::wrap(|| Err::<(), _>(Error::runtime("some error")));
lua.globals().set("ferr", ferr)?;
lua.load(
r#"
local ok, err = pcall(ferr)
assert(not ok and tostring(err):find("some error"))
"#,
)
.exec()
.unwrap();
// Mutable callback
let mut i = 0;
let fmut = Function::wrap_mut(move || {
i += 1;
Ok(i)
});
lua.globals().set("fmut", fmut)?;
lua.load(r#"fmut(); fmut(); assert(fmut() == 3)"#).exec().unwrap();
// Check mutable callback with error
let fmut_err = Function::wrap_mut(|| Err::<(), _>(Error::runtime("some error")));
lua.globals().set("fmut_err", fmut_err)?;
lua.load(
r#"
local ok, err = pcall(fmut_err)
assert(not ok and tostring(err):find("some error"))
"#,
)
.exec()
.unwrap();
// Check recursive mut callback error
let fmut = Function::wrap_mut(|f: Function| match f.call::<()>(&f) {
Err(Error::CallbackError { cause, .. }) => match cause.as_ref() {
Error::RecursiveMutCallback { .. } => Ok(()),
other => panic!("incorrect result: {other:?}"),
},
other => panic!("incorrect result: {other:?}"),
};
});
let fmut = lua.convert::<Function>(fmut)?;
assert!(fmut.call::<()>(&fmut).is_ok());
Ok(())
}
#[test]
fn test_function_wrap_raw() -> Result<()> {
let lua = Lua::new();
let f = Function::wrap_raw(|| "hello");
lua.globals().set("f", f)?;
lua.load(r#"assert(f() == "hello")"#).exec().unwrap();
// Return error
let ferr = Function::wrap_raw(|| Err::<(), _>("some error"));
lua.globals().set("ferr", ferr)?;
lua.load(
r#"
local _, err = ferr()
assert(err == "some error")
"#,
)
.exec()
.unwrap();
// Mutable callback
let mut i = 0;
let fmut = Function::wrap_raw_mut(move || {
i += 1;
i
});
lua.globals().set("fmut", fmut)?;
lua.load(r#"fmut(); fmut(); assert(fmut() == 3)"#).exec().unwrap();
// Check mutable callback with error
let fmut_err = Function::wrap_raw_mut(|| Err::<(), _>("some error"));
lua.globals().set("fmut_err", fmut_err)?;
lua.load(
r#"
local _, err = fmut_err()
assert(err == "some error")
"#,
)
.exec()
.unwrap();
Ok(())
}
+43 -7
View File
@@ -4,7 +4,7 @@ use std::ops::Deref;
use std::sync::atomic::{AtomicI64, Ordering};
use std::sync::{Arc, Mutex};
use mlua::{DebugEvent, Error, HookTriggers, Lua, Result, Value};
use mlua::{DebugEvent, Error, HookTriggers, Lua, Result, ThreadStatus, Value, VmState};
#[test]
fn test_hook_triggers() {
@@ -26,7 +26,7 @@ fn test_line_counts() -> Result<()> {
lua.set_hook(HookTriggers::EVERY_LINE, move |_lua, debug| {
assert_eq!(debug.event(), DebugEvent::Line);
hook_output.lock().unwrap().push(debug.curr_line());
Ok(())
Ok(VmState::Continue)
});
lua.load(
r#"
@@ -61,7 +61,7 @@ fn test_function_calls() -> Result<()> {
let source = debug.source();
let name = names.name.map(|s| s.into_owned());
hook_output.lock().unwrap().push((name, source.what));
Ok(())
Ok(VmState::Continue)
});
lua.load(
@@ -120,7 +120,7 @@ fn test_limit_execution_instructions() -> Result<()> {
if max_instructions.fetch_sub(30, Ordering::Relaxed) <= 30 {
Err(Error::runtime("time's up"))
} else {
Ok(())
Ok(VmState::Continue)
}
},
);
@@ -191,10 +191,10 @@ fn test_hook_swap_within_hook() -> Result<()> {
TL_LUA.with(|tl| {
tl.borrow().as_ref().unwrap().remove_hook();
});
Ok(())
Ok(VmState::Continue)
})
});
Ok(())
Ok(VmState::Continue)
})
});
@@ -234,7 +234,7 @@ fn test_hook_threads() -> Result<()> {
co.set_hook(HookTriggers::EVERY_LINE, move |_lua, debug| {
assert_eq!(debug.event(), DebugEvent::Line);
hook_output.lock().unwrap().push(debug.curr_line());
Ok(())
Ok(VmState::Continue)
});
co.resume::<()>(())?;
@@ -249,3 +249,39 @@ fn test_hook_threads() -> Result<()> {
Ok(())
}
#[test]
fn test_hook_yield() -> Result<()> {
let lua = Lua::new();
let func = lua
.load(
r#"
local x = 2 + 3
local y = x * 63
local z = string.len(x..", "..y)
"#,
)
.into_function()?;
let co = lua.create_thread(func)?;
co.set_hook(HookTriggers::EVERY_LINE, move |_lua, _debug| Ok(VmState::Yield));
#[cfg(any(feature = "lua54", feature = "lua53"))]
{
assert!(co.resume::<()>(()).is_ok());
assert!(co.resume::<()>(()).is_ok());
assert!(co.resume::<()>(()).is_ok());
assert!(co.resume::<()>(()).is_ok());
assert!(co.status() == ThreadStatus::Finished);
}
#[cfg(any(feature = "lua51", feature = "lua52", feature = "luajit"))]
{
assert!(
matches!(co.resume::<()>(()), Err(Error::RuntimeError(err)) if err.contains("attempt to yield from a hook"))
);
assert!(co.status() == ThreadStatus::Error);
}
Ok(())
}
+1 -27
View File
@@ -194,7 +194,7 @@ fn test_vector_metatable() -> Result<()> {
)
.eval::<Table>()?;
vector_mt.set_metatable(Some(vector_mt.clone()));
lua.set_vector_metatable(Some(vector_mt.clone()));
lua.set_type_metatable::<Vector>(Some(vector_mt.clone()));
lua.globals().set("Vector3", vector_mt)?;
let compiler = Compiler::new().set_vector_lib("Vector3").set_vector_ctor("new");
@@ -465,32 +465,6 @@ fn test_coverage() -> Result<()> {
Ok(())
}
#[test]
fn test_buffer() -> Result<()> {
let lua = Lua::new();
let buf1 = lua
.load(
r#"
local buf = buffer.fromstring("hello")
assert(buffer.len(buf) == 5)
return buf
"#,
)
.eval::<Value>()?;
assert!(buf1.is_userdata() && buf1.is_buffer());
assert_eq!(buf1.type_name(), "buffer");
let buf2 = lua.load("buffer.fromstring('hello')").eval::<Value>()?;
assert_ne!(buf1, buf2);
// 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:"));
Ok(())
}
#[test]
fn test_fflags() {
// We cannot really on any particular feature flag to be present
+91 -151
View File
@@ -1,11 +1,10 @@
use std::cell::Cell;
use std::rc::Rc;
use std::string::String as StdString;
use std::sync::Arc;
use mlua::{
AnyUserData, Error, Function, Lua, MetaMethod, Result, String, UserData, UserDataFields,
UserDataMethods,
AnyUserData, Error, Function, Lua, MetaMethod, ObjectLike, Result, String, UserData, UserDataFields,
UserDataMethods, UserDataRegistry,
};
#[test]
@@ -14,20 +13,20 @@ fn test_scope_func() -> Result<()> {
let rc = Rc::new(Cell::new(0));
lua.scope(|scope| {
let r = rc.clone();
let rc2 = rc.clone();
let f = scope.create_function(move |_, ()| {
r.set(42);
rc2.set(42);
Ok(())
})?;
lua.globals().set("bad", f.clone())?;
f.call::<_, ()>(())?;
lua.globals().set("f", &f)?;
f.call::<()>(())?;
assert_eq!(Rc::strong_count(&rc), 2);
Ok(())
})?;
assert_eq!(rc.get(), 42);
assert_eq!(Rc::strong_count(&rc), 1);
match lua.globals().get::<_, Function>("bad")?.call::<_, ()>(()) {
match lua.globals().get::<Function>("f")?.call::<()>(()) {
Err(Error::CallbackError { ref cause, .. }) => match *cause.as_ref() {
Error::CallbackDestructed => {}
ref err => panic!("wrong error type {:?}", err),
@@ -49,7 +48,7 @@ fn test_scope_capture() -> Result<()> {
i = 42;
Ok(())
})?
.call::<_, ()>(())
.call::<()>(())
})?;
assert_eq!(i, 42);
@@ -61,12 +60,8 @@ fn test_scope_outer_lua_access() -> Result<()> {
let lua = Lua::new();
let table = lua.create_table()?;
lua.scope(|scope| {
scope
.create_function_mut(|_, ()| table.set("a", "b"))?
.call::<_, ()>(())
})?;
assert_eq!(table.get::<_, String>("a")?, "b");
lua.scope(|scope| scope.create_function(|_, ()| table.set("a", "b"))?.call::<()>(()))?;
assert_eq!(table.get::<String>("a")?, "b");
Ok(())
}
@@ -75,11 +70,11 @@ fn test_scope_outer_lua_access() -> Result<()> {
fn test_scope_userdata_fields() -> Result<()> {
struct MyUserData<'a>(&'a Cell<i64>);
impl<'a> UserData for MyUserData<'a> {
fn add_fields<'lua, F: UserDataFields<'lua, Self>>(fields: &mut F) {
fields.add_field("field", "hello");
fields.add_field_method_get("val", |_, data| Ok(data.0.get()));
fields.add_field_method_set("val", |_, data, val| {
impl UserData for MyUserData<'_> {
fn register(reg: &mut UserDataRegistry<Self>) {
reg.add_field("field", "hello");
reg.add_field_method_get("val", |_, data| Ok(data.0.get()));
reg.add_field_method_set("val", |_, data, val| {
data.0.set(val);
Ok(())
});
@@ -101,7 +96,7 @@ fn test_scope_userdata_fields() -> Result<()> {
)
.eval()?;
lua.scope(|scope| f.call::<_, ()>(scope.create_nonstatic_userdata(MyUserData(&i))?))?;
lua.scope(|scope| f.call::<()>(scope.create_userdata(MyUserData(&i))?))?;
assert_eq!(i.get(), 44);
@@ -112,14 +107,14 @@ fn test_scope_userdata_fields() -> Result<()> {
fn test_scope_userdata_methods() -> Result<()> {
struct MyUserData<'a>(&'a Cell<i64>);
impl<'a> UserData for MyUserData<'a> {
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
methods.add_method("inc", |_, data, ()| {
impl UserData for MyUserData<'_> {
fn register(reg: &mut UserDataRegistry<Self>) {
reg.add_method("inc", |_, data, ()| {
data.0.set(data.0.get() + 1);
Ok(())
});
methods.add_method("dec", |_, data, ()| {
reg.add_method("dec", |_, data, ()| {
data.0.set(data.0.get() - 1);
Ok(())
});
@@ -142,7 +137,7 @@ fn test_scope_userdata_methods() -> Result<()> {
)
.eval()?;
lua.scope(|scope| f.call::<_, ()>(scope.create_nonstatic_userdata(MyUserData(&i))?))?;
lua.scope(|scope| f.call::<()>(scope.create_userdata(MyUserData(&i))?))?;
assert_eq!(i.get(), 44);
@@ -150,19 +145,19 @@ fn test_scope_userdata_methods() -> Result<()> {
}
#[test]
fn test_scope_userdata_functions() -> Result<()> {
fn test_scope_userdata_ops() -> Result<()> {
struct MyUserData<'a>(&'a i64);
impl<'a> UserData for MyUserData<'a> {
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
methods.add_meta_method(MetaMethod::Add, |lua, this, ()| {
impl UserData for MyUserData<'_> {
fn register(reg: &mut UserDataRegistry<Self>) {
reg.add_meta_method(MetaMethod::Add, |lua, this, ()| {
let globals = lua.globals();
globals.set("i", globals.get::<_, i64>("i")? + this.0)?;
globals.set("i", globals.get::<i64>("i")? + this.0)?;
Ok(())
});
methods.add_meta_method(MetaMethod::Sub, |lua, this, ()| {
reg.add_meta_method(MetaMethod::Sub, |lua, this, ()| {
let globals = lua.globals();
globals.set("i", globals.get::<_, i64>("i")? + this.0)?;
globals.set("i", globals.get::<i64>("i")? + this.0)?;
Ok(())
});
}
@@ -184,9 +179,34 @@ fn test_scope_userdata_functions() -> Result<()> {
)
.eval::<Function>()?;
lua.scope(|scope| f.call::<_, ()>(scope.create_nonstatic_userdata(MyUserData(&dummy))?))?;
lua.scope(|scope| f.call::<()>(scope.create_userdata(MyUserData(&dummy))?))?;
assert_eq!(lua.globals().get::<_, i64>("i")?, 3);
assert_eq!(lua.globals().get::<i64>("i")?, 3);
Ok(())
}
#[test]
fn test_scope_userdata_values() -> Result<()> {
struct MyUserData<'a>(&'a i64);
impl UserData for MyUserData<'_> {
fn register(registry: &mut UserDataRegistry<Self>) {
registry.add_method("get", |_, data, ()| Ok(*data.0));
}
}
let lua = Lua::new();
let i = 42;
let data = MyUserData(&i);
lua.scope(|scope| {
let ud = scope.create_userdata(data)?;
assert_eq!(ud.call_method::<i64>("get", &ud)?, 42);
ud.set_user_value("user_value")?;
assert_eq!(ud.user_value::<String>()?, "user_value");
Ok(())
})?;
Ok(())
}
@@ -196,8 +216,8 @@ fn test_scope_userdata_mismatch() -> Result<()> {
struct MyUserData<'a>(&'a Cell<i64>);
impl<'a> UserData for MyUserData<'a> {
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
methods.add_method("inc", |_, data, ()| {
fn register(reg: &mut UserDataRegistry<Self>) {
reg.add_method("inc", |_, data, ()| {
data.0.set(data.0.get() + 1);
Ok(())
});
@@ -208,13 +228,7 @@ fn test_scope_userdata_mismatch() -> Result<()> {
lua.load(
r#"
function okay(a, b)
a.inc(a)
b.inc(b)
end
function bad(a, b)
a.inc(b)
end
function inc(a, b) a.inc(b) end
"#,
)
.exec()?;
@@ -222,29 +236,22 @@ fn test_scope_userdata_mismatch() -> Result<()> {
let a = Cell::new(1);
let b = Cell::new(1);
let okay: Function = lua.globals().get("okay")?;
let bad: Function = lua.globals().get("bad")?;
let inc: Function = lua.globals().get("inc")?;
lua.scope(|scope| {
let au = scope.create_nonstatic_userdata(MyUserData(&a))?;
let bu = scope.create_nonstatic_userdata(MyUserData(&b))?;
assert!(okay.call::<_, ()>((au.clone(), bu.clone())).is_ok());
match bad.call::<_, ()>((au, bu)) {
let au = scope.create_userdata(MyUserData(&a))?;
let bu = scope.create_userdata(MyUserData(&b))?;
assert!(inc.call::<()>((&au, &au)).is_ok());
match inc.call::<()>((&au, &bu)) {
Err(Error::CallbackError { ref cause, .. }) => match cause.as_ref() {
Error::BadArgument {
to,
pos,
name,
cause,
} => {
Error::BadArgument { to, pos, name, cause } => {
assert_eq!(to.as_deref(), Some("MyUserData.inc"));
assert_eq!(*pos, 1);
assert_eq!(name.as_deref(), Some("self"));
assert!(matches!(*cause.as_ref(), Error::UserDataTypeMismatch));
}
other => panic!("wrong error type {:?}", other),
other => panic!("wrong error type {other:?}"),
},
Err(other) => panic!("wrong error type {:?}", other),
Err(other) => panic!("wrong error type {other:?}"),
Ok(_) => panic!("incorrectly returned Ok"),
}
Ok(())
@@ -257,114 +264,46 @@ fn test_scope_userdata_mismatch() -> Result<()> {
fn test_scope_userdata_drop() -> Result<()> {
let lua = Lua::new();
struct MyUserData(#[allow(unused)] Rc<()>);
struct MyUserData<'a>(&'a Cell<i64>, #[allow(unused)] Rc<()>);
impl UserData for MyUserData {
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
methods.add_method("method", |_, _, ()| Ok(()));
}
}
struct MyUserDataArc(#[allow(unused)] Arc<()>);
impl UserData for MyUserDataArc {}
let rc = Rc::new(());
let arc = Arc::new(());
lua.scope(|scope| {
let ud = scope.create_userdata(MyUserData(rc.clone()))?;
ud.set_user_value(MyUserDataArc(arc.clone()))?;
lua.globals().set("ud", ud)?;
assert_eq!(Rc::strong_count(&rc), 2);
assert_eq!(Arc::strong_count(&arc), 2);
Ok(())
})?;
lua.gc_collect()?;
assert_eq!(Rc::strong_count(&rc), 1);
assert_eq!(Arc::strong_count(&arc), 1);
match lua.load("ud:method()").exec() {
Err(Error::CallbackError { ref cause, .. }) => match cause.as_ref() {
Error::CallbackDestructed => {}
err => panic!("expected CallbackDestructed, got {:?}", err),
},
r => panic!("improper return for destructed userdata: {:?}", r),
};
let ud = lua.globals().get::<_, AnyUserData>("ud")?;
match ud.borrow::<MyUserData>() {
Ok(_) => panic!("succesfull borrow for destructed userdata"),
Err(Error::UserDataDestructed) => {}
Err(err) => panic!("improper borrow error for destructed userdata: {:?}", err),
}
match ud.get_metatable() {
Ok(_) => panic!("successful metatable retrieval of destructed userdata"),
Err(Error::UserDataDestructed) => {}
Err(err) => panic!(
"improper metatable error for destructed userdata: {:?}",
err
),
}
Ok(())
}
#[test]
fn test_scope_nonstatic_userdata_drop() -> Result<()> {
let lua = Lua::new();
struct MyUserData<'a>(&'a Cell<i64>, #[allow(unused)] Arc<()>);
impl<'a> UserData for MyUserData<'a> {
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
methods.add_method("inc", |_, data, ()| {
impl UserData for MyUserData<'_> {
fn register(reg: &mut UserDataRegistry<Self>) {
reg.add_method("inc", |_, data, ()| {
data.0.set(data.0.get() + 1);
Ok(())
});
}
}
struct MyUserDataArc(#[allow(unused)] Arc<()>);
impl UserData for MyUserDataArc {}
let i = Cell::new(1);
let arc = Arc::new(());
let (i, rc) = (Cell::new(1), Rc::new(()));
lua.scope(|scope| {
let ud = scope.create_nonstatic_userdata(MyUserData(&i, arc.clone()))?;
ud.set_user_value(MyUserDataArc(arc.clone()))?;
let ud = scope.create_userdata(MyUserData(&i, rc.clone()))?;
lua.globals().set("ud", ud)?;
lua.load("ud:inc()").exec()?;
assert_eq!(Arc::strong_count(&arc), 3);
assert_eq!(Rc::strong_count(&rc), 2);
Ok(())
})?;
lua.gc_collect()?;
assert_eq!(Arc::strong_count(&arc), 1);
assert_eq!(Rc::strong_count(&rc), 1);
assert_eq!(i.get(), 2);
match lua.load("ud:inc()").exec() {
Err(Error::CallbackError { ref cause, .. }) => match cause.as_ref() {
Error::CallbackDestructed => {}
err => panic!("expected CallbackDestructed, got {:?}", err),
Error::UserDataDestructed => {}
err => panic!("expected UserDataDestructed, got {err:?}"),
},
r => panic!("improper return for destructed userdata: {:?}", r),
r => panic!("improper return for destructed userdata: {r:?}"),
};
let ud = lua.globals().get::<_, AnyUserData>("ud")?;
match ud.borrow::<MyUserData>() {
let ud = lua.globals().get::<AnyUserData>("ud")?;
match ud.borrow_scoped::<MyUserData, _>(|_| Ok::<_, Error>(())) {
Ok(_) => panic!("succesfull borrow for destructed userdata"),
Err(Error::UserDataDestructed) => {}
Err(err) => panic!("improper borrow error for destructed userdata: {:?}", err),
Err(err) => panic!("improper borrow error for destructed userdata: {err:?}"),
}
match ud.get_metatable() {
match ud.metatable() {
Ok(_) => panic!("successful metatable retrieval of destructed userdata"),
Err(Error::UserDataDestructed) => {}
Err(err) => panic!(
"improper metatable error for destructed userdata: {:?}",
err
),
Err(err) => panic!("improper metatable error for destructed userdata: {err:?}"),
}
Ok(())
@@ -377,7 +316,7 @@ fn test_scope_userdata_ref() -> Result<()> {
struct MyUserData(Cell<i64>);
impl UserData for MyUserData {
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
fn add_methods<M: UserDataMethods<Self>>(methods: &mut M) {
methods.add_method("inc", |_, data, ()| {
data.0.set(data.0.get() + 1);
Ok(())
@@ -407,7 +346,7 @@ fn test_scope_userdata_ref_mut() -> Result<()> {
struct MyUserData(i64);
impl UserData for MyUserData {
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
fn add_methods<M: UserDataMethods<Self>>(methods: &mut M) {
methods.add_method_mut("inc", |_, data, ()| {
data.0 += 1;
Ok(())
@@ -438,8 +377,9 @@ fn test_scope_any_userdata() -> Result<()> {
reg.add_meta_method("__tostring", |_, data, ()| Ok(data.clone()));
})?;
let data = StdString::from("foo");
lua.scope(|scope| {
let ud = scope.create_any_userdata(StdString::from("foo"))?;
let ud = scope.create_any_userdata_ref(&data)?;
lua.globals().set("ud", ud)?;
lua.load("assert(tostring(ud) == 'foo')").exec()
})?;
@@ -447,10 +387,10 @@ fn test_scope_any_userdata() -> Result<()> {
// Check that userdata is destructed
match lua.load("tostring(ud)").exec() {
Err(Error::CallbackError { ref cause, .. }) => match cause.as_ref() {
Error::CallbackDestructed => {}
err => panic!("expected CallbackDestructed, got {:?}", err),
Error::UserDataDestructed => {}
err => panic!("expected CallbackDestructed, got {err:?}"),
},
r => panic!("improper return for destructed userdata: {:?}", r),
r => panic!("improper return for destructed userdata: {r:?}"),
};
Ok(())
@@ -495,7 +435,7 @@ fn modify_userdata(lua: &Lua, ud: AnyUserData) -> Result<()> {
)
.eval()?;
f.call(ud)?;
f.call::<()>(ud)?;
Ok(())
}
+28 -58
View File
@@ -4,8 +4,8 @@ use std::collections::HashMap;
use std::error::Error as StdError;
use mlua::{
DeserializeOptions, Error, ExternalResult, Lua, LuaSerdeExt, Result as LuaResult, SerializeOptions,
UserData, Value,
AnyUserData, DeserializeOptions, Error, ExternalResult, IntoLua, Lua, LuaSerdeExt, Result as LuaResult,
SerializeOptions, UserData, Value,
};
use serde::{Deserialize, Serialize};
@@ -71,63 +71,31 @@ fn test_serialize() -> Result<(), Box<dyn StdError>> {
Ok(())
}
// #[test]
// fn test_serialize_in_scope() -> LuaResult<()> {
// #[derive(Serialize, Clone)]
// struct MyUserData(i64, String);
// impl UserData for MyUserData {}
// let lua = Lua::new();
// lua.scope(|scope| {
// let ud = scope.create_ser_userdata(MyUserData(-5, "test userdata".into()))?;
// assert_eq!(
// serde_json::to_value(&ud).unwrap(),
// serde_json::json!((-5, "test userdata"))
// );
// Ok(())
// })?;
// lua.scope(|scope| {
// let ud = scope.create_ser_userdata(MyUserData(-5, "test userdata".into()))?;
// lua.globals().set("ud", ud)
// })?;
// let val = lua.load("ud").eval::<Value>()?;
// match serde_json::to_value(&val) {
// Ok(v) => panic!("expected destructed error, got {}", v),
// Err(e) if e.to_string().contains("destructed") => {}
// Err(e) => panic!("expected destructed error, got {}", e),
// }
// struct MyUserDataRef<'a>(#[allow(unused)] &'a ());
// impl<'a> UserData for MyUserDataRef<'a> {}
// lua.scope(|scope| {
// let ud = scope.create_nonstatic_userdata(MyUserDataRef(&()))?;
// match serde_json::to_value(&ud) {
// Ok(v) => panic!("expected serialization error, got {}", v),
// Err(serde_json::Error { .. }) => {}
// };
// Ok(())
// })?;
// Ok(())
// }
#[test]
fn test_serialize_any_userdata() -> Result<(), Box<dyn StdError>> {
fn test_serialize_any_userdata() {
let lua = Lua::new();
let json_val = serde_json::json!({
"a": 1,
"b": "test",
});
let json_ud = lua.create_ser_any_userdata(json_val)?;
let json_str = serde_json::to_string_pretty(&json_ud)?;
let json_ud = lua.create_ser_any_userdata(json_val).unwrap();
let json_str = serde_json::to_string_pretty(&json_ud).unwrap();
assert_eq!(json_str, "{\n \"a\": 1,\n \"b\": \"test\"\n}");
}
Ok(())
#[test]
fn test_serialize_wrapped_any_userdata() {
let lua = Lua::new();
let json_val = serde_json::json!({
"a": 1,
"b": "test",
});
let ud = AnyUserData::wrap_ser(json_val);
let json_ud = ud.into_lua(&lua).unwrap();
let json_str = serde_json::to_string(&json_ud).unwrap();
assert_eq!(json_str, "{\"a\":1,\"b\":\"test\"}");
}
#[test]
@@ -761,27 +729,29 @@ fn test_arbitrary_precision() {
#[cfg(feature = "luau")]
#[test]
fn test_buffer_serialize() {
fn test_buffer_serialize() -> LuaResult<()> {
let lua = Lua::new();
let buf = lua.create_buffer(&[1, 2, 3, 4]).unwrap();
let buf = lua.create_buffer(&[1, 2, 3, 4])?;
let val = serde_value::to_value(&buf).unwrap();
assert_eq!(val, serde_value::Value::Bytes(vec![1, 2, 3, 4]));
// Try empty buffer
let buf = lua.create_buffer(&[]).unwrap();
let buf = lua.create_buffer(&[])?;
let val = serde_value::to_value(&buf).unwrap();
assert_eq!(val, serde_value::Value::Bytes(vec![]));
Ok(())
}
#[cfg(feature = "luau")]
#[test]
fn test_buffer_from_value() {
fn test_buffer_from_value() -> LuaResult<()> {
let lua = Lua::new();
let buf = lua.create_buffer(&[1, 2, 3, 4]).unwrap();
let val = lua
.from_value::<serde_value::Value>(Value::UserData(buf))
.unwrap();
let buf = lua.create_buffer(&[1, 2, 3, 4])?;
let val = lua.from_value::<serde_value::Value>(Value::Buffer(buf)).unwrap();
assert_eq!(val, serde_value::Value::Bytes(vec![1, 2, 3, 4]));
Ok(())
}
+1 -1
View File
@@ -86,7 +86,7 @@ fn test_string_debug() -> Result<()> {
// Invalid utf8
let s = lua.create_string(b"hello\0world\r\n\t\xF0\x90\x80")?;
assert_eq!(format!("{s:?}"), r#"b"hello\0world\r\n\t\xf0\x90\x80""#);
assert_eq!(format!("{s:?}"), r#"b"hello\0world\r\n\t\xF0\x90\x80""#);
Ok(())
}
+1 -1
View File
@@ -181,7 +181,7 @@ fn test_table_clear() -> Result<()> {
assert_eq!(t2.raw_len(), 0);
assert!(t2.is_empty());
assert_eq!(t2.raw_get::<Value>("a")?, Value::Nil);
assert_ne!(t2.get_metatable(), None);
assert_ne!(t2.metatable(), None);
Ok(())
}
+35 -2
View File
@@ -1256,8 +1256,7 @@ fn test_luajit_cdata() -> Result<()> {
"#,
)
.eval::<Value>()?;
assert!(cdata.is_userdata() && cdata.is_cdata());
assert_eq!(cdata.type_name(), "cdata");
assert_eq!(cdata.type_name(), "other");
assert!(cdata.to_string()?.starts_with("cdata<void *>:"));
Ok(())
@@ -1289,3 +1288,37 @@ fn test_multi_thread() -> Result<()> {
Ok(())
}
#[test]
fn test_exec_raw() -> Result<()> {
let lua = Lua::new();
let sum = lua.create_function(|_, args: Variadic<i32>| {
let mut sum = 0;
for i in args {
sum += i;
}
Ok(sum)
})?;
lua.globals().set("sum", sum)?;
let n: i32 = unsafe {
lua.exec_raw((), |state| {
ffi::lua_getglobal(state, b"sum\0".as_ptr() as _);
ffi::lua_pushinteger(state, 1);
ffi::lua_pushinteger(state, 7);
ffi::lua_call(state, 2, 1);
})
}?;
assert_eq!(n, 8);
// Test error handling
let res: Result<()> = unsafe {
lua.exec_raw("test error", |state| {
ffi::lua_error(state);
})
};
assert!(matches!(res, Err(Error::RuntimeError(err)) if err.contains("test error")));
Ok(())
}
+112 -1
View File
@@ -1,6 +1,6 @@
use std::os::raw::c_void;
use mlua::{Function, LightUserData, Lua, Result};
use mlua::{Function, LightUserData, Lua, Number, Result, String as LuaString, Thread};
#[test]
fn test_lightuserdata() -> Result<()> {
@@ -24,3 +24,114 @@ fn test_lightuserdata() -> Result<()> {
Ok(())
}
#[test]
fn test_boolean_type_metatable() -> Result<()> {
let lua = Lua::new();
let mt = lua.create_table()?;
mt.set("__add", Function::wrap(|a, b| Ok(a || b)))?;
lua.set_type_metatable::<bool>(Some(mt));
lua.load(r#"assert(true + true == true)"#).exec().unwrap();
lua.load(r#"assert(true + false == true)"#).exec().unwrap();
lua.load(r#"assert(false + true == true)"#).exec().unwrap();
lua.load(r#"assert(false + false == false)"#).exec().unwrap();
Ok(())
}
#[test]
fn test_lightuserdata_type_metatable() -> Result<()> {
let lua = Lua::new();
let mt = lua.create_table()?;
mt.set(
"__add",
Function::wrap(|a: LightUserData, b: LightUserData| {
Ok(LightUserData((a.0 as usize + b.0 as usize) as *mut c_void))
}),
)?;
lua.set_type_metatable::<LightUserData>(Some(mt));
let res = lua
.load(
r#"
local a, b = ...
return a + b
"#,
)
.call::<LightUserData>((
LightUserData(42 as *mut c_void),
LightUserData(100 as *mut c_void),
))
.unwrap();
assert_eq!(res, LightUserData(142 as *mut c_void));
Ok(())
}
#[test]
fn test_number_type_metatable() -> Result<()> {
let lua = Lua::new();
let mt = lua.create_table()?;
mt.set("__call", Function::wrap(|n1: f64, n2: f64| Ok(n1 * n2)))?;
lua.set_type_metatable::<Number>(Some(mt));
lua.load(r#"assert((1.5)(3.0) == 4.5)"#).exec().unwrap();
lua.load(r#"assert((5)(5) == 25)"#).exec().unwrap();
Ok(())
}
#[test]
fn test_string_type_metatable() -> Result<()> {
let lua = Lua::new();
let mt = lua.create_table()?;
mt.set(
"__add",
Function::wrap(|a: String, b: String| Ok(format!("{a}{b}"))),
)?;
lua.set_type_metatable::<LuaString>(Some(mt));
lua.load(r#"assert(("foo" + "bar") == "foobar")"#).exec().unwrap();
Ok(())
}
#[test]
fn test_function_type_metatable() -> Result<()> {
let lua = Lua::new();
let mt = lua.create_table()?;
mt.set(
"__index",
Function::wrap(|_: Function, key: String| Ok(format!("function.{key}"))),
)?;
lua.set_type_metatable::<Function>(Some(mt));
lua.load(r#"assert((function() end).foo == "function.foo")"#)
.exec()
.unwrap();
Ok(())
}
#[test]
fn test_thread_type_metatable() -> Result<()> {
let lua = Lua::new();
let mt = lua.create_table()?;
mt.set(
"__index",
Function::wrap(|_: Thread, key: String| Ok(format!("thread.{key}"))),
)?;
lua.set_type_metatable::<Thread>(Some(mt));
lua.load(r#"assert((coroutine.create(function() end)).foo == "thread.foo")"#)
.exec()
.unwrap();
Ok(())
}
+31 -22
View File
@@ -198,16 +198,16 @@ fn test_metamethods() -> Result<()> {
assert!(userdata2.equals(userdata3)?);
let userdata1: AnyUserData = globals.get("userdata1")?;
assert!(userdata1.get_metatable()?.contains(MetaMethod::Add)?);
assert!(userdata1.get_metatable()?.contains(MetaMethod::Sub)?);
assert!(userdata1.get_metatable()?.contains(MetaMethod::Index)?);
assert!(!userdata1.get_metatable()?.contains(MetaMethod::Pow)?);
assert!(userdata1.metatable()?.contains(MetaMethod::Add)?);
assert!(userdata1.metatable()?.contains(MetaMethod::Sub)?);
assert!(userdata1.metatable()?.contains(MetaMethod::Index)?);
assert!(!userdata1.metatable()?.contains(MetaMethod::Pow)?);
Ok(())
}
#[test]
#[cfg(feature = "lua54")]
#[test]
fn test_metamethod_close() -> Result<()> {
#[derive(Clone)]
struct MyUserData(Arc<AtomicI64>);
@@ -336,8 +336,8 @@ fn test_userdata_take() -> Result<()> {
}
match lua.load("userdata:num()").exec() {
Err(Error::CallbackError { ref cause, .. }) => match cause.as_ref() {
Error::CallbackDestructed => {}
err => panic!("expected `CallbackDestructed`, got {:?}", err),
Error::UserDataDestructed => {}
err => panic!("expected `UserDataDestructed`, got {:?}", err),
},
r => panic!("improper return for destructed userdata: {:?}", r),
}
@@ -565,7 +565,7 @@ fn test_metatable() -> Result<()> {
impl UserData for MyUserData {
fn add_methods<M: UserDataMethods<Self>>(methods: &mut M) {
methods.add_function("my_type_name", |_, data: AnyUserData| {
let metatable = data.get_metatable()?;
let metatable = data.metatable()?;
metatable.get::<String>(MetaMethod::Type)
});
}
@@ -583,7 +583,7 @@ fn test_metatable() -> Result<()> {
lua.load(r#"assert(typeof(ud) == "MyUserData")"#).exec()?;
let ud: AnyUserData = globals.get("ud")?;
let metatable = ud.get_metatable()?;
let metatable = ud.metatable()?;
match metatable.get::<Value>("__gc") {
Ok(_) => panic!("expected MetaMethodRestricted, got no error"),
@@ -629,7 +629,7 @@ fn test_metatable() -> Result<()> {
}
let ud = lua.create_userdata(MyUserData3)?;
let metatable = ud.get_metatable()?;
let metatable = ud.metatable()?;
assert_eq!(metatable.get::<String>(MetaMethod::Type)?.to_str()?, "CustomName");
Ok(())
@@ -791,18 +791,27 @@ 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 Err(Error::CallbackError { cause, .. }) = res else {
panic!("expected CallbackError, got {res:?}");
};
assert!(matches!(
&*cause,
Error::BadArgument {
to,
name,
..
} if to.as_deref() == Some("MyUserData.get_value") && name.as_deref() == Some("self")
));
let res = ud.call_function::<()>("get_value", "not a userdata");
match res {
Err(Error::CallbackError { cause, .. }) => match cause.as_ref() {
Error::BadArgument {
to,
name,
cause: cause2,
..
} => {
assert_eq!(to.as_deref(), Some("MyUserData.get_value"));
assert_eq!(name.as_deref(), Some("self"));
println!("{}", cause2.to_string());
assert_eq!(
cause2.to_string(),
"error converting Lua string to userdata (expected userdata of type 'MyUserData')"
);
}
err => panic!("expected BadArgument, got {err:?}"),
},
r => panic!("expected CallbackError, got {r:?}"),
}
Ok(())
}
+19
View File
@@ -140,6 +140,17 @@ fn test_value_to_string() -> Result<()> {
let err = Value::Error(Box::new(Error::runtime("test error")));
assert_eq!(err.to_string()?, "runtime error: test error");
#[cfg(feature = "luau")]
{
let buf = Value::Buffer(lua.create_buffer(b"hello")?);
assert!(buf.to_string()?.starts_with("buffer:"));
// Set `__tostring` metamethod for buffer
let mt = lua.load("{__tostring = buffer.tostring}").eval()?;
lua.set_type_metatable::<mlua::Buffer>(mt);
assert_eq!(buf.to_string()?, "hello");
}
Ok(())
}
@@ -220,5 +231,13 @@ fn test_value_conversions() -> Result<()> {
Some(&"hello")
);
assert!(Value::Error(Box::new(Error::runtime("some error"))).is_error());
assert_eq!(
(Value::Error(Box::new(Error::runtime("some error"))).as_error())
.unwrap()
.to_string(),
"runtime error: some error"
);
Ok(())
}