mirror of
https://github.com/mlua-rs/mlua
synced 2026-06-08 16:05:43 +00:00
Compare commits
92 Commits
v0.8
...
v0.9.0-beta.2
| Author | SHA1 | Date | |
|---|---|---|---|
| 3253ae8f4a | |||
| fdda0d3724 | |||
| a1d385c7b7 | |||
| 21b834decc | |||
| 4daa7de997 | |||
| bd68a155c0 | |||
| 993aaf44c7 | |||
| 54cbc62727 | |||
| 2d6a0fdf9c | |||
| d0cbd32ad2 | |||
| be64706cff | |||
| c178bc0a55 | |||
| 0fccdfed5c | |||
| aaf0a5e44a | |||
| 98888883bc | |||
| 65e72f39ae | |||
| f9d1213c4b | |||
| dc10c80e61 | |||
| 23b9cefdca | |||
| 15dc0e9f23 | |||
| 0c53e09e30 | |||
| cdbf04f50c | |||
| ba324b4f54 | |||
| 3e83753466 | |||
| 288934c82c | |||
| 483bc80fc4 | |||
| 354c69acc4 | |||
| 6fa0a78ec0 | |||
| f66932e8d2 | |||
| da6d5a93c9 | |||
| 1be927bc5d | |||
| 0848ddcdf7 | |||
| e182d474e0 | |||
| 0f937b0a03 | |||
| cd9fc1d08f | |||
| d9aac08b81 | |||
| 9c1669020b | |||
| 742307a267 | |||
| 781ded573a | |||
| 6a647f58be | |||
| 4bc6501d2e | |||
| 8d80321738 | |||
| a0d37fd182 | |||
| 03787668fd | |||
| 3059f82552 | |||
| 22fbcfd9fb | |||
| 304880bfd1 | |||
| 33c276d0b4 | |||
| 87d027e8ac | |||
| d7db1b7f2e | |||
| 22c973af02 | |||
| 8c18fa1764 | |||
| 5394faca16 | |||
| 4a3f6d60ad | |||
| 68b60e2a0a | |||
| 888bd77e60 | |||
| 949906f9f7 | |||
| 94f01e597c | |||
| b8e3290f35 | |||
| 03ab828342 | |||
| b66bff9155 | |||
| f52abf919e | |||
| b790b525c1 | |||
| f5182e0584 | |||
| 47c8300ccf | |||
| 8339621f9c | |||
| 16cbb442e5 | |||
| 88da28a68d | |||
| a62061f453 | |||
| cb3d58b1c7 | |||
| fa46720f5a | |||
| 72b9209ae9 | |||
| 0cb7058476 | |||
| ac8b3424d1 | |||
| 2cb4b49d0d | |||
| d67332a3ac | |||
| 22c6e9a7e7 | |||
| 1d4a135e8e | |||
| 9d28b790e7 | |||
| 61c285746c | |||
| a13c188de3 | |||
| c60f633a62 | |||
| 56abc4a700 | |||
| 9b4e3a1598 | |||
| 0aa30226df | |||
| fdb5724053 | |||
| b532f86674 | |||
| c44463f030 | |||
| de38e299fc | |||
| 7526773a90 | |||
| bf79d9e75d | |||
| 02c08c6350 |
@@ -14,7 +14,7 @@ jobs:
|
||||
|
||||
- name: Generate coverage report
|
||||
run: |
|
||||
cargo tarpaulin --verbose --features lua54,vendored,async,send,serialize,macros --out xml --exclude-files benches --exclude-files build --exclude-files mlua_derive --exclude-files src/ffi --exclude-files tests
|
||||
cargo tarpaulin --out xml --tests --exclude-files benches/* --exclude-files mlua-sys/src/*/*
|
||||
|
||||
- name: Upload report to codecov.io
|
||||
uses: codecov/codecov-action@v3
|
||||
|
||||
+13
-11
@@ -27,7 +27,7 @@ jobs:
|
||||
- name: Build ${{ matrix.lua }} vendored
|
||||
run: |
|
||||
cargo build --features "${{ matrix.lua }},vendored"
|
||||
cargo build --features "${{ matrix.lua }},vendored,async,send,serialize,macros,parking_lot"
|
||||
cargo build --features "${{ matrix.lua }},vendored,async,send,serialize,macros,parking_lot,unstable"
|
||||
shell: bash
|
||||
- name: Build ${{ matrix.lua }} pkg-config
|
||||
if: ${{ matrix.os == 'ubuntu-22.04' }}
|
||||
@@ -50,7 +50,7 @@ jobs:
|
||||
toolchain: stable
|
||||
target: aarch64-apple-darwin
|
||||
- name: Cross-compile
|
||||
run: cargo build --target aarch64-apple-darwin --features "${{ matrix.lua }},vendored,async,send,serialize,macros,parking_lot"
|
||||
run: cargo build --target aarch64-apple-darwin --features "${{ matrix.lua }},vendored,async,send,serialize,macros,parking_lot,unstable"
|
||||
|
||||
build_aarch64_cross_ubuntu:
|
||||
name: Cross-compile to aarch64-unknown-linux-gnu
|
||||
@@ -71,7 +71,7 @@ jobs:
|
||||
sudo apt-get install -y --no-install-recommends gcc-aarch64-linux-gnu libc6-dev-arm64-cross
|
||||
shell: bash
|
||||
- name: Cross-compile
|
||||
run: cargo build --target aarch64-unknown-linux-gnu --features "${{ matrix.lua }},vendored,async,send,serialize,macros,parking_lot"
|
||||
run: cargo build --target aarch64-unknown-linux-gnu --features "${{ matrix.lua }},vendored,async,send,serialize,macros,parking_lot,unstable"
|
||||
shell: bash
|
||||
|
||||
build_armv7_cross_ubuntu:
|
||||
@@ -93,7 +93,7 @@ jobs:
|
||||
sudo apt-get install -y --no-install-recommends gcc-arm-linux-gnueabihf libc-dev-armhf-cross
|
||||
shell: bash
|
||||
- name: Cross-compile
|
||||
run: cargo build --target armv7-unknown-linux-gnueabihf --features "${{ matrix.lua }},vendored,async,send,serialize,macros,parking_lot"
|
||||
run: cargo build --target armv7-unknown-linux-gnueabihf --features "${{ matrix.lua }},vendored,async,send,serialize,macros,parking_lot,unstable"
|
||||
shell: bash
|
||||
|
||||
test:
|
||||
@@ -118,17 +118,18 @@ jobs:
|
||||
with:
|
||||
toolchain: ${{ matrix.rust }}
|
||||
target: ${{ matrix.target }}
|
||||
- uses: Swatinem/rust-cache@v1
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
- name: Run ${{ matrix.lua }} tests
|
||||
run: |
|
||||
cargo test --features "${{ matrix.lua }},vendored"
|
||||
cargo test --features "${{ matrix.lua }},vendored,async,send,serialize,macros,parking_lot"
|
||||
cargo test --features "${{ matrix.lua }},vendored,async,serialize,macros,parking_lot,unstable"
|
||||
shell: bash
|
||||
- name: Run compile tests (macos lua54)
|
||||
if: ${{ matrix.os == 'macos-latest' && matrix.lua == 'lua54' }}
|
||||
run: |
|
||||
TRYBUILD=overwrite cargo test --features "${{ matrix.lua }},vendored" -- --ignored
|
||||
TRYBUILD=overwrite cargo test --features "${{ matrix.lua }},vendored,async,send,serialize,macros,parking_lot" -- --ignored
|
||||
TRYBUILD=overwrite cargo test --features "${{ matrix.lua }},vendored,async,send,serialize,macros,parking_lot,unstable" -- --ignored
|
||||
shell: bash
|
||||
|
||||
test_with_sanitizer:
|
||||
@@ -149,12 +150,13 @@ jobs:
|
||||
with:
|
||||
toolchain: ${{ matrix.rust }}
|
||||
target: ${{ matrix.target }}
|
||||
- uses: Swatinem/rust-cache@v1
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
- name: Run ${{ matrix.lua }} tests with address sanitizer
|
||||
run: |
|
||||
RUSTFLAGS="-Z sanitizer=address" \
|
||||
cargo test --tests --features "${{ matrix.lua }},vendored,async,send,serialize,macros,parking_lot" --target x86_64-unknown-linux-gnu -- --skip test_too_many_recursions
|
||||
cargo test --tests --features "${{ matrix.lua }},vendored,async,send,serialize,macros,parking_lot,unstable" --target x86_64-unknown-linux-gnu -- --skip test_too_many_recursions
|
||||
shell: bash
|
||||
env:
|
||||
RUSTFLAGS: -Z sanitizer=address
|
||||
|
||||
test_modules:
|
||||
name: Test modules
|
||||
@@ -176,7 +178,7 @@ jobs:
|
||||
with:
|
||||
toolchain: ${{ matrix.rust }}
|
||||
target: ${{ matrix.target }}
|
||||
- uses: Swatinem/rust-cache@v1
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
- name: Run ${{ matrix.lua }} module tests
|
||||
run: |
|
||||
(cd tests/module && cargo build --release --features "${{ matrix.lua }}")
|
||||
@@ -230,4 +232,4 @@ jobs:
|
||||
- uses: actions-rs/clippy-check@v1
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
args: --features "${{ matrix.lua }},vendored,async,send,serialize,macros,parking_lot"
|
||||
args: --features "${{ matrix.lua }},vendored,async,send,serialize,macros,parking_lot,unstable"
|
||||
|
||||
@@ -1,3 +1,58 @@
|
||||
## v0.9.0-beta.2
|
||||
|
||||
New features:
|
||||
- Added `Thread::set_hook()` function to set hook on threads
|
||||
- Added pretty print to the Debug formatting to Lua `Value` and `Table`
|
||||
- ffi layer moved to `mlua-sys` crate
|
||||
- Added OwnedString (unstable)
|
||||
|
||||
Breaking changes:
|
||||
- Refactor `HookTriggers` (make it const)
|
||||
|
||||
## v0.9.0-beta.1
|
||||
|
||||
New features:
|
||||
- Owned Lua types (unstable feature flag)
|
||||
- New functions `Function::wrap`/`Function::wrap_mut`/`Function::wrap_async`
|
||||
- `Lua::register_userdata_type()` to register a custom userdata types (without requiring `UserData` trait)
|
||||
- `Lua::create_any_userdata()`
|
||||
- Added `create_userdata_ref`/`create_userdata_ref_mut` for scopes
|
||||
- Added `AnyUserDataExt` trait with auxiliary functions for `AnyUserData`
|
||||
- Added `UserDataRef` and `UserDataRefMut` type wrapped that implement `FromLua`
|
||||
- Improved error handling:
|
||||
* Improved error reporting when calling Rust functions from Lua.
|
||||
* Added `Error::BadArgument` to help identify bad argument position or name
|
||||
* Added `ErrorContext` extension trait to attach additional context to `Error`
|
||||
|
||||
Breaking changes:
|
||||
- Refactored `AsChunk` trait
|
||||
- `ToLua`/`ToLuaMulti` renamed to `IntoLua`/`IntoLuaMulti`
|
||||
- Renamed `to_lua_err` to `into_lua_err`
|
||||
- Removed `FromLua` impl for `T: UserData+Clone`
|
||||
- Removed `Lua::async_scope`
|
||||
- Added `&Lua` arg to Luau interrupt callback
|
||||
|
||||
Other:
|
||||
- Better Debug for String
|
||||
- Allow deserializing values from serializable UserData using `Lua::from_value()` method
|
||||
- Added `Table::clear()` method
|
||||
- Added `Error::downcast_ref()` method
|
||||
- Support setting memory limit for Lua 5.1/JIT/Luau
|
||||
- Support setting module name in `#[lua_module(name = "...")]` macro
|
||||
- Minor fixes and improvements
|
||||
|
||||
## v0.8.8
|
||||
|
||||
- Fix potential deadlock when trying to reuse dropped registry keys.
|
||||
- Optimize userdata methods call when __index and fields_getters are nil
|
||||
|
||||
## v0.8.7
|
||||
|
||||
- Minimum Luau updated to 0.555 (`LUAI_MAXCSTACK` limit increased to 100000)
|
||||
- `_VERSION` in Luau now includes version number
|
||||
- Fixed lifetime of `DebugNames` in `Debug::names()` and `DebugSource` in `Debug::source()`
|
||||
- Fixed subtraction overflow when calculating index for `MultiValue::get()`
|
||||
|
||||
## v0.8.6
|
||||
|
||||
- Fixed bug when recycled Registry slot can be set to Nil
|
||||
|
||||
+21
-24
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "mlua"
|
||||
version = "0.8.6" # remember to update html_root_url and mlua_derive
|
||||
version = "0.9.0-beta.2" # remember to update mlua_derive
|
||||
authors = ["Aleksandr Orlenko <zxteam@pm.me>", "kyren <catherine@chucklefish.org>"]
|
||||
edition = "2021"
|
||||
repository = "https://github.com/khvzak/mlua"
|
||||
@@ -9,40 +9,40 @@ readme = "README.md"
|
||||
keywords = ["lua", "luajit", "luau", "async", "scripting"]
|
||||
categories = ["api-bindings", "asynchronous"]
|
||||
license = "MIT"
|
||||
links = "lua"
|
||||
build = "build/main.rs"
|
||||
description = """
|
||||
High level bindings to Lua 5.4/5.3/5.2/5.1 (including LuaJIT) and Roblox Luau
|
||||
with async/await features and support of writing native Lua modules in Rust.
|
||||
"""
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
features = ["lua54", "vendored", "async", "send", "serialize", "macros", "parking_lot"]
|
||||
features = ["lua54", "vendored", "async", "send", "serialize", "macros", "parking_lot", "unstable"]
|
||||
rustdoc-args = ["--cfg", "docsrs"]
|
||||
|
||||
[workspace]
|
||||
members = [
|
||||
"mlua_derive",
|
||||
"mlua-sys",
|
||||
]
|
||||
|
||||
[features]
|
||||
lua54 = []
|
||||
lua53 = []
|
||||
lua52 = []
|
||||
lua51 = []
|
||||
luajit = []
|
||||
luajit52 = ["luajit"]
|
||||
luau = ["luau0-src"]
|
||||
vendored = ["lua-src", "luajit-src"]
|
||||
module = ["mlua_derive"]
|
||||
lua54 = ["ffi/lua54"]
|
||||
lua53 = ["ffi/lua53"]
|
||||
lua52 = ["ffi/lua52"]
|
||||
lua51 = ["ffi/lua51"]
|
||||
luajit = ["ffi/luajit"]
|
||||
luajit52 = ["luajit", "ffi/luajit52"]
|
||||
luau = ["ffi/luau"]
|
||||
vendored = ["ffi/vendored"]
|
||||
module = ["mlua_derive", "ffi/module"]
|
||||
async = ["futures-core", "futures-task", "futures-util"]
|
||||
send = []
|
||||
serialize = ["serde", "erased-serde"]
|
||||
serialize = ["serde", "erased-serde", "serde-value"]
|
||||
macros = ["mlua_derive/macros"]
|
||||
unstable = []
|
||||
|
||||
[dependencies]
|
||||
mlua_derive = { version = "=0.8.0", optional = true, path = "mlua_derive" }
|
||||
bstr = { version = "0.2", features = ["std"], default_features = false }
|
||||
mlua_derive = { version = "=0.9.0-beta.1", optional = true, path = "mlua_derive" }
|
||||
bstr = { version = "1.0", features = ["std"], default_features = false }
|
||||
once_cell = { version = "1.0" }
|
||||
num-traits = { version = "0.2.14" }
|
||||
rustc-hash = "1.0"
|
||||
@@ -51,18 +51,14 @@ futures-task = { version = "0.3.5", optional = true }
|
||||
futures-util = { version = "0.3.5", optional = true }
|
||||
serde = { version = "1.0", optional = true }
|
||||
erased-serde = { version = "0.3", optional = true }
|
||||
serde-value = { version = "0.7", optional = true }
|
||||
parking_lot = { version = "0.12", optional = true }
|
||||
|
||||
[build-dependencies]
|
||||
cc = { version = "1.0" }
|
||||
pkg-config = { version = "0.3.17" }
|
||||
lua-src = { version = ">= 544.0.0, < 550.0.0", optional = true }
|
||||
luajit-src = { version = ">= 210.4.0, < 220.0.0", optional = true }
|
||||
luau0-src = { version = "0.5.0", optional = true }
|
||||
ffi = { package = "mlua-sys", version = "0.1.0", path = "mlua-sys" }
|
||||
|
||||
[dev-dependencies]
|
||||
rustyline = "10.0"
|
||||
criterion = { version = "0.3.4", features = ["html_reports", "async_tokio"] }
|
||||
rustyline = "11.0"
|
||||
criterion = { version = "0.4", features = ["html_reports", "async_tokio"] }
|
||||
trybuild = "1.0"
|
||||
futures = "0.3.5"
|
||||
hyper = { version = "0.14", features = ["client", "server"] }
|
||||
@@ -73,6 +69,7 @@ serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
maplit = "1.0"
|
||||
tempfile = "3"
|
||||
static_assertions = "1.0"
|
||||
|
||||
[[bench]]
|
||||
name = "benchmark"
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
[docs.rs]: https://docs.rs/mlua
|
||||
[Coverage Status]: https://codecov.io/gh/khvzak/mlua/branch/master/graph/badge.svg?token=99339FS1CG
|
||||
[codecov.io]: https://codecov.io/gh/khvzak/mlua
|
||||
[MSRV]: https://img.shields.io/badge/rust-1.56+-brightgreen.svg?&logo=rust
|
||||
[MSRV]: https://img.shields.io/badge/rust-1.63+-brightgreen.svg?&logo=rust
|
||||
|
||||
[Guided Tour] | [Benchmarks] | [FAQ]
|
||||
|
||||
@@ -17,6 +17,10 @@
|
||||
[Benchmarks]: https://github.com/khvzak/script-bench-rs
|
||||
[FAQ]: FAQ.md
|
||||
|
||||
> **Note**
|
||||
>
|
||||
> Please see the [v0.8](https://github.com/khvzak/mlua/tree/v0.8) branch for the stable versions of `mlua` released to crates.io.
|
||||
|
||||
`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.
|
||||
|
||||
@@ -48,6 +52,7 @@ Below is a list of the available feature flags. By default `mlua` does not enabl
|
||||
* `serialize`: add serialization and deserialization support to `mlua` types using [serde] framework
|
||||
* `macros`: enable procedural macros (such as `chunk!`)
|
||||
* `parking_lot`: support UserData types wrapped in [parking_lot]'s primitives (`Arc<Mutex>` and `Arc<RwLock>`)
|
||||
* `unstable`: enable **unstable** features. The public API of these features may break between releases.
|
||||
|
||||
[5.4]: https://www.lua.org/manual/5.4/manual.html
|
||||
[5.3]: https://www.lua.org/manual/5.3/manual.html
|
||||
|
||||
@@ -120,7 +120,7 @@ fn call_sum_callback(c: &mut Criterion) {
|
||||
}
|
||||
|
||||
fn call_async_sum_callback(c: &mut Criterion) {
|
||||
let options = LuaOptions::new().thread_cache_size(1024);
|
||||
let options = LuaOptions::new().thread_pool_size(1024);
|
||||
let lua = Lua::new_with(LuaStdLib::ALL_SAFE, options).unwrap();
|
||||
let callback = lua
|
||||
.create_async_function(|_, (a, b, c): (i64, i64, i64)| async move {
|
||||
@@ -272,7 +272,7 @@ fn call_async_userdata_method(c: &mut Criterion) {
|
||||
}
|
||||
}
|
||||
|
||||
let options = LuaOptions::new().thread_cache_size(1024);
|
||||
let options = LuaOptions::new().thread_pool_size(1024);
|
||||
let lua = Lua::new_with(LuaStdLib::ALL_SAFE, options).unwrap();
|
||||
lua.globals().set("userdata", UserData(10)).unwrap();
|
||||
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
use std::path::PathBuf;
|
||||
|
||||
pub fn probe_lua() -> Option<PathBuf> {
|
||||
None
|
||||
}
|
||||
-115
@@ -1,115 +0,0 @@
|
||||
#[cfg_attr(
|
||||
any(
|
||||
feature = "luau",
|
||||
all(
|
||||
feature = "vendored",
|
||||
any(
|
||||
feature = "lua54",
|
||||
feature = "lua53",
|
||||
feature = "lua52",
|
||||
feature = "lua51",
|
||||
feature = "luajit"
|
||||
)
|
||||
)
|
||||
),
|
||||
path = "find_vendored.rs"
|
||||
)]
|
||||
#[cfg_attr(
|
||||
all(
|
||||
not(feature = "vendored"),
|
||||
any(
|
||||
feature = "lua54",
|
||||
feature = "lua53",
|
||||
feature = "lua52",
|
||||
feature = "lua51",
|
||||
feature = "luajit"
|
||||
)
|
||||
),
|
||||
path = "find_normal.rs"
|
||||
)]
|
||||
#[cfg_attr(
|
||||
not(any(
|
||||
feature = "lua54",
|
||||
feature = "lua53",
|
||||
feature = "lua52",
|
||||
feature = "lua51",
|
||||
feature = "luajit",
|
||||
feature = "luau"
|
||||
)),
|
||||
path = "find_dummy.rs"
|
||||
)]
|
||||
mod find;
|
||||
|
||||
fn main() {
|
||||
#[cfg(not(any(
|
||||
feature = "lua54",
|
||||
feature = "lua53",
|
||||
feature = "lua52",
|
||||
feature = "lua51",
|
||||
feature = "luajit",
|
||||
feature = "luau"
|
||||
)))]
|
||||
compile_error!(
|
||||
"You must enable one of the features: lua54, lua53, lua52, lua51, luajit, luajit52, luau"
|
||||
);
|
||||
|
||||
#[cfg(all(
|
||||
feature = "lua54",
|
||||
any(
|
||||
feature = "lua53",
|
||||
feature = "lua52",
|
||||
feature = "lua51",
|
||||
feature = "luajit",
|
||||
feature = "luau"
|
||||
)
|
||||
))]
|
||||
compile_error!(
|
||||
"You can enable only one of the features: lua54, lua53, lua52, lua51, luajit, luajit52, luau"
|
||||
);
|
||||
|
||||
#[cfg(all(
|
||||
feature = "lua53",
|
||||
any(
|
||||
feature = "lua52",
|
||||
feature = "lua51",
|
||||
feature = "luajit",
|
||||
feature = "luau"
|
||||
)
|
||||
))]
|
||||
compile_error!(
|
||||
"You can enable only one of the features: lua54, lua53, lua52, lua51, luajit, luajit52, luau"
|
||||
);
|
||||
|
||||
#[cfg(all(
|
||||
feature = "lua52",
|
||||
any(feature = "lua51", feature = "luajit", feature = "luau")
|
||||
))]
|
||||
compile_error!(
|
||||
"You can enable only one of the features: lua54, lua53, lua52, lua51, luajit, luajit52, luau"
|
||||
);
|
||||
|
||||
#[cfg(all(feature = "lua51", any(feature = "luajit", feature = "luau")))]
|
||||
compile_error!(
|
||||
"You can enable only one of the features: lua54, lua53, lua52, lua51, luajit, luajit52, luau"
|
||||
);
|
||||
|
||||
#[cfg(all(feature = "luajit", feature = "luau"))]
|
||||
compile_error!(
|
||||
"You can enable only one of the features: lua54, lua53, lua52, lua51, luajit, luajit52, luau"
|
||||
);
|
||||
|
||||
// We don't support "vendored module" mode on windows
|
||||
#[cfg(all(feature = "vendored", feature = "module", target_os = "windows"))]
|
||||
compile_error!(
|
||||
"Vendored (static) builds are not supported for modules on Windows.\n"
|
||||
+ "Please, use `pkg-config` or custom mode to link to a Lua dll."
|
||||
);
|
||||
|
||||
#[cfg(all(feature = "luau", feature = "module"))]
|
||||
compile_error!("Luau does not support module mode");
|
||||
|
||||
#[cfg(any(not(feature = "module"), target_os = "windows"))]
|
||||
find::probe_lua();
|
||||
|
||||
println!("cargo:rerun-if-changed=build");
|
||||
}
|
||||
@@ -12,7 +12,7 @@ impl UserData for BodyReader {
|
||||
methods.add_async_function("read", |lua, reader: AnyUserData| async move {
|
||||
let mut reader = reader.borrow_mut::<Self>()?;
|
||||
if let Some(bytes) = reader.0.data().await {
|
||||
let bytes = bytes.to_lua_err()?;
|
||||
let bytes = bytes.into_lua_err()?;
|
||||
return Some(lua.create_string(&bytes)).transpose();
|
||||
}
|
||||
Ok(None)
|
||||
@@ -26,8 +26,8 @@ async fn main() -> Result<()> {
|
||||
|
||||
let fetch_url = lua.create_async_function(|lua, uri: String| async move {
|
||||
let client = HyperClient::new();
|
||||
let uri = uri.parse().to_lua_err()?;
|
||||
let resp = client.get(uri).await.to_lua_err()?;
|
||||
let uri = uri.parse().into_lua_err()?;
|
||||
let resp = client.get(uri).await.into_lua_err()?;
|
||||
|
||||
let lua_resp = lua.create_table()?;
|
||||
lua_resp.set("status", resp.status().as_u16())?;
|
||||
@@ -37,7 +37,7 @@ async fn main() -> Result<()> {
|
||||
headers
|
||||
.entry(key.as_str())
|
||||
.or_insert(Vec::new())
|
||||
.push(value.to_str().to_lua_err()?);
|
||||
.push(value.to_str().into_lua_err()?);
|
||||
}
|
||||
|
||||
lua_resp.set("headers", headers)?;
|
||||
|
||||
@@ -10,8 +10,8 @@ async fn main() -> Result<()> {
|
||||
let resp = reqwest::get(&uri)
|
||||
.await
|
||||
.and_then(|resp| resp.error_for_status())
|
||||
.to_lua_err()?;
|
||||
let json = resp.json::<serde_json::Value>().await.to_lua_err()?;
|
||||
.into_lua_err()?;
|
||||
let json = resp.json::<serde_json::Value>().await.into_lua_err()?;
|
||||
lua.to_value(&json)
|
||||
})?;
|
||||
|
||||
|
||||
+14
-2
@@ -1,7 +1,9 @@
|
||||
use std::f32;
|
||||
use std::iter::FromIterator;
|
||||
|
||||
use mlua::{chunk, Function, Lua, MetaMethod, Result, UserData, UserDataMethods, Variadic};
|
||||
use mlua::{
|
||||
chunk, FromLua, Function, Lua, MetaMethod, Result, UserData, UserDataMethods, Value, Variadic,
|
||||
};
|
||||
|
||||
fn main() -> Result<()> {
|
||||
// You can create a new Lua state with `Lua::new()`. This loads the default Lua std library
|
||||
@@ -30,7 +32,7 @@ fn main() -> Result<()> {
|
||||
global = 'foo'..'bar'
|
||||
"#,
|
||||
)
|
||||
.set_name("example code")?
|
||||
.set_name("example code")
|
||||
.exec()?;
|
||||
assert_eq!(globals.get::<_, String>("global")?, "foobar");
|
||||
|
||||
@@ -151,6 +153,16 @@ fn main() -> Result<()> {
|
||||
#[derive(Copy, Clone)]
|
||||
struct Vec2(f32, f32);
|
||||
|
||||
// We can implement `FromLua` trait for our `Vec2` to return a copy
|
||||
impl<'lua> FromLua<'lua> for Vec2 {
|
||||
fn from_lua(value: Value<'lua>, _: &'lua Lua) -> Result<Self> {
|
||||
match value {
|
||||
Value::UserData(ud) => Ok(*ud.borrow::<Self>()?),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl UserData for Vec2 {
|
||||
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
|
||||
methods.add_method("magnitude", |_, vec, ()| {
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
name = "rust_module"
|
||||
version = "0.0.0"
|
||||
authors = ["Aleksandr Orlenko <zxteam@pm.me>"]
|
||||
edition = "2018"
|
||||
edition = "2021"
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib"]
|
||||
|
||||
+2
-2
@@ -5,7 +5,7 @@ use rustyline::Editor;
|
||||
|
||||
fn main() {
|
||||
let lua = Lua::new();
|
||||
let mut editor = Editor::<()>::new().expect("Failed to make rustyline editor");
|
||||
let mut editor = Editor::<(), _>::new().expect("Failed to make rustyline editor");
|
||||
|
||||
loop {
|
||||
let mut prompt = "> ";
|
||||
@@ -19,7 +19,7 @@ fn main() {
|
||||
|
||||
match lua.load(&line).eval::<MultiValue>() {
|
||||
Ok(values) => {
|
||||
editor.add_history_entry(line);
|
||||
editor.add_history_entry(line).unwrap();
|
||||
println!(
|
||||
"{}",
|
||||
values
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
[package]
|
||||
name = "mlua-sys"
|
||||
version = "0.1.1"
|
||||
authors = ["Aleksandr Orlenko <zxteam@pm.me>"]
|
||||
edition = "2021"
|
||||
repository = "https://github.com/khvzak/mlua"
|
||||
documentation = "https://docs.rs/mlua-sys"
|
||||
readme = "README.md"
|
||||
categories = ["external-ffi-bindings"]
|
||||
license = "MIT"
|
||||
links = "lua"
|
||||
build = "build/main.rs"
|
||||
description = """
|
||||
Low level (FFI) bindings to Lua 5.4/5.3/5.2/5.1 (including LuaJIT) and Roblox Luau
|
||||
"""
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
features = ["lua54", "vendored"]
|
||||
rustdoc-args = ["--cfg", "docsrs"]
|
||||
|
||||
[features]
|
||||
lua54 = []
|
||||
lua53 = []
|
||||
lua52 = []
|
||||
lua51 = []
|
||||
luajit = []
|
||||
luajit52 = ["luajit"]
|
||||
luau = ["luau0-src"]
|
||||
vendored = ["lua-src", "luajit-src"]
|
||||
module = []
|
||||
|
||||
[dependencies]
|
||||
|
||||
[build-dependencies]
|
||||
cc = "1.0"
|
||||
cfg-if = "1.0"
|
||||
pkg-config = "0.3.17"
|
||||
lua-src = { version = ">= 544.0.0, < 550.0.0", optional = true }
|
||||
luajit-src = { version = ">= 210.4.0, < 220.0.0", optional = true }
|
||||
luau0-src = { version = "0.5.6", optional = true }
|
||||
@@ -0,0 +1,8 @@
|
||||
# mlua-sys
|
||||
|
||||
Low level (FFI) bindings to Lua 5.4/5.3/5.2/5.1 (including LuaJIT) and Roblox [Luau].
|
||||
|
||||
Intended to be consumed by the [mlua] crate.
|
||||
|
||||
[Luau]: https://github.com/Roblox/luau
|
||||
[mlua]: https://crates.io/crates/mlua
|
||||
@@ -8,7 +8,7 @@ fn get_env_var(name: &str) -> String {
|
||||
match env::var(name) {
|
||||
Ok(val) => val,
|
||||
Err(env::VarError::NotPresent) => String::new(),
|
||||
Err(err) => panic!("cannot get {}: {}", name, err),
|
||||
Err(err) => panic!("cannot get {name}: {err}"),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,8 +37,8 @@ pub fn probe_lua() -> Option<PathBuf> {
|
||||
if get_env_var("LUA_LINK") == "static" {
|
||||
link_lib = "static=";
|
||||
};
|
||||
println!("cargo:rustc-link-search=native={}", lib_dir);
|
||||
println!("cargo:rustc-link-lib={}{}", link_lib, lua_lib);
|
||||
println!("cargo:rustc-link-search=native={lib_dir}");
|
||||
println!("cargo:rustc-link-lib={link_lib}{lua_lib}");
|
||||
}
|
||||
return Some(PathBuf::from(include_dir));
|
||||
}
|
||||
@@ -72,7 +72,7 @@ pub fn probe_lua() -> Option<PathBuf> {
|
||||
.probe(alt_probe);
|
||||
}
|
||||
|
||||
lua.unwrap_or_else(|_| panic!("cannot find Lua {} using `pkg-config`", ver))
|
||||
lua.unwrap_or_else(|_| panic!("cannot find Lua {ver} using `pkg-config`"))
|
||||
.include_paths
|
||||
.get(0)
|
||||
.cloned()
|
||||
@@ -0,0 +1,19 @@
|
||||
cfg_if::cfg_if! {
|
||||
if #[cfg(all(feature = "lua54", not(any(feature = "lua53", feature = "lua52", feature = "lua51", feature = "luajit", feature = "luau"))))] {
|
||||
include!("main_inner.rs");
|
||||
} else if #[cfg(all(feature = "lua53", not(any(feature = "lua54", feature = "lua52", feature = "lua51", feature = "luajit", feature = "luau"))))] {
|
||||
include!("main_inner.rs");
|
||||
} else if #[cfg(all(feature = "lua52", not(any(feature = "lua54", feature = "lua53", feature = "lua51", feature = "luajit", feature = "luau"))))] {
|
||||
include!("main_inner.rs");
|
||||
} else if #[cfg(all(feature = "lua51", not(any(feature = "lua54", feature = "lua53", feature = "lua52", feature = "luajit", feature = "luau"))))] {
|
||||
include!("main_inner.rs");
|
||||
} else if #[cfg(all(feature = "luajit", not(any(feature = "lua54", feature = "lua53", feature = "lua52", feature = "lua51", feature = "luau"))))] {
|
||||
include!("main_inner.rs");
|
||||
} else if #[cfg(all(feature = "luau", not(any(feature = "lua54", feature = "lua53", feature = "lua52", feature = "lua51", feature = "luajit"))))] {
|
||||
include!("main_inner.rs");
|
||||
} else {
|
||||
fn main() {
|
||||
compile_error!("You can enable only one of the features: lua54, lua53, lua52, lua51, luajit, luajit52, luau");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
cfg_if::cfg_if! {
|
||||
if #[cfg(any(feature = "luau", feature = "vendored"))] {
|
||||
#[path = "find_vendored.rs"]
|
||||
mod find;
|
||||
} else {
|
||||
#[path = "find_normal.rs"]
|
||||
mod find;
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
// We don't support "vendored module" mode on windows
|
||||
#[cfg(all(feature = "vendored", feature = "module", target_os = "windows"))]
|
||||
compile_error!(
|
||||
"Vendored (static) builds are not supported for modules on Windows.\n"
|
||||
+ "Please, use `pkg-config` or custom mode to link to a Lua dll."
|
||||
);
|
||||
|
||||
#[cfg(all(feature = "luau", feature = "module"))]
|
||||
compile_error!("Luau does not support module mode");
|
||||
|
||||
#[cfg(any(not(feature = "module"), target_os = "windows"))]
|
||||
find::probe_lua();
|
||||
|
||||
println!("cargo:rerun-if-changed=build");
|
||||
}
|
||||
@@ -1,38 +1,46 @@
|
||||
//! Low level bindings to Lua 5.4/5.3/5.2/5.1 including LuaJIT.
|
||||
//! Low level bindings to Lua 5.4/5.3/5.2/5.1 (including LuaJIT) and Roblox Luau.
|
||||
|
||||
#![allow(non_camel_case_types, non_snake_case, dead_code)]
|
||||
#![allow(clippy::missing_safety_doc)]
|
||||
#![doc(test(attr(deny(warnings))))]
|
||||
#![cfg_attr(docsrs, feature(doc_cfg))]
|
||||
|
||||
use std::os::raw::c_int;
|
||||
|
||||
#[cfg(feature = "lua54")]
|
||||
#[cfg(any(feature = "lua54", doc))]
|
||||
pub use lua54::*;
|
||||
|
||||
#[cfg(feature = "lua53")]
|
||||
#[cfg(any(feature = "lua53", doc))]
|
||||
pub use lua53::*;
|
||||
|
||||
#[cfg(feature = "lua52")]
|
||||
#[cfg(any(feature = "lua52", doc))]
|
||||
pub use lua52::*;
|
||||
|
||||
#[cfg(any(feature = "lua51", feature = "luajit"))]
|
||||
#[cfg(any(feature = "lua51", feature = "luajit", doc))]
|
||||
pub use lua51::*;
|
||||
|
||||
#[cfg(feature = "luau")]
|
||||
#[cfg(any(feature = "luau", doc))]
|
||||
pub use luau::*;
|
||||
|
||||
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
|
||||
#[doc(hidden)]
|
||||
pub const LUA_MAX_UPVALUES: c_int = 255;
|
||||
|
||||
#[cfg(any(feature = "lua51", all(feature = "luajit", not(feature = "vendored"))))]
|
||||
#[doc(hidden)]
|
||||
pub const LUA_MAX_UPVALUES: c_int = 60;
|
||||
|
||||
#[cfg(all(feature = "luajit", feature = "vendored"))]
|
||||
#[doc(hidden)]
|
||||
pub const LUA_MAX_UPVALUES: c_int = 120;
|
||||
|
||||
#[cfg(feature = "luau")]
|
||||
#[doc(hidden)]
|
||||
pub const LUA_MAX_UPVALUES: c_int = 200;
|
||||
|
||||
// I believe `luaL_traceback` < 5.4 requires this much free stack to not error.
|
||||
// 5.4 uses `luaL_Buffer`
|
||||
#[doc(hidden)]
|
||||
pub const LUA_TRACEBACK_STACK: c_int = 11;
|
||||
|
||||
// The minimum alignment guaranteed by the architecture. This value is used to
|
||||
@@ -51,6 +59,7 @@ pub const LUA_TRACEBACK_STACK: c_int = 11;
|
||||
all(target_arch = "riscv32", not(target_os = "espidf")),
|
||||
all(target_arch = "xtensa", not(target_os = "espidf")),
|
||||
)))]
|
||||
#[doc(hidden)]
|
||||
pub const SYS_MIN_ALIGN: usize = 8;
|
||||
#[cfg(all(any(
|
||||
target_arch = "x86_64",
|
||||
@@ -61,43 +70,35 @@ pub const SYS_MIN_ALIGN: usize = 8;
|
||||
target_arch = "riscv64",
|
||||
target_arch = "wasm64",
|
||||
)))]
|
||||
#[doc(hidden)]
|
||||
pub const SYS_MIN_ALIGN: usize = 16;
|
||||
// The allocator on the esp-idf platform guarentees 4 byte alignment.
|
||||
#[cfg(all(any(
|
||||
all(target_arch = "riscv32", target_os = "espidf"),
|
||||
all(target_arch = "xtensa", target_os = "espidf"),
|
||||
)))]
|
||||
#[doc(hidden)]
|
||||
pub const SYS_MIN_ALIGN: usize = 4;
|
||||
|
||||
// Hack to avoid stripping a few unused Lua symbols that could be imported
|
||||
// by C modules in unsafe mode
|
||||
#[cfg(not(feature = "luau"))]
|
||||
pub(crate) fn keep_lua_symbols() {
|
||||
let mut symbols: Vec<*const extern "C" fn()> = Vec::new();
|
||||
symbols.push(lua_atpanic as _);
|
||||
symbols.push(lua_isuserdata as _);
|
||||
symbols.push(lua_tocfunction as _);
|
||||
symbols.push(luaL_loadstring as _);
|
||||
symbols.push(luaL_openlibs as _);
|
||||
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
|
||||
{
|
||||
symbols.push(lua_getglobal as _);
|
||||
symbols.push(lua_setglobal as _);
|
||||
symbols.push(luaL_setfuncs as _);
|
||||
}
|
||||
}
|
||||
#[macro_use]
|
||||
mod macros;
|
||||
|
||||
#[cfg(feature = "lua54")]
|
||||
#[cfg(any(feature = "lua54", doc))]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "lua54")))]
|
||||
pub mod lua54;
|
||||
|
||||
#[cfg(feature = "lua53")]
|
||||
#[cfg(any(feature = "lua53", doc))]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "lua53")))]
|
||||
pub mod lua53;
|
||||
|
||||
#[cfg(feature = "lua52")]
|
||||
#[cfg(any(feature = "lua52", doc))]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "lua52")))]
|
||||
pub mod lua52;
|
||||
|
||||
#[cfg(any(feature = "lua51", feature = "luajit"))]
|
||||
#[cfg(any(feature = "lua51", feature = "luajit", doc))]
|
||||
#[cfg_attr(docsrs, doc(cfg(any(feature = "lua51", feature = "luajit"))))]
|
||||
pub mod lua51;
|
||||
|
||||
#[cfg(feature = "luau")]
|
||||
#[cfg(any(feature = "luau", doc))]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
|
||||
pub mod luau;
|
||||
@@ -1,4 +1,4 @@
|
||||
//! MLua compatibility layer for Lua 5.2
|
||||
//! MLua compatibility layer for Lua 5.3
|
||||
|
||||
use std::os::raw::c_int;
|
||||
|
||||
@@ -74,6 +74,7 @@ pub type lua_Continuation = unsafe extern "C" fn(L: *mut lua_State, status: c_in
|
||||
|
||||
/// Type for userdata destructor functions.
|
||||
pub type lua_Udestructor = unsafe extern "C" fn(*mut c_void);
|
||||
pub type lua_Destructor = unsafe extern "C" fn(L: *mut lua_State, *mut c_void);
|
||||
|
||||
/// Type for memory-allocation functions.
|
||||
pub type lua_Alloc = unsafe extern "C" fn(
|
||||
@@ -265,11 +266,8 @@ extern "C" {
|
||||
// TODO: lua_encodepointer
|
||||
pub fn lua_clock() -> c_double;
|
||||
pub fn lua_setuserdatatag(L: *mut lua_State, idx: c_int, tag: c_int);
|
||||
pub fn lua_setuserdatadtor(
|
||||
L: *mut lua_State,
|
||||
tag: c_int,
|
||||
dtor: Option<unsafe extern "C" fn(*mut lua_State, *mut c_void)>,
|
||||
);
|
||||
pub fn lua_setuserdatadtor(L: *mut lua_State, tag: c_int, dtor: Option<lua_Destructor>);
|
||||
pub fn lua_getuserdatadtor(L: *mut lua_State, tag: c_int) -> Option<lua_Destructor>;
|
||||
pub fn lua_clonefunction(L: *mut lua_State, idx: c_int);
|
||||
pub fn lua_cleartable(L: *mut lua_State, idx: c_int);
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
#[allow(unused_macros)]
|
||||
macro_rules! cstr {
|
||||
($s:expr) => {
|
||||
concat!($s, "\0") as *const str as *const [::std::os::raw::c_char]
|
||||
as *const ::std::os::raw::c_char
|
||||
};
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
[package]
|
||||
name = "mlua_derive"
|
||||
version = "0.8.0"
|
||||
version = "0.9.0-beta.1"
|
||||
authors = ["Aleksandr Orlenko <zxteam@pm.me>"]
|
||||
edition = "2018"
|
||||
edition = "2021"
|
||||
description = "Procedural macros for the mlua crate."
|
||||
repository = "https://github.com/khvzak/mlua"
|
||||
keywords = ["lua", "mlua"]
|
||||
@@ -18,7 +18,7 @@ macros = ["proc-macro-error", "itertools", "regex", "once_cell"]
|
||||
quote = "1.0"
|
||||
proc-macro2 = { version = "1.0", features = ["span-locations"] }
|
||||
proc-macro-error = { version = "1.0", optional = true }
|
||||
syn = { version = "1.0", features = ["full"] }
|
||||
syn = { version = "2.0", features = ["full"] }
|
||||
itertools = { version = "0.10", optional = true }
|
||||
regex = { version = "1.4", optional = true }
|
||||
once_cell = { version = "1.0", optional = true }
|
||||
|
||||
+46
-24
@@ -1,7 +1,8 @@
|
||||
use proc_macro::TokenStream;
|
||||
use proc_macro2::{Ident, Span};
|
||||
use quote::quote;
|
||||
use syn::{parse_macro_input, AttributeArgs, Error, ItemFn};
|
||||
use syn::meta::ParseNestedMeta;
|
||||
use syn::{parse_macro_input, ItemFn, LitStr, Result};
|
||||
|
||||
#[cfg(feature = "macros")]
|
||||
use {
|
||||
@@ -9,19 +10,41 @@ use {
|
||||
proc_macro_error::proc_macro_error,
|
||||
};
|
||||
|
||||
#[derive(Default)]
|
||||
struct ModuleAttributes {
|
||||
name: Option<Ident>,
|
||||
}
|
||||
|
||||
impl ModuleAttributes {
|
||||
fn parse(&mut self, meta: ParseNestedMeta) -> Result<()> {
|
||||
if meta.path.is_ident("name") {
|
||||
match meta.value() {
|
||||
Ok(value) => {
|
||||
self.name = Some(value.parse::<LitStr>()?.parse()?);
|
||||
}
|
||||
Err(_) => {
|
||||
return Err(meta.error("`name` attribute must have a value"));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return Err(meta.error("unsupported module attribute"));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[proc_macro_attribute]
|
||||
pub fn lua_module(attr: TokenStream, item: TokenStream) -> TokenStream {
|
||||
let args = parse_macro_input!(attr as AttributeArgs);
|
||||
let func = parse_macro_input!(item as ItemFn);
|
||||
|
||||
if !args.is_empty() {
|
||||
let err = Error::new(Span::call_site(), "the macro does not support arguments")
|
||||
.to_compile_error();
|
||||
return err.into();
|
||||
let mut args = ModuleAttributes::default();
|
||||
if !attr.is_empty() {
|
||||
let args_parser = syn::meta::parser(|meta| args.parse(meta));
|
||||
parse_macro_input!(attr with args_parser);
|
||||
}
|
||||
|
||||
let func_name = func.sig.ident.clone();
|
||||
let ext_entrypoint_name = Ident::new(&format!("luaopen_{}", func_name), Span::call_site());
|
||||
let func = parse_macro_input!(item as ItemFn);
|
||||
let func_name = &func.sig.ident;
|
||||
let module_name = args.name.unwrap_or_else(|| func_name.clone());
|
||||
let ext_entrypoint_name = Ident::new(&format!("luaopen_{module_name}"), Span::call_site());
|
||||
|
||||
let wrapped = quote! {
|
||||
::mlua::require_module_feature!();
|
||||
@@ -64,37 +87,36 @@ pub fn chunk(input: TokenStream) -> TokenStream {
|
||||
use ::mlua::{AsChunk, ChunkMode, Lua, Result, Value};
|
||||
use ::std::borrow::Cow;
|
||||
use ::std::io::Result as IoResult;
|
||||
use ::std::marker::PhantomData;
|
||||
use ::std::sync::Mutex;
|
||||
|
||||
fn annotate<'a, F: FnOnce(&'a Lua) -> Result<Value<'a>>>(f: F) -> F { f }
|
||||
struct InnerChunk<F: for <'a> FnOnce(&'a Lua) -> Result<Value<'a>>>(Mutex<Option<F>>);
|
||||
|
||||
struct InnerChunk<'a, F: FnOnce(&'a Lua) -> Result<Value<'a>>>(Mutex<Option<F>>, PhantomData<&'a ()>);
|
||||
|
||||
impl<'lua, F> AsChunk<'lua> for InnerChunk<'lua, F>
|
||||
impl<F> AsChunk<'static> for InnerChunk<F>
|
||||
where
|
||||
F: FnOnce(&'lua Lua) -> Result<Value<'lua>>,
|
||||
F: for <'a> FnOnce(&'a Lua) -> Result<Value<'a>>,
|
||||
{
|
||||
fn source(&self) -> IoResult<Cow<[u8]>> {
|
||||
Ok(Cow::Borrowed((#source).as_bytes()))
|
||||
}
|
||||
|
||||
fn env(&self, lua: &'lua Lua) -> Result<Option<Value<'lua>>> {
|
||||
fn env<'lua>(&self, lua: &'lua Lua) -> Result<Value<'lua>> {
|
||||
if #caps_len > 0 {
|
||||
if let Ok(mut make_env) = self.0.lock() {
|
||||
if let Some(make_env) = make_env.take() {
|
||||
return make_env(lua).map(Some);
|
||||
return make_env(lua);
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(None)
|
||||
Ok(Value::Nil)
|
||||
}
|
||||
|
||||
fn mode(&self) -> Option<ChunkMode> {
|
||||
Some(ChunkMode::Text)
|
||||
}
|
||||
|
||||
fn source(self) -> IoResult<Cow<'static, [u8]>> {
|
||||
Ok(Cow::Borrowed((#source).as_bytes()))
|
||||
}
|
||||
}
|
||||
|
||||
fn annotate<F: for<'a> FnOnce(&'a Lua) -> Result<Value<'a>>>(f: F) -> F { f }
|
||||
|
||||
let make_env = annotate(move |lua: &Lua| -> Result<Value> {
|
||||
let globals = lua.globals();
|
||||
let env = lua.create_table()?;
|
||||
@@ -109,7 +131,7 @@ pub fn chunk(input: TokenStream) -> TokenStream {
|
||||
Ok(Value::Table(env))
|
||||
});
|
||||
|
||||
&InnerChunk(Mutex::new(Some(make_env)), PhantomData)
|
||||
InnerChunk(Mutex::new(Some(make_env)))
|
||||
}};
|
||||
|
||||
wrapped_code.into()
|
||||
|
||||
@@ -59,7 +59,7 @@ fn parse_pos(span: &Span) -> Option<(usize, usize)> {
|
||||
|
||||
static RE: Lazy<Regex> = Lazy::new(|| Regex::new(r"bytes\(([0-9]+)\.\.([0-9]+)\)").unwrap());
|
||||
|
||||
match RE.captures(&format!("{:?}", span)) {
|
||||
match RE.captures(&format!("{span:?}")) {
|
||||
Some(caps) => match (caps.get(1), caps.get(2)) {
|
||||
(Some(start), Some(end)) => Some((
|
||||
match start.as_str().parse() {
|
||||
|
||||
+63
-61
@@ -6,10 +6,9 @@ use std::path::{Path, PathBuf};
|
||||
use std::string::String as StdString;
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
use crate::ffi;
|
||||
use crate::function::Function;
|
||||
use crate::lua::Lua;
|
||||
use crate::value::{FromLuaMulti, ToLua, ToLuaMulti, Value};
|
||||
use crate::value::{FromLuaMulti, IntoLua, IntoLuaMulti, Value};
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
use {futures_core::future::LocalBoxFuture, futures_util::future};
|
||||
@@ -18,10 +17,7 @@ use {futures_core::future::LocalBoxFuture, futures_util::future};
|
||||
///
|
||||
/// [loadable by Lua]: https://www.lua.org/manual/5.4/manual.html#3.3.2
|
||||
/// [`Chunk`]: crate::Chunk
|
||||
pub trait AsChunk<'lua> {
|
||||
/// Returns chunk data (can be text or binary)
|
||||
fn source(&self) -> IoResult<Cow<[u8]>>;
|
||||
|
||||
pub trait AsChunk<'a> {
|
||||
/// Returns optional chunk name
|
||||
fn name(&self) -> Option<StdString> {
|
||||
None
|
||||
@@ -30,58 +26,74 @@ pub trait AsChunk<'lua> {
|
||||
/// Returns optional chunk [environment]
|
||||
///
|
||||
/// [environment]: https://www.lua.org/manual/5.4/manual.html#2.2
|
||||
fn env(&self, _lua: &'lua Lua) -> Result<Option<Value<'lua>>> {
|
||||
Ok(None)
|
||||
fn env<'lua>(&self, lua: &'lua Lua) -> Result<Value<'lua>> {
|
||||
let _lua = lua; // suppress warning
|
||||
Ok(Value::Nil)
|
||||
}
|
||||
|
||||
/// Returns optional chunk mode (text or binary)
|
||||
fn mode(&self) -> Option<ChunkMode> {
|
||||
None
|
||||
}
|
||||
|
||||
/// Returns chunk data (can be text or binary)
|
||||
fn source(self) -> IoResult<Cow<'a, [u8]>>;
|
||||
}
|
||||
|
||||
impl<'lua> AsChunk<'lua> for str {
|
||||
fn source(&self) -> IoResult<Cow<[u8]>> {
|
||||
impl<'a> AsChunk<'a> for &'a str {
|
||||
fn source(self) -> IoResult<Cow<'a, [u8]>> {
|
||||
Ok(Cow::Borrowed(self.as_ref()))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> AsChunk<'lua> for StdString {
|
||||
fn source(&self) -> IoResult<Cow<[u8]>> {
|
||||
impl AsChunk<'static> for StdString {
|
||||
fn source(self) -> IoResult<Cow<'static, [u8]>> {
|
||||
Ok(Cow::Owned(self.into_bytes()))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> AsChunk<'a> for &'a StdString {
|
||||
fn source(self) -> IoResult<Cow<'a, [u8]>> {
|
||||
Ok(Cow::Borrowed(self.as_bytes()))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> AsChunk<'a> for &'a [u8] {
|
||||
fn source(self) -> IoResult<Cow<'a, [u8]>> {
|
||||
Ok(Cow::Borrowed(self))
|
||||
}
|
||||
}
|
||||
|
||||
impl AsChunk<'static> for Vec<u8> {
|
||||
fn source(self) -> IoResult<Cow<'static, [u8]>> {
|
||||
Ok(Cow::Owned(self))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> AsChunk<'a> for &'a Vec<u8> {
|
||||
fn source(self) -> IoResult<Cow<'a, [u8]>> {
|
||||
Ok(Cow::Borrowed(self.as_ref()))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> AsChunk<'lua> for [u8] {
|
||||
fn source(&self) -> IoResult<Cow<[u8]>> {
|
||||
Ok(Cow::Borrowed(self))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> AsChunk<'lua> for Vec<u8> {
|
||||
fn source(&self) -> IoResult<Cow<[u8]>> {
|
||||
Ok(Cow::Borrowed(self))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> AsChunk<'lua> for Path {
|
||||
fn source(&self) -> IoResult<Cow<[u8]>> {
|
||||
std::fs::read(self).map(Cow::Owned)
|
||||
}
|
||||
|
||||
impl AsChunk<'static> for &Path {
|
||||
fn name(&self) -> Option<StdString> {
|
||||
Some(format!("@{}", self.display()))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> AsChunk<'lua> for PathBuf {
|
||||
fn source(&self) -> IoResult<Cow<[u8]>> {
|
||||
fn source(self) -> IoResult<Cow<'static, [u8]>> {
|
||||
std::fs::read(self).map(Cow::Owned)
|
||||
}
|
||||
}
|
||||
|
||||
impl AsChunk<'static> for PathBuf {
|
||||
fn name(&self) -> Option<StdString> {
|
||||
Some(format!("@{}", self.display()))
|
||||
}
|
||||
|
||||
fn source(self) -> IoResult<Cow<'static, [u8]>> {
|
||||
std::fs::read(self).map(Cow::Owned)
|
||||
}
|
||||
}
|
||||
|
||||
/// Returned from [`Lua::load`] and is used to finalize loading and executing Lua main chunks.
|
||||
@@ -90,10 +102,10 @@ impl<'lua> AsChunk<'lua> for PathBuf {
|
||||
#[must_use = "`Chunk`s do nothing unless one of `exec`, `eval`, `call`, or `into_function` are called on them"]
|
||||
pub struct Chunk<'lua, 'a> {
|
||||
pub(crate) lua: &'lua Lua,
|
||||
pub(crate) source: IoResult<Cow<'a, [u8]>>,
|
||||
pub(crate) name: Option<StdString>,
|
||||
pub(crate) env: Result<Option<Value<'lua>>>,
|
||||
pub(crate) name: StdString,
|
||||
pub(crate) env: Result<Value<'lua>>,
|
||||
pub(crate) mode: Option<ChunkMode>,
|
||||
pub(crate) source: IoResult<Cow<'a, [u8]>>,
|
||||
#[cfg(feature = "luau")]
|
||||
pub(crate) compiler: Option<Compiler>,
|
||||
}
|
||||
@@ -237,11 +249,9 @@ impl Compiler {
|
||||
|
||||
impl<'lua, 'a> Chunk<'lua, 'a> {
|
||||
/// Sets the name of this chunk, which results in more informative error traces.
|
||||
pub fn set_name(mut self, name: impl AsRef<str>) -> Result<Self> {
|
||||
self.name = Some(name.as_ref().to_string());
|
||||
// Do extra validation
|
||||
let _ = self.convert_name()?;
|
||||
Ok(self)
|
||||
pub fn set_name(mut self, name: impl Into<String>) -> Self {
|
||||
self.name = name.into();
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the first upvalue (`_ENV`) of the loaded chunk to the given value.
|
||||
@@ -255,10 +265,9 @@ impl<'lua, 'a> Chunk<'lua, 'a> {
|
||||
/// All global variables (including the standard library!) are looked up in `_ENV`, so it may be
|
||||
/// necessary to populate the environment in order for scripts using custom environments to be
|
||||
/// useful.
|
||||
pub fn set_environment<V: ToLua<'lua>>(mut self, env: V) -> Result<Self> {
|
||||
// Prefer to propagate errors here and wrap to `Ok`
|
||||
self.env = Ok(Some(env.to_lua(self.lua)?));
|
||||
Ok(self)
|
||||
pub fn set_environment<V: IntoLua<'lua>>(mut self, env: V) -> Self {
|
||||
self.env = env.into_lua(self.lua);
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets whether the chunk is text or binary (autodetected by default).
|
||||
@@ -299,10 +308,7 @@ impl<'lua, 'a> Chunk<'lua, 'a> {
|
||||
/// [`exec`]: #method.exec
|
||||
#[cfg(feature = "async")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
|
||||
pub fn exec_async<'fut>(self) -> LocalBoxFuture<'fut, Result<()>>
|
||||
where
|
||||
'lua: 'fut,
|
||||
{
|
||||
pub fn exec_async(self) -> LocalBoxFuture<'lua, Result<()>> {
|
||||
self.call_async(())
|
||||
}
|
||||
|
||||
@@ -351,7 +357,7 @@ impl<'lua, 'a> Chunk<'lua, 'a> {
|
||||
/// Load the chunk function and call it with the given arguments.
|
||||
///
|
||||
/// This is equivalent to `into_function` and calling the resulting function.
|
||||
pub fn call<A: ToLuaMulti<'lua>, R: FromLuaMulti<'lua>>(self, args: A) -> Result<R> {
|
||||
pub fn call<A: IntoLuaMulti<'lua>, R: FromLuaMulti<'lua>>(self, args: A) -> Result<R> {
|
||||
self.into_function()?.call(args)
|
||||
}
|
||||
|
||||
@@ -367,7 +373,7 @@ impl<'lua, 'a> Chunk<'lua, 'a> {
|
||||
pub fn call_async<'fut, A, R>(self, args: A) -> LocalBoxFuture<'fut, Result<R>>
|
||||
where
|
||||
'lua: 'fut,
|
||||
A: ToLuaMulti<'lua>,
|
||||
A: IntoLuaMulti<'lua>,
|
||||
R: FromLuaMulti<'lua> + 'fut,
|
||||
{
|
||||
match self.into_function() {
|
||||
@@ -387,9 +393,9 @@ impl<'lua, 'a> Chunk<'lua, 'a> {
|
||||
self.compile();
|
||||
}
|
||||
|
||||
let name = self.convert_name()?;
|
||||
let name = Self::convert_name(self.name)?;
|
||||
self.lua
|
||||
.load_chunk(self.source?.as_ref(), name.as_deref(), self.env?, self.mode)
|
||||
.load_chunk(Some(&name), self.env?, self.mode, self.source?.as_ref())
|
||||
}
|
||||
|
||||
/// Compiles the chunk and changes mode to binary.
|
||||
@@ -408,7 +414,7 @@ impl<'lua, 'a> Chunk<'lua, 'a> {
|
||||
self.mode = Some(ChunkMode::Binary);
|
||||
}
|
||||
#[cfg(not(feature = "luau"))]
|
||||
if let Ok(func) = self.lua.load_chunk(source.as_ref(), None, None, None) {
|
||||
if let Ok(func) = self.lua.load_chunk(None, Value::Nil, None, source.as_ref()) {
|
||||
let data = func.dump(false);
|
||||
self.source = Ok(Cow::Owned(data));
|
||||
self.mode = Some(ChunkMode::Binary);
|
||||
@@ -470,9 +476,9 @@ impl<'lua, 'a> Chunk<'lua, 'a> {
|
||||
.map(|c| c.compile(&source))
|
||||
.unwrap_or(source);
|
||||
|
||||
let name = self.convert_name()?;
|
||||
let name = Self::convert_name(self.name.clone())?;
|
||||
self.lua
|
||||
.load_chunk(&source, name.as_deref(), self.env.clone()?, None)
|
||||
.load_chunk(Some(&name), self.env.clone()?, None, &source)
|
||||
}
|
||||
|
||||
fn detect_mode(&self) -> ChunkMode {
|
||||
@@ -493,12 +499,8 @@ impl<'lua, 'a> Chunk<'lua, 'a> {
|
||||
}
|
||||
}
|
||||
|
||||
fn convert_name(&self) -> Result<Option<CString>> {
|
||||
self.name
|
||||
.clone()
|
||||
.map(CString::new)
|
||||
.transpose()
|
||||
.map_err(|err| Error::RuntimeError(format!("invalid name: {err}")))
|
||||
fn convert_name(name: String) -> Result<CString> {
|
||||
CString::new(name).map_err(|err| Error::RuntimeError(format!("invalid name: {err}")))
|
||||
}
|
||||
|
||||
fn expression_source(source: &[u8]) -> Vec<u8> {
|
||||
|
||||
+151
-78
@@ -1,5 +1,3 @@
|
||||
#![allow(clippy::wrong_self_convention)]
|
||||
|
||||
use std::borrow::Cow;
|
||||
use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet};
|
||||
use std::convert::TryInto;
|
||||
@@ -11,18 +9,24 @@ use bstr::{BStr, BString};
|
||||
use num_traits::cast;
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
use crate::function::Function;
|
||||
use crate::function::{Function, WrappedFunction};
|
||||
use crate::lua::Lua;
|
||||
use crate::string::String;
|
||||
use crate::table::Table;
|
||||
use crate::thread::Thread;
|
||||
use crate::types::{LightUserData, MaybeSend};
|
||||
use crate::userdata::{AnyUserData, UserData};
|
||||
use crate::value::{FromLua, Nil, ToLua, Value};
|
||||
use crate::userdata::{AnyUserData, UserData, UserDataRef, UserDataRefMut};
|
||||
use crate::value::{FromLua, IntoLua, Nil, Value};
|
||||
|
||||
impl<'lua> ToLua<'lua> for Value<'lua> {
|
||||
#[cfg(all(feature = "unstable", any(not(feature = "send"), doc)))]
|
||||
use crate::{function::OwnedFunction, table::OwnedTable, userdata::OwnedAnyUserData};
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
use crate::function::WrappedAsyncFunction;
|
||||
|
||||
impl<'lua> IntoLua<'lua> for Value<'lua> {
|
||||
#[inline]
|
||||
fn to_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
|
||||
fn into_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
|
||||
Ok(self)
|
||||
}
|
||||
}
|
||||
@@ -34,9 +38,9 @@ impl<'lua> FromLua<'lua> for Value<'lua> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> ToLua<'lua> for String<'lua> {
|
||||
impl<'lua> IntoLua<'lua> for String<'lua> {
|
||||
#[inline]
|
||||
fn to_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
|
||||
fn into_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
|
||||
Ok(Value::String(self))
|
||||
}
|
||||
}
|
||||
@@ -54,9 +58,9 @@ impl<'lua> FromLua<'lua> for String<'lua> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> ToLua<'lua> for Table<'lua> {
|
||||
impl<'lua> IntoLua<'lua> for Table<'lua> {
|
||||
#[inline]
|
||||
fn to_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
|
||||
fn into_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
|
||||
Ok(Value::Table(self))
|
||||
}
|
||||
}
|
||||
@@ -75,9 +79,27 @@ impl<'lua> FromLua<'lua> for Table<'lua> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> ToLua<'lua> for Function<'lua> {
|
||||
#[cfg(all(feature = "unstable", any(not(feature = "send"), doc)))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all(feature = "unstable", not(feature = "send")))))]
|
||||
impl<'lua> IntoLua<'lua> for OwnedTable {
|
||||
#[inline]
|
||||
fn to_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
|
||||
fn into_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
|
||||
Ok(Value::Table(Table(lua.adopt_owned_ref(self.0))))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "unstable", any(not(feature = "send"), doc)))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all(feature = "unstable", not(feature = "send")))))]
|
||||
impl<'lua> FromLua<'lua> for OwnedTable {
|
||||
#[inline]
|
||||
fn from_lua(value: Value<'lua>, lua: &'lua Lua) -> Result<OwnedTable> {
|
||||
Table::from_lua(value, lua).map(|s| s.into_owned())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> IntoLua<'lua> for Function<'lua> {
|
||||
#[inline]
|
||||
fn into_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
|
||||
Ok(Value::Function(self))
|
||||
}
|
||||
}
|
||||
@@ -96,9 +118,42 @@ impl<'lua> FromLua<'lua> for Function<'lua> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> ToLua<'lua> for Thread<'lua> {
|
||||
#[cfg(all(feature = "unstable", any(not(feature = "send"), doc)))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all(feature = "unstable", not(feature = "send")))))]
|
||||
impl<'lua> IntoLua<'lua> for OwnedFunction {
|
||||
#[inline]
|
||||
fn to_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
|
||||
fn into_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
|
||||
Ok(Value::Function(Function(lua.adopt_owned_ref(self.0))))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "unstable", any(not(feature = "send"), doc)))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all(feature = "unstable", not(feature = "send")))))]
|
||||
impl<'lua> FromLua<'lua> for OwnedFunction {
|
||||
#[inline]
|
||||
fn from_lua(value: Value<'lua>, lua: &'lua Lua) -> Result<OwnedFunction> {
|
||||
Function::from_lua(value, lua).map(|s| s.into_owned())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> IntoLua<'lua> for WrappedFunction<'lua> {
|
||||
#[inline]
|
||||
fn into_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
|
||||
lua.create_callback(self.0).map(Value::Function)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
impl<'lua> IntoLua<'lua> for WrappedAsyncFunction<'lua> {
|
||||
#[inline]
|
||||
fn into_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
|
||||
lua.create_async_callback(self.0).map(Value::Function)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> IntoLua<'lua> for Thread<'lua> {
|
||||
#[inline]
|
||||
fn into_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
|
||||
Ok(Value::Thread(self))
|
||||
}
|
||||
}
|
||||
@@ -117,9 +172,9 @@ impl<'lua> FromLua<'lua> for Thread<'lua> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> ToLua<'lua> for AnyUserData<'lua> {
|
||||
impl<'lua> IntoLua<'lua> for AnyUserData<'lua> {
|
||||
#[inline]
|
||||
fn to_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
|
||||
fn into_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
|
||||
Ok(Value::UserData(self))
|
||||
}
|
||||
}
|
||||
@@ -138,30 +193,48 @@ impl<'lua> FromLua<'lua> for AnyUserData<'lua> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua, T: 'static + MaybeSend + UserData> ToLua<'lua> for T {
|
||||
#[cfg(all(feature = "unstable", any(not(feature = "send"), doc)))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all(feature = "unstable", not(feature = "send")))))]
|
||||
impl<'lua> IntoLua<'lua> for OwnedAnyUserData {
|
||||
#[inline]
|
||||
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
|
||||
fn into_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
|
||||
Ok(Value::UserData(AnyUserData(lua.adopt_owned_ref(self.0))))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "unstable", any(not(feature = "send"), doc)))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all(feature = "unstable", not(feature = "send")))))]
|
||||
impl<'lua> FromLua<'lua> for OwnedAnyUserData {
|
||||
#[inline]
|
||||
fn from_lua(value: Value<'lua>, lua: &'lua Lua) -> Result<OwnedAnyUserData> {
|
||||
AnyUserData::from_lua(value, lua).map(|s| s.into_owned())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua, T: 'static + MaybeSend + UserData> IntoLua<'lua> for T {
|
||||
#[inline]
|
||||
fn into_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
|
||||
Ok(Value::UserData(lua.create_userdata(self)?))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua, T: 'static + UserData + Clone> FromLua<'lua> for T {
|
||||
impl<'lua, T: 'static> FromLua<'lua> for UserDataRef<'lua, T> {
|
||||
#[inline]
|
||||
fn from_lua(value: Value<'lua>, _: &'lua Lua) -> Result<T> {
|
||||
match value {
|
||||
Value::UserData(ud) => Ok(ud.borrow::<T>()?.clone()),
|
||||
_ => Err(Error::FromLuaConversionError {
|
||||
from: value.type_name(),
|
||||
to: "userdata",
|
||||
message: None,
|
||||
}),
|
||||
}
|
||||
fn from_lua(value: Value<'lua>, _: &'lua Lua) -> Result<Self> {
|
||||
Self::from_value(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> ToLua<'lua> for Error {
|
||||
impl<'lua, T: 'static> FromLua<'lua> for UserDataRefMut<'lua, T> {
|
||||
#[inline]
|
||||
fn to_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
|
||||
fn from_lua(value: Value<'lua>, _: &'lua Lua) -> Result<Self> {
|
||||
Self::from_value(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> IntoLua<'lua> for Error {
|
||||
#[inline]
|
||||
fn into_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
|
||||
Ok(Value::Error(self))
|
||||
}
|
||||
}
|
||||
@@ -180,9 +253,9 @@ impl<'lua> FromLua<'lua> for Error {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> ToLua<'lua> for bool {
|
||||
impl<'lua> IntoLua<'lua> for bool {
|
||||
#[inline]
|
||||
fn to_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
|
||||
fn into_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
|
||||
Ok(Value::Boolean(self))
|
||||
}
|
||||
}
|
||||
@@ -198,9 +271,9 @@ impl<'lua> FromLua<'lua> for bool {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> ToLua<'lua> for LightUserData {
|
||||
impl<'lua> IntoLua<'lua> for LightUserData {
|
||||
#[inline]
|
||||
fn to_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
|
||||
fn into_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
|
||||
Ok(Value::LightUserData(self))
|
||||
}
|
||||
}
|
||||
@@ -219,9 +292,9 @@ impl<'lua> FromLua<'lua> for LightUserData {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> ToLua<'lua> for StdString {
|
||||
impl<'lua> IntoLua<'lua> for StdString {
|
||||
#[inline]
|
||||
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
|
||||
fn into_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
|
||||
Ok(Value::String(lua.create_string(&self)?))
|
||||
}
|
||||
}
|
||||
@@ -242,23 +315,23 @@ impl<'lua> FromLua<'lua> for StdString {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> ToLua<'lua> for &str {
|
||||
impl<'lua> IntoLua<'lua> for &str {
|
||||
#[inline]
|
||||
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
|
||||
fn into_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
|
||||
Ok(Value::String(lua.create_string(self)?))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> ToLua<'lua> for Cow<'_, str> {
|
||||
impl<'lua> IntoLua<'lua> for Cow<'_, str> {
|
||||
#[inline]
|
||||
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
|
||||
fn into_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
|
||||
Ok(Value::String(lua.create_string(self.as_bytes())?))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> ToLua<'lua> for Box<str> {
|
||||
impl<'lua> IntoLua<'lua> for Box<str> {
|
||||
#[inline]
|
||||
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
|
||||
fn into_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
|
||||
Ok(Value::String(lua.create_string(&*self)?))
|
||||
}
|
||||
}
|
||||
@@ -280,9 +353,9 @@ impl<'lua> FromLua<'lua> for Box<str> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> ToLua<'lua> for CString {
|
||||
impl<'lua> IntoLua<'lua> for CString {
|
||||
#[inline]
|
||||
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
|
||||
fn into_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
|
||||
Ok(Value::String(lua.create_string(self.as_bytes())?))
|
||||
}
|
||||
}
|
||||
@@ -310,23 +383,23 @@ impl<'lua> FromLua<'lua> for CString {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> ToLua<'lua> for &CStr {
|
||||
impl<'lua> IntoLua<'lua> for &CStr {
|
||||
#[inline]
|
||||
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
|
||||
fn into_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
|
||||
Ok(Value::String(lua.create_string(self.to_bytes())?))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> ToLua<'lua> for Cow<'_, CStr> {
|
||||
impl<'lua> IntoLua<'lua> for Cow<'_, CStr> {
|
||||
#[inline]
|
||||
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
|
||||
fn into_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
|
||||
Ok(Value::String(lua.create_string(self.to_bytes())?))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> ToLua<'lua> for BString {
|
||||
impl<'lua> IntoLua<'lua> for BString {
|
||||
#[inline]
|
||||
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
|
||||
fn into_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
|
||||
Ok(Value::String(lua.create_string(&self)?))
|
||||
}
|
||||
}
|
||||
@@ -348,18 +421,18 @@ impl<'lua> FromLua<'lua> for BString {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> ToLua<'lua> for &BStr {
|
||||
impl<'lua> IntoLua<'lua> for &BStr {
|
||||
#[inline]
|
||||
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
|
||||
fn into_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
|
||||
Ok(Value::String(lua.create_string(self)?))
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! lua_convert_int {
|
||||
($x:ty) => {
|
||||
impl<'lua> ToLua<'lua> for $x {
|
||||
impl<'lua> IntoLua<'lua> for $x {
|
||||
#[inline]
|
||||
fn to_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
|
||||
fn into_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
|
||||
cast(self)
|
||||
.map(Value::Integer)
|
||||
.or_else(|| cast(self).map(Value::Number))
|
||||
@@ -420,9 +493,9 @@ lua_convert_int!(usize);
|
||||
|
||||
macro_rules! lua_convert_float {
|
||||
($x:ty) => {
|
||||
impl<'lua> ToLua<'lua> for $x {
|
||||
impl<'lua> IntoLua<'lua> for $x {
|
||||
#[inline]
|
||||
fn to_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
|
||||
fn into_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
|
||||
cast(self)
|
||||
.ok_or_else(|| Error::ToLuaConversionError {
|
||||
from: stringify!($x),
|
||||
@@ -458,24 +531,24 @@ macro_rules! lua_convert_float {
|
||||
lua_convert_float!(f32);
|
||||
lua_convert_float!(f64);
|
||||
|
||||
impl<'lua, T> ToLua<'lua> for &[T]
|
||||
impl<'lua, T> IntoLua<'lua> for &[T]
|
||||
where
|
||||
T: Clone + ToLua<'lua>,
|
||||
T: Clone + IntoLua<'lua>,
|
||||
{
|
||||
#[inline]
|
||||
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
|
||||
fn into_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
|
||||
Ok(Value::Table(
|
||||
lua.create_sequence_from(self.iter().cloned())?,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua, T, const N: usize> ToLua<'lua> for [T; N]
|
||||
impl<'lua, T, const N: usize> IntoLua<'lua> for [T; N]
|
||||
where
|
||||
T: ToLua<'lua>,
|
||||
T: IntoLua<'lua>,
|
||||
{
|
||||
#[inline]
|
||||
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
|
||||
fn into_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
|
||||
Ok(Value::Table(lua.create_sequence_from(self)?))
|
||||
}
|
||||
}
|
||||
@@ -516,9 +589,9 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua, T: ToLua<'lua>> ToLua<'lua> for Box<[T]> {
|
||||
impl<'lua, T: IntoLua<'lua>> IntoLua<'lua> for Box<[T]> {
|
||||
#[inline]
|
||||
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
|
||||
fn into_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
|
||||
Ok(Value::Table(lua.create_sequence_from(self.into_vec())?))
|
||||
}
|
||||
}
|
||||
@@ -530,9 +603,9 @@ impl<'lua, T: FromLua<'lua>> FromLua<'lua> for Box<[T]> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua, T: ToLua<'lua>> ToLua<'lua> for Vec<T> {
|
||||
impl<'lua, T: IntoLua<'lua>> IntoLua<'lua> for Vec<T> {
|
||||
#[inline]
|
||||
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
|
||||
fn into_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
|
||||
Ok(Value::Table(lua.create_sequence_from(self)?))
|
||||
}
|
||||
}
|
||||
@@ -557,11 +630,11 @@ impl<'lua, T: FromLua<'lua>> FromLua<'lua> for Vec<T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua, K: Eq + Hash + ToLua<'lua>, V: ToLua<'lua>, S: BuildHasher> ToLua<'lua>
|
||||
impl<'lua, K: Eq + Hash + IntoLua<'lua>, V: IntoLua<'lua>, S: BuildHasher> IntoLua<'lua>
|
||||
for HashMap<K, V, S>
|
||||
{
|
||||
#[inline]
|
||||
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
|
||||
fn into_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
|
||||
Ok(Value::Table(lua.create_table_from(self)?))
|
||||
}
|
||||
}
|
||||
@@ -583,9 +656,9 @@ impl<'lua, K: Eq + Hash + FromLua<'lua>, V: FromLua<'lua>, S: BuildHasher + Defa
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua, K: Ord + ToLua<'lua>, V: ToLua<'lua>> ToLua<'lua> for BTreeMap<K, V> {
|
||||
impl<'lua, K: Ord + IntoLua<'lua>, V: IntoLua<'lua>> IntoLua<'lua> for BTreeMap<K, V> {
|
||||
#[inline]
|
||||
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
|
||||
fn into_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
|
||||
Ok(Value::Table(lua.create_table_from(self)?))
|
||||
}
|
||||
}
|
||||
@@ -605,9 +678,9 @@ impl<'lua, K: Ord + FromLua<'lua>, V: FromLua<'lua>> FromLua<'lua> for BTreeMap<
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua, T: Eq + Hash + ToLua<'lua>, S: BuildHasher> ToLua<'lua> for HashSet<T, S> {
|
||||
impl<'lua, T: Eq + Hash + IntoLua<'lua>, S: BuildHasher> IntoLua<'lua> for HashSet<T, S> {
|
||||
#[inline]
|
||||
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
|
||||
fn into_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
|
||||
Ok(Value::Table(lua.create_table_from(
|
||||
self.into_iter().map(|val| (val, true)),
|
||||
)?))
|
||||
@@ -632,9 +705,9 @@ impl<'lua, T: Eq + Hash + FromLua<'lua>, S: BuildHasher + Default> FromLua<'lua>
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua, T: Ord + ToLua<'lua>> ToLua<'lua> for BTreeSet<T> {
|
||||
impl<'lua, T: Ord + IntoLua<'lua>> IntoLua<'lua> for BTreeSet<T> {
|
||||
#[inline]
|
||||
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
|
||||
fn into_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
|
||||
Ok(Value::Table(lua.create_table_from(
|
||||
self.into_iter().map(|val| (val, true)),
|
||||
)?))
|
||||
@@ -659,11 +732,11 @@ impl<'lua, T: Ord + FromLua<'lua>> FromLua<'lua> for BTreeSet<T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua, T: ToLua<'lua>> ToLua<'lua> for Option<T> {
|
||||
impl<'lua, T: IntoLua<'lua>> IntoLua<'lua> for Option<T> {
|
||||
#[inline]
|
||||
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
|
||||
fn into_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
|
||||
match self {
|
||||
Some(val) => val.to_lua(lua),
|
||||
Some(val) => val.into_lua(lua),
|
||||
None => Ok(Nil),
|
||||
}
|
||||
}
|
||||
|
||||
+142
-28
@@ -1,5 +1,3 @@
|
||||
#![allow(clippy::wrong_self_convention)]
|
||||
|
||||
use std::error::Error as StdError;
|
||||
use std::fmt;
|
||||
use std::io::Error as IoError;
|
||||
@@ -9,6 +7,8 @@ use std::str::Utf8Error;
|
||||
use std::string::String as StdString;
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::private::Sealed;
|
||||
|
||||
/// Error type returned by `mlua` methods.
|
||||
#[derive(Debug, Clone)]
|
||||
#[non_exhaustive]
|
||||
@@ -71,6 +71,20 @@ pub enum Error {
|
||||
StackError,
|
||||
/// Too many arguments to `Function::bind`
|
||||
BindError,
|
||||
/// Bad argument received from Lua (usually when calling a function).
|
||||
///
|
||||
/// This error can help to identify the argument that caused the error
|
||||
/// (which is stored in the corresponding field).
|
||||
BadArgument {
|
||||
/// Function that was called.
|
||||
to: Option<StdString>,
|
||||
/// Argument position (usually starts from 1).
|
||||
pos: usize,
|
||||
/// Argument name.
|
||||
name: Option<StdString>,
|
||||
/// Underlying error returned when converting argument to a Lua value.
|
||||
cause: Arc<Error>,
|
||||
},
|
||||
/// A Rust value could not be converted to a Lua value.
|
||||
ToLuaConversionError {
|
||||
/// Name of the Rust type that could not be converted.
|
||||
@@ -142,8 +156,11 @@ pub enum Error {
|
||||
///
|
||||
/// [`MetaMethod`]: crate::MetaMethod
|
||||
MetaMethodTypeError {
|
||||
/// Name of the metamethod.
|
||||
method: StdString,
|
||||
/// Passed value type.
|
||||
type_name: &'static str,
|
||||
/// A string containing more detailed error information.
|
||||
message: Option<StdString>,
|
||||
},
|
||||
/// A [`RegistryKey`] produced from a different Lua state was used.
|
||||
@@ -178,6 +195,13 @@ pub enum Error {
|
||||
/// error. The Rust code that originally invoked the Lua code then receives a `CallbackError`,
|
||||
/// from which the original error (and a stack traceback) can be recovered.
|
||||
ExternalError(Arc<dyn StdError + Send + Sync>),
|
||||
/// An error with additional context.
|
||||
WithContext {
|
||||
/// A string containing additional context.
|
||||
context: StdString,
|
||||
/// Underlying error.
|
||||
cause: Arc<Error>,
|
||||
},
|
||||
}
|
||||
|
||||
/// A specialized `Result` type used by `mlua`'s API.
|
||||
@@ -187,17 +211,17 @@ pub type Result<T> = StdResult<T, Error>;
|
||||
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::SyntaxError { ref message, .. } => write!(fmt, "syntax error: {message}"),
|
||||
Error::RuntimeError(ref msg) => write!(fmt, "runtime error: {msg}"),
|
||||
Error::MemoryError(ref msg) => {
|
||||
write!(fmt, "memory error: {}", msg)
|
||||
write!(fmt, "memory error: {msg}")
|
||||
}
|
||||
#[cfg(any(feature = "lua53", feature = "lua52"))]
|
||||
Error::GarbageCollectorError(ref msg) => {
|
||||
write!(fmt, "garbage collector error: {}", msg)
|
||||
write!(fmt, "garbage collector error: {msg}")
|
||||
}
|
||||
Error::SafetyError(ref msg) => {
|
||||
write!(fmt, "safety error: {}", msg)
|
||||
write!(fmt, "safety error: {msg}")
|
||||
},
|
||||
Error::MemoryLimitNotAvailable => {
|
||||
write!(fmt, "setting memory limit is not available")
|
||||
@@ -218,18 +242,29 @@ impl fmt::Display for Error {
|
||||
fmt,
|
||||
"too many arguments to Function::bind"
|
||||
),
|
||||
Error::BadArgument { ref to, pos, ref name, ref cause } => {
|
||||
if let Some(name) = name {
|
||||
write!(fmt, "bad argument `{name}`")?;
|
||||
} else {
|
||||
write!(fmt, "bad argument #{pos}")?;
|
||||
}
|
||||
if let Some(to) = to {
|
||||
write!(fmt, " to `{to}`")?;
|
||||
}
|
||||
write!(fmt, ": {cause}")
|
||||
},
|
||||
Error::ToLuaConversionError { from, to, ref message } => {
|
||||
write!(fmt, "error converting {} to Lua {}", from, to)?;
|
||||
write!(fmt, "error converting {from} to Lua {to}")?;
|
||||
match *message {
|
||||
None => Ok(()),
|
||||
Some(ref message) => write!(fmt, " ({})", message),
|
||||
Some(ref message) => write!(fmt, " ({message})"),
|
||||
}
|
||||
}
|
||||
Error::FromLuaConversionError { from, to, ref message } => {
|
||||
write!(fmt, "error converting Lua {} to {}", from, to)?;
|
||||
write!(fmt, "error converting Lua {from} to {to}")?;
|
||||
match *message {
|
||||
None => Ok(()),
|
||||
Some(ref message) => write!(fmt, " ({})", message),
|
||||
Some(ref message) => write!(fmt, " ({message})"),
|
||||
}
|
||||
}
|
||||
Error::CoroutineInactive => write!(fmt, "cannot resume inactive coroutine"),
|
||||
@@ -237,25 +272,25 @@ impl fmt::Display for Error {
|
||||
Error::UserDataDestructed => write!(fmt, "userdata has been destructed"),
|
||||
Error::UserDataBorrowError => write!(fmt, "userdata already mutably borrowed"),
|
||||
Error::UserDataBorrowMutError => write!(fmt, "userdata already borrowed"),
|
||||
Error::MetaMethodRestricted(ref method) => write!(fmt, "metamethod {} is restricted", method),
|
||||
Error::MetaMethodRestricted(ref method) => write!(fmt, "metamethod {method} is restricted"),
|
||||
Error::MetaMethodTypeError { ref method, type_name, ref message } => {
|
||||
write!(fmt, "metamethod {} has unsupported type {}", method, type_name)?;
|
||||
write!(fmt, "metamethod {method} has unsupported type {type_name}")?;
|
||||
match *message {
|
||||
None => Ok(()),
|
||||
Some(ref message) => write!(fmt, " ({})", message),
|
||||
Some(ref message) => write!(fmt, " ({message})"),
|
||||
}
|
||||
}
|
||||
Error::MismatchedRegistryKey => {
|
||||
write!(fmt, "RegistryKey used from different Lua state")
|
||||
}
|
||||
Error::CallbackError { ref cause, ref traceback } => {
|
||||
writeln!(fmt, "callback error")?;
|
||||
// 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 {
|
||||
cause = cause2;
|
||||
full_traceback = Some(traceback2);
|
||||
}
|
||||
writeln!(fmt, "{cause}")?;
|
||||
if let Some(full_traceback) = full_traceback {
|
||||
let traceback = traceback.trim_start_matches("stack traceback:");
|
||||
let traceback = traceback.trim_start().trim_end();
|
||||
@@ -269,20 +304,24 @@ impl fmt::Display for Error {
|
||||
} else {
|
||||
writeln!(fmt, "{}", traceback.trim_end())?;
|
||||
}
|
||||
write!(fmt, "caused by: {}", cause)
|
||||
Ok(())
|
||||
}
|
||||
Error::PreviouslyResumedPanic => {
|
||||
write!(fmt, "previously resumed panic returned again")
|
||||
}
|
||||
#[cfg(feature = "serialize")]
|
||||
Error::SerializeError(ref err) => {
|
||||
write!(fmt, "serialize error: {}", err)
|
||||
write!(fmt, "serialize error: {err}")
|
||||
},
|
||||
#[cfg(feature = "serialize")]
|
||||
Error::DeserializeError(ref err) => {
|
||||
write!(fmt, "deserialize error: {}", err)
|
||||
write!(fmt, "deserialize error: {err}")
|
||||
},
|
||||
Error::ExternalError(ref err) => write!(fmt, "{}", err),
|
||||
Error::ExternalError(ref err) => write!(fmt, "{err}"),
|
||||
Error::WithContext { ref context, ref cause } => {
|
||||
writeln!(fmt, "{context}")?;
|
||||
write!(fmt, "{cause}")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -302,47 +341,122 @@ impl StdError for Error {
|
||||
}
|
||||
|
||||
impl Error {
|
||||
pub fn external<T: Into<Box<dyn StdError + Send + Sync>>>(err: T) -> Error {
|
||||
/// Wraps an external error object.
|
||||
pub fn external<T: Into<Box<dyn StdError + Send + Sync>>>(err: T) -> Self {
|
||||
Error::ExternalError(err.into().into())
|
||||
}
|
||||
|
||||
/// Attempts to downcast the external error object to a concrete type by reference.
|
||||
pub fn downcast_ref<T>(&self) -> Option<&T>
|
||||
where
|
||||
T: StdError + 'static,
|
||||
{
|
||||
match self {
|
||||
Error::ExternalError(err) => err.downcast_ref(),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn bad_self_argument(to: &str, cause: Error) -> Self {
|
||||
Error::BadArgument {
|
||||
to: Some(to.to_string()),
|
||||
pos: 1,
|
||||
name: Some("self".to_string()),
|
||||
cause: Arc::new(cause),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn from_lua_conversion<'a>(
|
||||
from: &'static str,
|
||||
to: &'static str,
|
||||
message: impl Into<Option<&'a str>>,
|
||||
) -> Self {
|
||||
Error::FromLuaConversionError {
|
||||
from,
|
||||
to,
|
||||
message: message.into().map(|s| s.into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait ExternalError {
|
||||
fn to_lua_err(self) -> Error;
|
||||
fn into_lua_err(self) -> Error;
|
||||
}
|
||||
|
||||
impl<E: Into<Box<dyn StdError + Send + Sync>>> ExternalError for E {
|
||||
fn to_lua_err(self) -> Error {
|
||||
fn into_lua_err(self) -> Error {
|
||||
Error::external(self)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait ExternalResult<T> {
|
||||
fn to_lua_err(self) -> Result<T>;
|
||||
fn into_lua_err(self) -> Result<T>;
|
||||
}
|
||||
|
||||
impl<T, E> ExternalResult<T> for StdResult<T, E>
|
||||
where
|
||||
E: ExternalError,
|
||||
{
|
||||
fn to_lua_err(self) -> Result<T> {
|
||||
self.map_err(|e| e.to_lua_err())
|
||||
fn into_lua_err(self) -> Result<T> {
|
||||
self.map_err(|e| e.into_lua_err())
|
||||
}
|
||||
}
|
||||
|
||||
impl std::convert::From<AddrParseError> for Error {
|
||||
/// Provides the `context` method for [`Error`] and `Result<T, Error>`.
|
||||
pub trait ErrorContext: Sealed {
|
||||
/// Wraps the error value with additional context.
|
||||
fn context<C: fmt::Display>(self, context: C) -> Self;
|
||||
|
||||
/// Wrap the error value with additional context that is evaluated lazily
|
||||
/// only once an error does occur.
|
||||
fn with_context<C: fmt::Display>(self, f: impl FnOnce(&Error) -> C) -> Self;
|
||||
}
|
||||
|
||||
impl ErrorContext for Error {
|
||||
fn context<C: fmt::Display>(self, context: C) -> Self {
|
||||
Error::WithContext {
|
||||
context: context.to_string(),
|
||||
cause: Arc::new(self),
|
||||
}
|
||||
}
|
||||
|
||||
fn with_context<C: fmt::Display>(self, f: impl FnOnce(&Error) -> C) -> Self {
|
||||
Error::WithContext {
|
||||
context: f(&self).to_string(),
|
||||
cause: Arc::new(self),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> ErrorContext for StdResult<T, Error> {
|
||||
fn context<C: fmt::Display>(self, context: C) -> Self {
|
||||
self.map_err(|err| Error::WithContext {
|
||||
context: context.to_string(),
|
||||
cause: Arc::new(err),
|
||||
})
|
||||
}
|
||||
|
||||
fn with_context<C: fmt::Display>(self, f: impl FnOnce(&Error) -> C) -> Self {
|
||||
self.map_err(|err| Error::WithContext {
|
||||
context: f(&err).to_string(),
|
||||
cause: Arc::new(err),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl From<AddrParseError> for Error {
|
||||
fn from(err: AddrParseError) -> Self {
|
||||
Error::external(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::convert::From<IoError> for Error {
|
||||
impl From<IoError> for Error {
|
||||
fn from(err: IoError) -> Self {
|
||||
Error::external(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::convert::From<Utf8Error> for Error {
|
||||
impl From<Utf8Error> for Error {
|
||||
fn from(err: Utf8Error) -> Self {
|
||||
Error::external(err)
|
||||
}
|
||||
|
||||
+172
-35
@@ -1,23 +1,50 @@
|
||||
use std::cell::RefCell;
|
||||
use std::mem;
|
||||
use std::os::raw::{c_int, c_void};
|
||||
use std::ptr;
|
||||
use std::slice;
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
use crate::ffi;
|
||||
use crate::types::LuaRef;
|
||||
use crate::lua::Lua;
|
||||
use crate::memory::MemoryState;
|
||||
use crate::types::{Callback, LuaRef, MaybeSend};
|
||||
use crate::util::{
|
||||
assert_stack, check_stack, error_traceback, pop_error, ptr_to_cstr_bytes, StackGuard,
|
||||
};
|
||||
use crate::value::{FromLuaMulti, ToLuaMulti};
|
||||
use crate::value::{FromLuaMulti, IntoLua, IntoLuaMulti};
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
use {futures_core::future::LocalBoxFuture, futures_util::future};
|
||||
use {
|
||||
crate::types::AsyncCallback,
|
||||
futures_core::future::{Future, LocalBoxFuture},
|
||||
futures_util::{future, TryFutureExt},
|
||||
};
|
||||
|
||||
/// Handle to an internal Lua function.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Function<'lua>(pub(crate) LuaRef<'lua>);
|
||||
|
||||
/// Owned handle to an internal Lua function.
|
||||
///
|
||||
/// The owned handle holds a *strong* reference to the current Lua instance.
|
||||
/// Be warned, if you place it into a Lua type (eg. [`UserData`] or a Rust callback), it is *very easy*
|
||||
/// to accidentally cause reference cycles that would prevent destroying Lua instance.
|
||||
///
|
||||
/// [`UserData`]: crate::UserData
|
||||
#[cfg(feature = "unstable")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "unstable")))]
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct OwnedFunction(pub(crate) crate::types::LuaOwnedRef);
|
||||
|
||||
#[cfg(feature = "unstable")]
|
||||
impl OwnedFunction {
|
||||
/// Get borrowed handle to the underlying Lua function.
|
||||
#[cfg_attr(feature = "send", allow(unused))]
|
||||
pub const fn to_ref(&self) -> Function {
|
||||
Function(self.0.to_ref())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct FunctionInfo {
|
||||
pub name: Option<Vec<u8>>,
|
||||
@@ -82,33 +109,34 @@ impl<'lua> Function<'lua> {
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn call<A: ToLuaMulti<'lua>, R: FromLuaMulti<'lua>>(&self, args: A) -> Result<R> {
|
||||
pub fn call<A: IntoLuaMulti<'lua>, R: FromLuaMulti<'lua>>(&self, args: A) -> Result<R> {
|
||||
let lua = self.0.lua;
|
||||
let state = lua.state();
|
||||
|
||||
let mut args = args.to_lua_multi(lua)?;
|
||||
let mut args = args.into_lua_multi(lua)?;
|
||||
let nargs = args.len() as c_int;
|
||||
|
||||
let results = unsafe {
|
||||
let _sg = StackGuard::new(lua.state);
|
||||
check_stack(lua.state, nargs + 3)?;
|
||||
let _sg = StackGuard::new(state);
|
||||
check_stack(state, nargs + 3)?;
|
||||
|
||||
ffi::lua_pushcfunction(lua.state, error_traceback);
|
||||
let stack_start = ffi::lua_gettop(lua.state);
|
||||
MemoryState::relax_limit_with(state, || ffi::lua_pushcfunction(state, error_traceback));
|
||||
let stack_start = ffi::lua_gettop(state);
|
||||
lua.push_ref(&self.0);
|
||||
for arg in args.drain_all() {
|
||||
lua.push_value(arg)?;
|
||||
}
|
||||
let ret = ffi::lua_pcall(lua.state, nargs, ffi::LUA_MULTRET, stack_start);
|
||||
let ret = ffi::lua_pcall(state, nargs, ffi::LUA_MULTRET, stack_start);
|
||||
if ret != ffi::LUA_OK {
|
||||
return Err(pop_error(lua.state, ret));
|
||||
return Err(pop_error(state, ret));
|
||||
}
|
||||
let nresults = ffi::lua_gettop(lua.state) - stack_start;
|
||||
let nresults = ffi::lua_gettop(state) - stack_start;
|
||||
let mut results = args; // Reuse MultiValue container
|
||||
assert_stack(lua.state, 2);
|
||||
assert_stack(state, 2);
|
||||
for _ in 0..nresults {
|
||||
results.push_front(lua.pop_value());
|
||||
}
|
||||
ffi::lua_pop(lua.state, 1);
|
||||
ffi::lua_pop(state, 1);
|
||||
results
|
||||
};
|
||||
R::from_lua_multi(results, lua)
|
||||
@@ -148,11 +176,11 @@ impl<'lua> Function<'lua> {
|
||||
pub fn call_async<'fut, A, R>(&self, args: A) -> LocalBoxFuture<'fut, Result<R>>
|
||||
where
|
||||
'lua: 'fut,
|
||||
A: ToLuaMulti<'lua>,
|
||||
A: IntoLuaMulti<'lua>,
|
||||
R: FromLuaMulti<'lua> + 'fut,
|
||||
{
|
||||
let lua = self.0.lua;
|
||||
match lua.create_recycled_thread(self.clone()) {
|
||||
match lua.create_recycled_thread(self) {
|
||||
Ok(t) => {
|
||||
let mut t = t.into_async(args);
|
||||
t.set_recyclable(true);
|
||||
@@ -189,7 +217,7 @@ impl<'lua> Function<'lua> {
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn bind<A: ToLuaMulti<'lua>>(&self, args: A) -> Result<Function<'lua>> {
|
||||
pub fn bind<A: IntoLuaMulti<'lua>>(&self, args: A) -> Result<Function<'lua>> {
|
||||
unsafe extern "C" fn args_wrapper_impl(state: *mut ffi::lua_State) -> c_int {
|
||||
let nargs = ffi::lua_gettop(state);
|
||||
let nbinds = ffi::lua_tointeger(state, ffi::lua_upvalueindex(1)) as c_int;
|
||||
@@ -206,8 +234,9 @@ impl<'lua> Function<'lua> {
|
||||
}
|
||||
|
||||
let lua = self.0.lua;
|
||||
let state = lua.state();
|
||||
|
||||
let args = args.to_lua_multi(lua)?;
|
||||
let args = args.into_lua_multi(lua)?;
|
||||
let nargs = args.len() as c_int;
|
||||
|
||||
if nargs == 0 {
|
||||
@@ -219,14 +248,14 @@ impl<'lua> Function<'lua> {
|
||||
}
|
||||
|
||||
let args_wrapper = unsafe {
|
||||
let _sg = StackGuard::new(lua.state);
|
||||
check_stack(lua.state, nargs + 3)?;
|
||||
let _sg = StackGuard::new(state);
|
||||
check_stack(state, nargs + 3)?;
|
||||
|
||||
ffi::lua_pushinteger(lua.state, nargs as ffi::lua_Integer);
|
||||
ffi::lua_pushinteger(state, nargs as ffi::lua_Integer);
|
||||
for arg in args {
|
||||
lua.push_value(arg)?;
|
||||
}
|
||||
protect_lua!(lua.state, nargs + 1, 1, fn(state) {
|
||||
protect_lua!(state, nargs + 1, 1, fn(state) {
|
||||
ffi::lua_pushcclosure(state, args_wrapper_impl, ffi::lua_gettop(state));
|
||||
})?;
|
||||
|
||||
@@ -242,7 +271,7 @@ impl<'lua> Function<'lua> {
|
||||
"#,
|
||||
)
|
||||
.try_cache()
|
||||
.set_name("_mlua_bind")?
|
||||
.set_name("_mlua_bind")
|
||||
.call((self.clone(), args_wrapper))
|
||||
}
|
||||
|
||||
@@ -253,16 +282,17 @@ impl<'lua> Function<'lua> {
|
||||
/// [`lua_getinfo`]: https://www.lua.org/manual/5.4/manual.html#lua_getinfo
|
||||
pub fn info(&self) -> FunctionInfo {
|
||||
let lua = self.0.lua;
|
||||
let state = lua.state();
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(lua.state);
|
||||
assert_stack(lua.state, 1);
|
||||
let _sg = StackGuard::new(state);
|
||||
assert_stack(state, 1);
|
||||
|
||||
let mut ar: ffi::lua_Debug = mem::zeroed();
|
||||
lua.push_ref(&self.0);
|
||||
#[cfg(not(feature = "luau"))]
|
||||
let res = ffi::lua_getinfo(lua.state, cstr!(">Sn"), &mut ar);
|
||||
let res = ffi::lua_getinfo(state, cstr!(">Sn"), &mut ar);
|
||||
#[cfg(feature = "luau")]
|
||||
let res = ffi::lua_getinfo(lua.state, -1, cstr!("sn"), &mut ar);
|
||||
let res = ffi::lua_getinfo(state, -1, cstr!("sn"), &mut ar);
|
||||
mlua_assert!(res != 0, "lua_getinfo failed with `>Sn`");
|
||||
|
||||
FunctionInfo {
|
||||
@@ -308,15 +338,16 @@ impl<'lua> Function<'lua> {
|
||||
}
|
||||
|
||||
let lua = self.0.lua;
|
||||
let state = lua.state();
|
||||
let mut data: Vec<u8> = Vec::new();
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(lua.state);
|
||||
assert_stack(lua.state, 1);
|
||||
let _sg = StackGuard::new(state);
|
||||
assert_stack(state, 1);
|
||||
|
||||
lua.push_ref(&self.0);
|
||||
let data_ptr = &mut data as *mut Vec<u8> as *mut c_void;
|
||||
ffi::lua_dump(lua.state, writer, data_ptr, strip as i32);
|
||||
ffi::lua_pop(lua.state, 1);
|
||||
ffi::lua_dump(state, writer, data_ptr, strip as i32);
|
||||
ffi::lua_pop(state, 1);
|
||||
}
|
||||
|
||||
data
|
||||
@@ -364,15 +395,24 @@ impl<'lua> Function<'lua> {
|
||||
}
|
||||
|
||||
let lua = self.0.lua;
|
||||
let state = lua.state();
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(lua.state);
|
||||
assert_stack(lua.state, 1);
|
||||
let _sg = StackGuard::new(state);
|
||||
assert_stack(state, 1);
|
||||
|
||||
lua.push_ref(&self.0);
|
||||
let func_ptr = &mut func as *mut F as *mut c_void;
|
||||
ffi::lua_getcoverage(lua.state, -1, func_ptr, callback::<F>);
|
||||
ffi::lua_getcoverage(state, -1, func_ptr, callback::<F>);
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert this handle to owned version.
|
||||
#[cfg(all(feature = "unstable", any(not(feature = "send"), doc)))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all(feature = "unstable", not(feature = "send")))))]
|
||||
#[inline]
|
||||
pub fn into_owned(self) -> OwnedFunction {
|
||||
OwnedFunction(self.0.into_owned())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> PartialEq for Function<'lua> {
|
||||
@@ -380,3 +420,100 @@ impl<'lua> PartialEq for Function<'lua> {
|
||||
self.0 == other.0
|
||||
}
|
||||
}
|
||||
|
||||
// Additional shortcuts
|
||||
#[cfg(feature = "unstable")]
|
||||
impl OwnedFunction {
|
||||
/// Calls the function, passing `args` as function arguments.
|
||||
///
|
||||
/// This is a shortcut for [`Function::call()`].
|
||||
#[inline]
|
||||
pub fn call<'lua, A, R>(&'lua self, args: A) -> Result<R>
|
||||
where
|
||||
A: IntoLuaMulti<'lua>,
|
||||
R: FromLuaMulti<'lua>,
|
||||
{
|
||||
self.to_ref().call(args)
|
||||
}
|
||||
|
||||
/// Returns a future that, when polled, calls `self`, passing `args` as function arguments,
|
||||
/// and drives the execution.
|
||||
///
|
||||
/// This is a shortcut for [`Function::call_async()`].
|
||||
#[cfg(feature = "async")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
|
||||
#[inline]
|
||||
pub fn call_async<'lua, A, R>(&'lua self, args: A) -> LocalBoxFuture<'lua, Result<R>>
|
||||
where
|
||||
A: IntoLuaMulti<'lua>,
|
||||
R: FromLuaMulti<'lua> + 'lua,
|
||||
{
|
||||
self.to_ref().call_async(args)
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct WrappedFunction<'lua>(pub(crate) Callback<'lua, 'static>);
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
pub(crate) struct WrappedAsyncFunction<'lua>(pub(crate) AsyncCallback<'lua, 'static>);
|
||||
|
||||
impl<'lua> Function<'lua> {
|
||||
/// Wraps a Rust function or closure, returning an opaque type that implements [`IntoLua`] trait.
|
||||
#[inline]
|
||||
pub fn wrap<A, R, F>(func: F) -> impl IntoLua<'lua>
|
||||
where
|
||||
A: FromLuaMulti<'lua>,
|
||||
R: IntoLuaMulti<'lua>,
|
||||
F: Fn(&'lua Lua, A) -> Result<R> + MaybeSend + 'static,
|
||||
{
|
||||
WrappedFunction(Box::new(move |lua, args| {
|
||||
func(lua, A::from_lua_multi(args, lua)?)?.into_lua_multi(lua)
|
||||
}))
|
||||
}
|
||||
|
||||
/// Wraps a Rust mutable closure, returning an opaque type that implements [`IntoLua`] trait.
|
||||
#[inline]
|
||||
pub fn wrap_mut<A, R, F>(func: F) -> impl IntoLua<'lua>
|
||||
where
|
||||
A: FromLuaMulti<'lua>,
|
||||
R: IntoLuaMulti<'lua>,
|
||||
F: FnMut(&'lua Lua, A) -> Result<R> + MaybeSend + 'static,
|
||||
{
|
||||
let func = RefCell::new(func);
|
||||
WrappedFunction(Box::new(move |lua, args| {
|
||||
let mut func = func
|
||||
.try_borrow_mut()
|
||||
.map_err(|_| Error::RecursiveMutCallback)?;
|
||||
func(lua, A::from_lua_multi(args, lua)?)?.into_lua_multi(lua)
|
||||
}))
|
||||
}
|
||||
|
||||
/// Wraps a Rust async function or closure, returning an opaque type that implements [`IntoLua`] trait.
|
||||
#[cfg(feature = "async")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
|
||||
pub fn wrap_async<A, R, F, FR>(func: F) -> impl IntoLua<'lua>
|
||||
where
|
||||
A: FromLuaMulti<'lua>,
|
||||
R: IntoLuaMulti<'lua>,
|
||||
F: Fn(&'lua Lua, A) -> FR + MaybeSend + 'static,
|
||||
FR: Future<Output = Result<R>> + 'lua,
|
||||
{
|
||||
WrappedAsyncFunction(Box::new(move |lua, args| {
|
||||
let args = match A::from_lua_multi(args, lua) {
|
||||
Ok(args) => args,
|
||||
Err(e) => return Box::pin(future::err(e)),
|
||||
};
|
||||
Box::pin(func(lua, args).and_then(move |ret| future::ready(ret.into_lua_multi(lua))))
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod assertions {
|
||||
use super::*;
|
||||
|
||||
static_assertions::assert_not_impl_any!(Function: Send);
|
||||
|
||||
#[cfg(all(feature = "unstable", not(feature = "send")))]
|
||||
static_assertions::assert_not_impl_any!(OwnedFunction: Send);
|
||||
}
|
||||
|
||||
+52
-39
@@ -3,7 +3,8 @@ use std::cell::UnsafeCell;
|
||||
use std::ops::{BitOr, BitOrAssign};
|
||||
use std::os::raw::c_int;
|
||||
|
||||
use crate::ffi::{self, lua_Debug};
|
||||
use ffi::lua_Debug;
|
||||
|
||||
use crate::lua::Lua;
|
||||
use crate::util::ptr_to_cstr_bytes;
|
||||
|
||||
@@ -67,12 +68,12 @@ impl<'lua> Debug<'lua> {
|
||||
unsafe {
|
||||
#[cfg(not(feature = "luau"))]
|
||||
mlua_assert!(
|
||||
ffi::lua_getinfo(self.lua.state, cstr!("n"), self.ar.get()) != 0,
|
||||
ffi::lua_getinfo(self.lua.state(), cstr!("n"), self.ar.get()) != 0,
|
||||
"lua_getinfo failed with `n`"
|
||||
);
|
||||
#[cfg(feature = "luau")]
|
||||
mlua_assert!(
|
||||
ffi::lua_getinfo(self.lua.state, self.level, cstr!("n"), self.ar.get()) != 0,
|
||||
ffi::lua_getinfo(self.lua.state(), self.level, cstr!("n"), self.ar.get()) != 0,
|
||||
"lua_getinfo failed with `n`"
|
||||
);
|
||||
|
||||
@@ -91,12 +92,12 @@ impl<'lua> Debug<'lua> {
|
||||
unsafe {
|
||||
#[cfg(not(feature = "luau"))]
|
||||
mlua_assert!(
|
||||
ffi::lua_getinfo(self.lua.state, cstr!("S"), self.ar.get()) != 0,
|
||||
ffi::lua_getinfo(self.lua.state(), cstr!("S"), self.ar.get()) != 0,
|
||||
"lua_getinfo failed with `S`"
|
||||
);
|
||||
#[cfg(feature = "luau")]
|
||||
mlua_assert!(
|
||||
ffi::lua_getinfo(self.lua.state, self.level, cstr!("s"), self.ar.get()) != 0,
|
||||
ffi::lua_getinfo(self.lua.state(), self.level, cstr!("s"), self.ar.get()) != 0,
|
||||
"lua_getinfo failed with `s`"
|
||||
);
|
||||
|
||||
@@ -119,12 +120,12 @@ impl<'lua> Debug<'lua> {
|
||||
unsafe {
|
||||
#[cfg(not(feature = "luau"))]
|
||||
mlua_assert!(
|
||||
ffi::lua_getinfo(self.lua.state, cstr!("l"), self.ar.get()) != 0,
|
||||
ffi::lua_getinfo(self.lua.state(), cstr!("l"), self.ar.get()) != 0,
|
||||
"lua_getinfo failed with `l`"
|
||||
);
|
||||
#[cfg(feature = "luau")]
|
||||
mlua_assert!(
|
||||
ffi::lua_getinfo(self.lua.state, self.level, cstr!("l"), self.ar.get()) != 0,
|
||||
ffi::lua_getinfo(self.lua.state(), self.level, cstr!("l"), self.ar.get()) != 0,
|
||||
"lua_getinfo failed with `l`"
|
||||
);
|
||||
|
||||
@@ -139,7 +140,7 @@ impl<'lua> Debug<'lua> {
|
||||
pub fn is_tail_call(&self) -> bool {
|
||||
unsafe {
|
||||
mlua_assert!(
|
||||
ffi::lua_getinfo(self.lua.state, cstr!("t"), self.ar.get()) != 0,
|
||||
ffi::lua_getinfo(self.lua.state(), cstr!("t"), self.ar.get()) != 0,
|
||||
"lua_getinfo failed with `t`"
|
||||
);
|
||||
(*self.ar.get()).currentline != 0
|
||||
@@ -151,20 +152,20 @@ impl<'lua> Debug<'lua> {
|
||||
unsafe {
|
||||
#[cfg(not(feature = "luau"))]
|
||||
mlua_assert!(
|
||||
ffi::lua_getinfo(self.lua.state, cstr!("u"), self.ar.get()) != 0,
|
||||
ffi::lua_getinfo(self.lua.state(), cstr!("u"), self.ar.get()) != 0,
|
||||
"lua_getinfo failed with `u`"
|
||||
);
|
||||
#[cfg(feature = "luau")]
|
||||
mlua_assert!(
|
||||
ffi::lua_getinfo(self.lua.state, self.level, cstr!("a"), self.ar.get()) != 0,
|
||||
ffi::lua_getinfo(self.lua.state(), self.level, cstr!("a"), self.ar.get()) != 0,
|
||||
"lua_getinfo failed with `a`"
|
||||
);
|
||||
|
||||
#[cfg(not(feature = "luau"))]
|
||||
let stack = DebugStack {
|
||||
num_ups: (*self.ar.get()).nups as i32,
|
||||
num_ups: (*self.ar.get()).nups as _,
|
||||
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
|
||||
num_params: (*self.ar.get()).nparams as i32,
|
||||
num_params: (*self.ar.get()).nparams as _,
|
||||
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
|
||||
is_vararg: (*self.ar.get()).isvararg != 0,
|
||||
};
|
||||
@@ -266,48 +267,59 @@ pub struct HookTriggers {
|
||||
|
||||
#[cfg(not(feature = "luau"))]
|
||||
impl HookTriggers {
|
||||
/// Returns a new instance of `HookTriggers` with [`on_calls`] trigger set.
|
||||
/// An instance of `HookTriggers` with `on_calls` trigger set.
|
||||
pub const ON_CALLS: Self = HookTriggers::new().on_calls();
|
||||
|
||||
/// An instance of `HookTriggers` with `on_returns` trigger set.
|
||||
pub const ON_RETURNS: Self = HookTriggers::new().on_returns();
|
||||
|
||||
/// An instance of `HookTriggers` with `every_line` trigger set.
|
||||
pub const EVERY_LINE: Self = HookTriggers::new().every_line();
|
||||
|
||||
/// Returns a new instance of `HookTriggers` with all triggers disabled.
|
||||
pub const fn new() -> Self {
|
||||
HookTriggers {
|
||||
on_calls: false,
|
||||
on_returns: false,
|
||||
every_line: false,
|
||||
every_nth_instruction: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns an instance of `HookTriggers` with [`on_calls`] trigger set.
|
||||
///
|
||||
/// [`on_calls`]: #structfield.on_calls
|
||||
pub fn on_calls() -> Self {
|
||||
HookTriggers {
|
||||
on_calls: true,
|
||||
..Default::default()
|
||||
}
|
||||
pub const fn on_calls(mut self) -> Self {
|
||||
self.on_calls = true;
|
||||
self
|
||||
}
|
||||
|
||||
/// Returns a new instance of `HookTriggers` with [`on_returns`] trigger set.
|
||||
/// Returns an instance of `HookTriggers` with [`on_returns`] trigger set.
|
||||
///
|
||||
/// [`on_returns`]: #structfield.on_returns
|
||||
pub fn on_returns() -> Self {
|
||||
HookTriggers {
|
||||
on_returns: true,
|
||||
..Default::default()
|
||||
}
|
||||
pub const fn on_returns(mut self) -> Self {
|
||||
self.on_returns = true;
|
||||
self
|
||||
}
|
||||
|
||||
/// Returns a new instance of `HookTriggers` with [`every_line`] trigger set.
|
||||
/// Returns an instance of `HookTriggers` with [`every_line`] trigger set.
|
||||
///
|
||||
/// [`every_line`]: #structfield.every_line
|
||||
pub fn every_line() -> Self {
|
||||
HookTriggers {
|
||||
every_line: true,
|
||||
..Default::default()
|
||||
}
|
||||
pub const fn every_line(mut self) -> Self {
|
||||
self.every_line = true;
|
||||
self
|
||||
}
|
||||
|
||||
/// Returns a new instance of `HookTriggers` with [`every_nth_instruction`] trigger set.
|
||||
/// Returns an instance of `HookTriggers` with [`every_nth_instruction`] trigger set.
|
||||
///
|
||||
/// [`every_nth_instruction`]: #structfield.every_nth_instruction
|
||||
pub fn every_nth_instruction(n: u32) -> Self {
|
||||
HookTriggers {
|
||||
every_nth_instruction: Some(n),
|
||||
..Default::default()
|
||||
}
|
||||
pub const fn every_nth_instruction(mut self, n: u32) -> Self {
|
||||
self.every_nth_instruction = Some(n);
|
||||
self
|
||||
}
|
||||
|
||||
// Compute the mask to pass to `lua_sethook`.
|
||||
pub(crate) fn mask(&self) -> c_int {
|
||||
pub(crate) const fn mask(&self) -> c_int {
|
||||
let mut mask: c_int = 0;
|
||||
if self.on_calls {
|
||||
mask |= ffi::LUA_MASKCALL
|
||||
@@ -326,8 +338,9 @@ impl HookTriggers {
|
||||
|
||||
// Returns the `count` parameter to pass to `lua_sethook`, if applicable. Otherwise, zero is
|
||||
// returned.
|
||||
pub(crate) fn count(&self) -> c_int {
|
||||
self.every_nth_instruction.unwrap_or(0) as c_int
|
||||
pub(crate) const fn count(&self) -> c_int {
|
||||
let Some(n) = self.every_nth_instruction else { return 0 };
|
||||
n as c_int
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+43
-12
@@ -10,10 +10,10 @@
|
||||
//!
|
||||
//! # Converting data
|
||||
//!
|
||||
//! The [`ToLua`] and [`FromLua`] traits allow conversion from Rust types to Lua values and vice
|
||||
//! The [`IntoLua`] and [`FromLua`] traits allow conversion from Rust types to Lua values and vice
|
||||
//! versa. They are implemented for many data structures found in Rust's standard library.
|
||||
//!
|
||||
//! For more general conversions, the [`ToLuaMulti`] and [`FromLuaMulti`] traits allow converting
|
||||
//! For more general conversions, the [`IntoLuaMulti`] and [`FromLuaMulti`] traits allow converting
|
||||
//! between Rust types and *any number* of Lua values.
|
||||
//!
|
||||
//! Most code in `mlua` is generic over implementors of those traits, so in most places the normal
|
||||
@@ -54,9 +54,9 @@
|
||||
//! [executing]: crate::Chunk::exec
|
||||
//! [evaluating]: crate::Chunk::eval
|
||||
//! [globals]: crate::Lua::globals
|
||||
//! [`ToLua`]: crate::ToLua
|
||||
//! [`IntoLua`]: crate::IntoLua
|
||||
//! [`FromLua`]: crate::FromLua
|
||||
//! [`ToLuaMulti`]: crate::ToLuaMulti
|
||||
//! [`IntoLuaMulti`]: crate::IntoLuaMulti
|
||||
//! [`FromLuaMulti`]: crate::FromLuaMulti
|
||||
//! [`Function`]: crate::Function
|
||||
//! [`UserData`]: crate::UserData
|
||||
@@ -71,8 +71,6 @@
|
||||
//! [`serde::Serialize`]: https://docs.serde.rs/serde/ser/trait.Serialize.html
|
||||
//! [`serde::Deserialize`]: https://docs.serde.rs/serde/de/trait.Deserialize.html
|
||||
|
||||
// mlua types in rustdoc of other crates get linked to here.
|
||||
#![doc(html_root_url = "https://docs.rs/mlua/0.8.6")]
|
||||
// Deny warnings inside doc tests / examples. When this isn't present, rustdoc doesn't show *any*
|
||||
// warnings at all.
|
||||
#![doc(test(attr(deny(warnings))))]
|
||||
@@ -84,12 +82,12 @@ mod macros;
|
||||
mod chunk;
|
||||
mod conversion;
|
||||
mod error;
|
||||
mod ffi;
|
||||
mod function;
|
||||
mod hook;
|
||||
mod lua;
|
||||
#[cfg(feature = "luau")]
|
||||
mod luau;
|
||||
mod memory;
|
||||
mod multi;
|
||||
mod scope;
|
||||
mod stdlib;
|
||||
@@ -98,16 +96,17 @@ mod table;
|
||||
mod thread;
|
||||
mod types;
|
||||
mod userdata;
|
||||
mod userdata_ext;
|
||||
mod userdata_impl;
|
||||
mod util;
|
||||
mod value;
|
||||
|
||||
pub mod prelude;
|
||||
|
||||
pub use crate::{ffi::lua_CFunction, ffi::lua_State};
|
||||
pub use ffi::{lua_CFunction, lua_State};
|
||||
|
||||
pub use crate::chunk::{AsChunk, Chunk, ChunkMode};
|
||||
pub use crate::error::{Error, ExternalError, ExternalResult, Result};
|
||||
pub use crate::error::{Error, ErrorContext, ExternalError, ExternalResult, Result};
|
||||
pub use crate::function::{Function, FunctionInfo};
|
||||
pub use crate::hook::{Debug, DebugEvent, DebugNames, DebugSource, DebugStack};
|
||||
pub use crate::lua::{GCMode, Lua, LuaOptions};
|
||||
@@ -120,8 +119,11 @@ pub use crate::thread::{Thread, ThreadStatus};
|
||||
pub use crate::types::{Integer, LightUserData, Number, RegistryKey};
|
||||
pub use crate::userdata::{
|
||||
AnyUserData, MetaMethod, UserData, UserDataFields, UserDataMetatable, UserDataMethods,
|
||||
UserDataRef, UserDataRefMut,
|
||||
};
|
||||
pub use crate::value::{FromLua, FromLuaMulti, MultiValue, Nil, ToLua, ToLuaMulti, Value};
|
||||
pub use crate::userdata_ext::AnyUserDataExt;
|
||||
pub use crate::userdata_impl::UserDataRegistrar;
|
||||
pub use crate::value::{FromLua, FromLuaMulti, IntoLua, IntoLuaMulti, MultiValue, Nil, Value};
|
||||
|
||||
#[cfg(not(feature = "luau"))]
|
||||
pub use crate::hook::HookTriggers;
|
||||
@@ -148,12 +150,18 @@ pub mod serde;
|
||||
#[macro_use]
|
||||
extern crate mlua_derive;
|
||||
|
||||
// Unstable features
|
||||
#[cfg(feature = "unstable")]
|
||||
pub use crate::{
|
||||
function::OwnedFunction, string::OwnedString, table::OwnedTable, userdata::OwnedAnyUserData,
|
||||
};
|
||||
|
||||
/// Create a type that implements [`AsChunk`] and can capture Rust variables.
|
||||
///
|
||||
/// This macro allows to write Lua code directly in Rust code.
|
||||
///
|
||||
/// Rust variables can be referenced from Lua using `$` prefix, as shown in the example below.
|
||||
/// User's Rust types needs to implement [`UserData`] or [`ToLua`] traits.
|
||||
/// User's Rust types needs to implement [`UserData`] or [`IntoLua`] traits.
|
||||
///
|
||||
/// Captured variables are **moved** into the chunk.
|
||||
///
|
||||
@@ -199,7 +207,7 @@ extern crate mlua_derive;
|
||||
///
|
||||
/// [`AsChunk`]: crate::AsChunk
|
||||
/// [`UserData`]: crate::UserData
|
||||
/// [`ToLua`]: crate::ToLua
|
||||
/// [`IntoLua`]: crate::IntoLua
|
||||
#[cfg(any(feature = "macros"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "macros")))]
|
||||
pub use mlua_derive::chunk;
|
||||
@@ -221,6 +229,29 @@ pub use mlua_derive::chunk;
|
||||
///
|
||||
/// Internally in the code above the compiler defines C function `luaopen_my_module`.
|
||||
///
|
||||
/// You can also pass options to the attribute:
|
||||
///
|
||||
/// name - name of the module, defaults to the name of the function
|
||||
///
|
||||
/// ```ignore
|
||||
/// #[mlua::lua_module(name = "alt_module")]
|
||||
/// fn my_module(lua: &Lua) -> Result<Table> {
|
||||
/// ...
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
#[cfg(any(feature = "module", docsrs))]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "module")))]
|
||||
pub use mlua_derive::lua_module;
|
||||
|
||||
pub(crate) mod private {
|
||||
use super::*;
|
||||
|
||||
pub trait Sealed {}
|
||||
|
||||
impl Sealed for Error {}
|
||||
impl<T> Sealed for std::result::Result<T, Error> {}
|
||||
impl Sealed for Lua {}
|
||||
impl Sealed for Table<'_> {}
|
||||
impl Sealed for AnyUserData<'_> {}
|
||||
}
|
||||
|
||||
+762
-709
File diff suppressed because it is too large
Load Diff
+8
-7
@@ -1,9 +1,9 @@
|
||||
use std::ffi::CStr;
|
||||
use std::os::raw::{c_float, c_int};
|
||||
use std::string::String as StdString;
|
||||
|
||||
use crate::chunk::ChunkMode;
|
||||
use crate::error::{Error, Result};
|
||||
use crate::ffi;
|
||||
use crate::lua::Lua;
|
||||
use crate::table::Table;
|
||||
use crate::util::{check_stack, StackGuard};
|
||||
@@ -69,14 +69,15 @@ unsafe extern "C" fn lua_collectgarbage(state: *mut ffi::lua_State) -> c_int {
|
||||
}
|
||||
}
|
||||
|
||||
fn lua_require(lua: &Lua, name: Option<std::string::String>) -> Result<Value> {
|
||||
fn lua_require(lua: &Lua, name: Option<StdString>) -> Result<Value> {
|
||||
let name = name.ok_or_else(|| Error::RuntimeError("invalid module name".into()))?;
|
||||
|
||||
// Find module in the cache
|
||||
let state = lua.state();
|
||||
let loaded = unsafe {
|
||||
let _sg = StackGuard::new(lua.state);
|
||||
check_stack(lua.state, 2)?;
|
||||
protect_lua!(lua.state, 0, 1, fn(state) {
|
||||
let _sg = StackGuard::new(state);
|
||||
check_stack(state, 2)?;
|
||||
protect_lua!(state, 0, 1, fn(state) {
|
||||
ffi::luaL_getsubtable(state, ffi::LUA_REGISTRYINDEX, cstr!("_LOADED"));
|
||||
})?;
|
||||
Table(lua.pop_ref())
|
||||
@@ -100,11 +101,11 @@ fn lua_require(lua: &Lua, name: Option<std::string::String>) -> Result<Value> {
|
||||
break;
|
||||
}
|
||||
}
|
||||
let source = source.ok_or_else(|| Error::RuntimeError(format!("cannot find '{}'", name)))?;
|
||||
let source = source.ok_or_else(|| Error::RuntimeError(format!("cannot find '{name}'")))?;
|
||||
|
||||
let value = lua
|
||||
.load(&source)
|
||||
.set_name(&format!("={}", source_name))?
|
||||
.set_name(&format!("={source_name}"))
|
||||
.set_mode(ChunkMode::Text)
|
||||
.call::<_, Value>(())?;
|
||||
|
||||
|
||||
+152
@@ -0,0 +1,152 @@
|
||||
use std::alloc::{self, Layout};
|
||||
use std::os::raw::c_void;
|
||||
use std::ptr;
|
||||
|
||||
#[cfg(feature = "luau")]
|
||||
use crate::lua::ExtraData;
|
||||
|
||||
pub(crate) static ALLOCATOR: ffi::lua_Alloc = allocator;
|
||||
|
||||
#[derive(Default)]
|
||||
pub(crate) struct MemoryState {
|
||||
used_memory: isize,
|
||||
memory_limit: isize,
|
||||
// Can be set to temporary ignore the memory limit.
|
||||
// This is used when calling `lua_pushcfunction` for lua5.1/jit/luau.
|
||||
ignore_limit: bool,
|
||||
// Indicates that the memory limit was reached on the last allocation.
|
||||
#[cfg(feature = "luau")]
|
||||
limit_reached: bool,
|
||||
}
|
||||
|
||||
impl MemoryState {
|
||||
#[inline]
|
||||
pub(crate) fn used_memory(&self) -> usize {
|
||||
self.used_memory as usize
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn memory_limit(&self) -> usize {
|
||||
self.memory_limit as usize
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn set_memory_limit(&mut self, limit: usize) -> usize {
|
||||
let prev_limit = self.memory_limit;
|
||||
self.memory_limit = limit as isize;
|
||||
prev_limit as usize
|
||||
}
|
||||
|
||||
// This function is used primarily for calling `lua_pushcfunction` in lua5.1/jit
|
||||
// to bypass the memory limit (if set).
|
||||
#[cfg(any(feature = "lua51", feature = "luajit"))]
|
||||
#[inline]
|
||||
pub(crate) unsafe fn relax_limit_with(state: *mut ffi::lua_State, f: impl FnOnce()) {
|
||||
let mut mem_state: *mut c_void = ptr::null_mut();
|
||||
if ffi::lua_getallocf(state, &mut mem_state) == ALLOCATOR {
|
||||
(*(mem_state as *mut MemoryState)).ignore_limit = true;
|
||||
f();
|
||||
(*(mem_state as *mut MemoryState)).ignore_limit = false;
|
||||
} else {
|
||||
f();
|
||||
}
|
||||
}
|
||||
|
||||
// Same as the above but for Luau
|
||||
// It does not have `lua_getallocf` function, so instead we use `lua_callbacks`
|
||||
#[cfg(feature = "luau")]
|
||||
#[inline]
|
||||
pub(crate) unsafe fn relax_limit_with(state: *mut ffi::lua_State, f: impl FnOnce()) {
|
||||
let extra = (*ffi::lua_callbacks(state)).userdata as *mut ExtraData;
|
||||
if extra.is_null() {
|
||||
return f();
|
||||
}
|
||||
let mem_state = (*extra).mem_state();
|
||||
(*mem_state.as_ptr()).ignore_limit = true;
|
||||
f();
|
||||
(*mem_state.as_ptr()).ignore_limit = false;
|
||||
}
|
||||
|
||||
// Does nothing apart from calling `f()`, we don't need to bypass any limits
|
||||
#[cfg(any(feature = "lua52", feature = "lua53", feature = "lua54"))]
|
||||
#[inline]
|
||||
pub(crate) unsafe fn relax_limit_with(_state: *mut ffi::lua_State, f: impl FnOnce()) {
|
||||
f();
|
||||
}
|
||||
|
||||
// Returns `true` if the memory limit was reached on the last memory operation
|
||||
#[cfg(feature = "luau")]
|
||||
pub(crate) unsafe fn limit_reached(state: *mut ffi::lua_State) -> bool {
|
||||
let extra = (*ffi::lua_callbacks(state)).userdata as *mut ExtraData;
|
||||
if extra.is_null() {
|
||||
return false;
|
||||
}
|
||||
(*(*extra).mem_state().as_ptr()).limit_reached
|
||||
}
|
||||
}
|
||||
|
||||
unsafe extern "C" fn allocator(
|
||||
extra: *mut c_void,
|
||||
ptr: *mut c_void,
|
||||
osize: usize,
|
||||
nsize: usize,
|
||||
) -> *mut c_void {
|
||||
let mem_state = &mut *(extra as *mut MemoryState);
|
||||
#[cfg(feature = "luau")]
|
||||
{
|
||||
// Reset the flag
|
||||
mem_state.limit_reached = false;
|
||||
}
|
||||
|
||||
if nsize == 0 {
|
||||
// Free memory
|
||||
if !ptr.is_null() {
|
||||
let layout = Layout::from_size_align_unchecked(osize, ffi::SYS_MIN_ALIGN);
|
||||
alloc::dealloc(ptr as *mut u8, layout);
|
||||
mem_state.used_memory -= osize as isize;
|
||||
}
|
||||
return ptr::null_mut();
|
||||
}
|
||||
|
||||
// Do not allocate more than isize::MAX
|
||||
if nsize > isize::MAX as usize {
|
||||
return ptr::null_mut();
|
||||
}
|
||||
|
||||
// Are we fit to the memory limits?
|
||||
let mut mem_diff = nsize as isize;
|
||||
if !ptr.is_null() {
|
||||
mem_diff -= osize as isize;
|
||||
}
|
||||
let mem_limit = mem_state.memory_limit;
|
||||
let new_used_memory = mem_state.used_memory + mem_diff;
|
||||
if mem_limit > 0 && new_used_memory > mem_limit && !mem_state.ignore_limit {
|
||||
#[cfg(feature = "luau")]
|
||||
{
|
||||
mem_state.limit_reached = true;
|
||||
}
|
||||
return ptr::null_mut();
|
||||
}
|
||||
mem_state.used_memory += mem_diff;
|
||||
|
||||
if ptr.is_null() {
|
||||
// Allocate new memory
|
||||
let new_layout = match Layout::from_size_align(nsize, ffi::SYS_MIN_ALIGN) {
|
||||
Ok(layout) => layout,
|
||||
Err(_) => return ptr::null_mut(),
|
||||
};
|
||||
let new_ptr = alloc::alloc(new_layout) as *mut c_void;
|
||||
if new_ptr.is_null() {
|
||||
alloc::handle_alloc_error(new_layout);
|
||||
}
|
||||
return new_ptr;
|
||||
}
|
||||
|
||||
// Reallocate memory
|
||||
let old_layout = Layout::from_size_align_unchecked(osize, ffi::SYS_MIN_ALIGN);
|
||||
let new_ptr = alloc::realloc(ptr as *mut u8, old_layout, nsize) as *mut c_void;
|
||||
if new_ptr.is_null() {
|
||||
alloc::handle_alloc_error(old_layout);
|
||||
}
|
||||
new_ptr
|
||||
}
|
||||
+54
-32
@@ -1,23 +1,21 @@
|
||||
#![allow(clippy::wrong_self_convention)]
|
||||
|
||||
use std::iter::FromIterator;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use std::result::Result as StdResult;
|
||||
|
||||
use crate::error::Result;
|
||||
use crate::lua::Lua;
|
||||
use crate::value::{FromLua, FromLuaMulti, MultiValue, Nil, ToLua, ToLuaMulti};
|
||||
use crate::value::{FromLua, FromLuaMulti, IntoLua, IntoLuaMulti, MultiValue, Nil};
|
||||
|
||||
/// Result is convertible to `MultiValue` following the common Lua idiom of returning the result
|
||||
/// on success, or in the case of an error, returning `nil` and an error message.
|
||||
impl<'lua, T: ToLua<'lua>, E: ToLua<'lua>> ToLuaMulti<'lua> for StdResult<T, E> {
|
||||
impl<'lua, T: IntoLua<'lua>, E: IntoLua<'lua>> IntoLuaMulti<'lua> for StdResult<T, E> {
|
||||
#[inline]
|
||||
fn to_lua_multi(self, lua: &'lua Lua) -> Result<MultiValue<'lua>> {
|
||||
let mut result = MultiValue::new_or_cached(lua);
|
||||
fn into_lua_multi(self, lua: &'lua Lua) -> Result<MultiValue<'lua>> {
|
||||
let mut result = MultiValue::new_or_pooled(lua);
|
||||
match self {
|
||||
Ok(v) => result.push_front(v.to_lua(lua)?),
|
||||
Ok(v) => result.push_front(v.into_lua(lua)?),
|
||||
Err(e) => {
|
||||
result.push_front(e.to_lua(lua)?);
|
||||
result.push_front(e.into_lua(lua)?);
|
||||
result.push_front(Nil);
|
||||
}
|
||||
}
|
||||
@@ -25,11 +23,11 @@ impl<'lua, T: ToLua<'lua>, E: ToLua<'lua>> ToLuaMulti<'lua> for StdResult<T, E>
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua, T: ToLua<'lua>> ToLuaMulti<'lua> for T {
|
||||
impl<'lua, T: IntoLua<'lua>> IntoLuaMulti<'lua> for T {
|
||||
#[inline]
|
||||
fn to_lua_multi(self, lua: &'lua Lua) -> Result<MultiValue<'lua>> {
|
||||
let mut v = MultiValue::new_or_cached(lua);
|
||||
v.push_front(self.to_lua(lua)?);
|
||||
fn into_lua_multi(self, lua: &'lua Lua) -> Result<MultiValue<'lua>> {
|
||||
let mut v = MultiValue::new_or_pooled(lua);
|
||||
v.push_front(self.into_lua(lua)?);
|
||||
Ok(v)
|
||||
}
|
||||
}
|
||||
@@ -38,14 +36,26 @@ impl<'lua, T: FromLua<'lua>> FromLuaMulti<'lua> for T {
|
||||
#[inline]
|
||||
fn from_lua_multi(mut values: MultiValue<'lua>, lua: &'lua Lua) -> Result<Self> {
|
||||
let res = T::from_lua(values.pop_front().unwrap_or(Nil), lua);
|
||||
lua.cache_multivalue(values);
|
||||
MultiValue::return_to_pool(values, lua);
|
||||
res
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn from_lua_multi_args(
|
||||
mut values: MultiValue<'lua>,
|
||||
i: usize,
|
||||
to: Option<&str>,
|
||||
lua: &'lua Lua,
|
||||
) -> Result<Self> {
|
||||
let res = T::from_lua_arg(values.pop_front().unwrap_or(Nil), i, to, lua);
|
||||
MultiValue::return_to_pool(values, lua);
|
||||
res
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> ToLuaMulti<'lua> for MultiValue<'lua> {
|
||||
impl<'lua> IntoLuaMulti<'lua> for MultiValue<'lua> {
|
||||
#[inline]
|
||||
fn to_lua_multi(self, _: &'lua Lua) -> Result<MultiValue<'lua>> {
|
||||
fn into_lua_multi(self, _: &'lua Lua) -> Result<MultiValue<'lua>> {
|
||||
Ok(self)
|
||||
}
|
||||
}
|
||||
@@ -128,11 +138,11 @@ impl<T> DerefMut for Variadic<T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua, T: ToLua<'lua>> ToLuaMulti<'lua> for Variadic<T> {
|
||||
impl<'lua, T: IntoLua<'lua>> IntoLuaMulti<'lua> for Variadic<T> {
|
||||
#[inline]
|
||||
fn to_lua_multi(self, lua: &'lua Lua) -> Result<MultiValue<'lua>> {
|
||||
let mut values = MultiValue::new_or_cached(lua);
|
||||
values.refill(self.0.into_iter().map(|e| e.to_lua(lua)))?;
|
||||
fn into_lua_multi(self, lua: &'lua Lua) -> Result<MultiValue<'lua>> {
|
||||
let mut values = MultiValue::new_or_pooled(lua);
|
||||
values.refill(self.0.into_iter().map(|e| e.into_lua(lua)))?;
|
||||
Ok(values)
|
||||
}
|
||||
}
|
||||
@@ -145,42 +155,42 @@ impl<'lua, T: FromLua<'lua>> FromLuaMulti<'lua> for Variadic<T> {
|
||||
.map(|e| T::from_lua(e, lua))
|
||||
.collect::<Result<Vec<T>>>()
|
||||
.map(Variadic);
|
||||
lua.cache_multivalue(values);
|
||||
MultiValue::return_to_pool(values, lua);
|
||||
res
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! impl_tuple {
|
||||
() => (
|
||||
impl<'lua> ToLuaMulti<'lua> for () {
|
||||
impl<'lua> IntoLuaMulti<'lua> for () {
|
||||
#[inline]
|
||||
fn to_lua_multi(self, lua: &'lua Lua) -> Result<MultiValue<'lua>> {
|
||||
Ok(MultiValue::new_or_cached(lua))
|
||||
fn into_lua_multi(self, lua: &'lua Lua) -> Result<MultiValue<'lua>> {
|
||||
Ok(MultiValue::new_or_pooled(lua))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> FromLuaMulti<'lua> for () {
|
||||
#[inline]
|
||||
fn from_lua_multi(values: MultiValue<'lua>, lua: &'lua Lua) -> Result<Self> {
|
||||
lua.cache_multivalue(values);
|
||||
MultiValue::return_to_pool(values, lua);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
($last:ident $($name:ident)*) => (
|
||||
impl<'lua, $($name,)* $last> ToLuaMulti<'lua> for ($($name,)* $last,)
|
||||
where $($name: ToLua<'lua>,)*
|
||||
$last: ToLuaMulti<'lua>
|
||||
impl<'lua, $($name,)* $last> IntoLuaMulti<'lua> for ($($name,)* $last,)
|
||||
where $($name: IntoLua<'lua>,)*
|
||||
$last: IntoLuaMulti<'lua>
|
||||
{
|
||||
#[allow(unused_mut)]
|
||||
#[allow(non_snake_case)]
|
||||
#[inline]
|
||||
fn to_lua_multi(self, lua: &'lua Lua) -> Result<MultiValue<'lua>> {
|
||||
fn into_lua_multi(self, lua: &'lua Lua) -> Result<MultiValue<'lua>> {
|
||||
let ($($name,)* $last,) = self;
|
||||
|
||||
let mut results = $last.to_lua_multi(lua)?;
|
||||
push_reverse!(results, $($name.to_lua(lua)?,)*);
|
||||
let mut results = $last.into_lua_multi(lua)?;
|
||||
push_reverse!(results, $($name.into_lua(lua)?,)*);
|
||||
Ok(results)
|
||||
}
|
||||
}
|
||||
@@ -193,9 +203,21 @@ macro_rules! impl_tuple {
|
||||
#[allow(non_snake_case)]
|
||||
#[inline]
|
||||
fn from_lua_multi(mut values: MultiValue<'lua>, lua: &'lua Lua) -> Result<Self> {
|
||||
$(let $name = values.pop_front().unwrap_or(Nil);)*
|
||||
$(let $name = FromLua::from_lua(values.pop_front().unwrap_or(Nil), lua)?;)*
|
||||
let $last = FromLuaMulti::from_lua_multi(values, lua)?;
|
||||
Ok(($(FromLua::from_lua($name, lua)?,)* $last,))
|
||||
Ok(($($name,)* $last,))
|
||||
}
|
||||
|
||||
#[allow(unused_mut)]
|
||||
#[allow(non_snake_case)]
|
||||
#[inline]
|
||||
fn from_lua_multi_args(mut values: MultiValue<'lua>, mut i: usize, to: Option<&str>, lua: &'lua Lua) -> Result<Self> {
|
||||
$(
|
||||
let $name = FromLua::from_lua_arg(values.pop_front().unwrap_or(Nil), i, to, lua)?;
|
||||
i += 1;
|
||||
)*
|
||||
let $last = FromLuaMulti::from_lua_multi_args(values, i, to, lua)?;
|
||||
Ok(($($name,)* $last,))
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
+19
-10
@@ -2,16 +2,18 @@
|
||||
|
||||
#[doc(no_inline)]
|
||||
pub use crate::{
|
||||
AnyUserData as LuaAnyUserData, Chunk as LuaChunk, Error as LuaError,
|
||||
ExternalError as LuaExternalError, ExternalResult as LuaExternalResult, FromLua, FromLuaMulti,
|
||||
Function as LuaFunction, FunctionInfo as LuaFunctionInfo, GCMode as LuaGCMode,
|
||||
Integer as LuaInteger, LightUserData as LuaLightUserData, Lua, LuaOptions,
|
||||
MetaMethod as LuaMetaMethod, MultiValue as LuaMultiValue, Nil as LuaNil, Number as LuaNumber,
|
||||
RegistryKey as LuaRegistryKey, Result as LuaResult, StdLib as LuaStdLib, String as LuaString,
|
||||
Table as LuaTable, TableExt as LuaTableExt, TablePairs as LuaTablePairs,
|
||||
TableSequence as LuaTableSequence, Thread as LuaThread, ThreadStatus as LuaThreadStatus, ToLua,
|
||||
ToLuaMulti, UserData as LuaUserData, UserDataFields as LuaUserDataFields,
|
||||
UserDataMetatable as LuaUserDataMetatable, UserDataMethods as LuaUserDataMethods,
|
||||
AnyUserData as LuaAnyUserData, AnyUserDataExt as LuaAnyUserDataExt, Chunk as LuaChunk,
|
||||
Error as LuaError, ErrorContext as LuaErrorContext, ExternalError as LuaExternalError,
|
||||
ExternalResult as LuaExternalResult, FromLua, FromLuaMulti, Function as LuaFunction,
|
||||
FunctionInfo as LuaFunctionInfo, GCMode as LuaGCMode, Integer as LuaInteger, IntoLua,
|
||||
IntoLuaMulti, LightUserData as LuaLightUserData, Lua, LuaOptions, MetaMethod as LuaMetaMethod,
|
||||
MultiValue as LuaMultiValue, Nil as LuaNil, Number as LuaNumber, RegistryKey as LuaRegistryKey,
|
||||
Result as LuaResult, StdLib as LuaStdLib, String as LuaString, Table as LuaTable,
|
||||
TableExt as LuaTableExt, TablePairs as LuaTablePairs, TableSequence as LuaTableSequence,
|
||||
Thread as LuaThread, ThreadStatus as LuaThreadStatus, UserData as LuaUserData,
|
||||
UserDataFields as LuaUserDataFields, UserDataMetatable as LuaUserDataMetatable,
|
||||
UserDataMethods as LuaUserDataMethods, UserDataRef as LuaUserDataRef,
|
||||
UserDataRefMut as LuaUserDataRefMut, UserDataRegistrar as LuaUserDataRegistrar,
|
||||
Value as LuaValue,
|
||||
};
|
||||
|
||||
@@ -33,3 +35,10 @@ pub use crate::{
|
||||
DeserializeOptions as LuaDeserializeOptions, LuaSerdeExt,
|
||||
SerializeOptions as LuaSerializeOptions,
|
||||
};
|
||||
|
||||
#[cfg(feature = "unstable")]
|
||||
#[doc(no_inline)]
|
||||
pub use crate::{
|
||||
OwnedAnyUserData as LuaOwnedAnyUserData, OwnedFunction as LuaOwnedFunction,
|
||||
OwnedString as LuaOwnedString, OwnedTable as LuaOwnedTable,
|
||||
};
|
||||
|
||||
+296
-375
@@ -2,14 +2,12 @@ use std::any::Any;
|
||||
use std::cell::{Cell, RefCell};
|
||||
use std::marker::PhantomData;
|
||||
use std::mem;
|
||||
use std::os::raw::{c_int, c_void};
|
||||
use std::rc::Rc;
|
||||
use std::os::raw::c_int;
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
use serde::Serialize;
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
use crate::ffi;
|
||||
use crate::function::Function;
|
||||
use crate::lua::Lua;
|
||||
use crate::types::{Callback, CallbackUpvalue, LuaRef, MaybeSend};
|
||||
@@ -20,17 +18,13 @@ use crate::util::{
|
||||
assert_stack, check_stack, get_userdata, init_userdata_metatable, push_table, rawset_field,
|
||||
take_userdata, StackGuard,
|
||||
};
|
||||
use crate::value::{FromLua, FromLuaMulti, MultiValue, ToLua, ToLuaMulti, Value};
|
||||
use crate::value::{FromLua, FromLuaMulti, IntoLua, IntoLuaMulti, MultiValue, Value};
|
||||
|
||||
#[cfg(feature = "lua54")]
|
||||
use crate::userdata::USER_VALUE_MAXSLOT;
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
use {
|
||||
crate::types::{AsyncCallback, AsyncCallbackUpvalue, AsyncPollUpvalue},
|
||||
futures_core::future::Future,
|
||||
futures_util::future::{self, TryFutureExt},
|
||||
};
|
||||
use futures_core::future::Future;
|
||||
|
||||
/// Constructed by the [`Lua::scope`] method, allows temporarily creating Lua userdata and
|
||||
/// callbacks that are not required to be Send or 'static.
|
||||
@@ -65,8 +59,8 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
|
||||
pub fn create_function<'callback, A, R, F>(&'callback self, func: F) -> Result<Function<'lua>>
|
||||
where
|
||||
A: FromLuaMulti<'callback>,
|
||||
R: ToLuaMulti<'callback>,
|
||||
F: 'scope + Fn(&'callback Lua, A) -> Result<R>,
|
||||
R: IntoLuaMulti<'callback>,
|
||||
F: Fn(&'callback Lua, A) -> Result<R> + 'scope,
|
||||
{
|
||||
// Safe, because 'scope must outlive 'callback (due to Self containing 'scope), however the
|
||||
// callback itself must be 'scope lifetime, so the function should not be able to capture
|
||||
@@ -79,7 +73,7 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
|
||||
// scope, and owned inside the callback itself.
|
||||
unsafe {
|
||||
self.create_callback(Box::new(move |lua, args| {
|
||||
func(lua, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
|
||||
func(lua, A::from_lua_multi(args, lua)?)?.into_lua_multi(lua)
|
||||
}))
|
||||
}
|
||||
}
|
||||
@@ -98,8 +92,8 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
|
||||
) -> Result<Function<'lua>>
|
||||
where
|
||||
A: FromLuaMulti<'callback>,
|
||||
R: ToLuaMulti<'callback>,
|
||||
F: 'scope + FnMut(&'callback Lua, A) -> Result<R>,
|
||||
R: IntoLuaMulti<'callback>,
|
||||
F: FnMut(&'callback Lua, A) -> Result<R> + 'scope,
|
||||
{
|
||||
let func = RefCell::new(func);
|
||||
self.create_function(move |lua, args| {
|
||||
@@ -109,40 +103,7 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
|
||||
})
|
||||
}
|
||||
|
||||
/// Wraps a Rust async function or closure, creating a callable Lua function handle to it.
|
||||
///
|
||||
/// This is a version of [`Lua::create_async_function`] that creates a callback which expires on
|
||||
/// scope drop. See [`Lua::scope`] and [`Lua::async_scope`] for more details.
|
||||
///
|
||||
/// Requires `feature = "async"`
|
||||
///
|
||||
/// [`Lua::create_async_function`]: crate::Lua::create_async_function
|
||||
/// [`Lua::scope`]: crate::Lua::scope
|
||||
/// [`Lua::async_scope`]: crate::Lua::async_scope
|
||||
#[cfg(feature = "async")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
|
||||
pub fn create_async_function<'callback, A, R, F, FR>(
|
||||
&'callback self,
|
||||
func: F,
|
||||
) -> Result<Function<'lua>>
|
||||
where
|
||||
A: FromLuaMulti<'callback>,
|
||||
R: ToLuaMulti<'callback>,
|
||||
F: 'scope + Fn(&'callback Lua, A) -> FR,
|
||||
FR: 'callback + Future<Output = Result<R>>,
|
||||
{
|
||||
unsafe {
|
||||
self.create_async_callback(Box::new(move |lua, args| {
|
||||
let args = match A::from_lua_multi(args, lua) {
|
||||
Ok(args) => args,
|
||||
Err(e) => return Box::pin(future::err(e)),
|
||||
};
|
||||
Box::pin(func(lua, args).and_then(move |ret| future::ready(ret.to_lua_multi(lua))))
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a Lua userdata object from a custom userdata type.
|
||||
/// Creates a Lua userdata object from a custom userdata type.
|
||||
///
|
||||
/// This is a version of [`Lua::create_userdata`] that creates a userdata which expires on
|
||||
/// scope drop, and does not require that the userdata type be Send (but still requires that the
|
||||
@@ -153,12 +114,18 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
|
||||
/// [`Lua::scope`]: crate::Lua::scope
|
||||
pub fn create_userdata<T>(&self, data: T) -> Result<AnyUserData<'lua>>
|
||||
where
|
||||
T: 'static + UserData,
|
||||
T: UserData + 'static,
|
||||
{
|
||||
self.create_userdata_inner(UserDataCell::new(data))
|
||||
// Safe even though T may not be Send, because the parent Lua cannot be sent to another
|
||||
// thread while the Scope is alive (or the returned AnyUserData handle even).
|
||||
unsafe {
|
||||
let ud = self.lua.make_userdata(UserDataCell::new(data))?;
|
||||
self.seal_userdata::<T>(&ud)?;
|
||||
Ok(ud)
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a Lua userdata object from a custom serializable userdata type.
|
||||
/// Creates a Lua userdata object from a custom serializable userdata type.
|
||||
///
|
||||
/// This is a version of [`Lua::create_ser_userdata`] that creates a userdata which expires on
|
||||
/// scope drop, and does not require that the userdata type be Send (but still requires that the
|
||||
@@ -173,60 +140,125 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "serialize")))]
|
||||
pub fn create_ser_userdata<T>(&self, data: T) -> Result<AnyUserData<'lua>>
|
||||
where
|
||||
T: 'static + UserData + Serialize,
|
||||
T: UserData + Serialize + 'static,
|
||||
{
|
||||
self.create_userdata_inner(UserDataCell::new_ser(data))
|
||||
}
|
||||
|
||||
fn create_userdata_inner<T>(&self, data: UserDataCell<T>) -> Result<AnyUserData<'lua>>
|
||||
where
|
||||
T: 'static + UserData,
|
||||
{
|
||||
// Safe even though T may not be Send, because the parent Lua cannot be sent to another
|
||||
// thread while the Scope is alive (or the returned AnyUserData handle even).
|
||||
unsafe {
|
||||
let ud = self.lua.make_userdata(data)?;
|
||||
|
||||
#[cfg(any(feature = "lua51", feature = "luajit"))]
|
||||
let newtable = self.lua.create_table()?;
|
||||
let destructor: DestructorCallback = Box::new(move |ud| {
|
||||
let state = ud.lua.state;
|
||||
let _sg = StackGuard::new(state);
|
||||
assert_stack(state, 2);
|
||||
|
||||
// Check that userdata is not destructed (via `take()` call)
|
||||
if ud.lua.push_userdata_ref(&ud).is_err() {
|
||||
return vec![];
|
||||
}
|
||||
|
||||
// Clear associated user values
|
||||
#[cfg(feature = "lua54")]
|
||||
for i in 1..=USER_VALUE_MAXSLOT {
|
||||
ffi::lua_pushnil(state);
|
||||
ffi::lua_setiuservalue(state, -2, i as c_int);
|
||||
}
|
||||
#[cfg(any(feature = "lua53", feature = "lua52", feature = "luau"))]
|
||||
{
|
||||
ffi::lua_pushnil(state);
|
||||
ffi::lua_setuservalue(state, -2);
|
||||
}
|
||||
#[cfg(any(feature = "lua51", feature = "luajit"))]
|
||||
{
|
||||
ud.lua.push_ref(&newtable.0);
|
||||
ffi::lua_setuservalue(state, -2);
|
||||
}
|
||||
|
||||
vec![Box::new(take_userdata::<UserDataCell<T>>(state))]
|
||||
});
|
||||
self.destructors
|
||||
.borrow_mut()
|
||||
.push((ud.0.clone(), destructor));
|
||||
|
||||
let ud = self.lua.make_userdata(UserDataCell::new_ser(data))?;
|
||||
self.seal_userdata::<T>(&ud)?;
|
||||
Ok(ud)
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a Lua userdata object from a custom userdata type.
|
||||
/// Creates a Lua userdata object from a reference to custom userdata type.
|
||||
///
|
||||
/// This is a version of [`Lua::create_userdata`] that creates a userdata which expires on
|
||||
/// scope drop, and does not require that the userdata type be Send. This method takes non-'static
|
||||
/// reference to the data. See [`Lua::scope`] for more details.
|
||||
///
|
||||
/// Userdata created with this method will not be able to be mutated from Lua.
|
||||
pub fn create_userdata_ref<T>(&self, data: &'scope T) -> Result<AnyUserData<'lua>>
|
||||
where
|
||||
T: UserData + 'static,
|
||||
{
|
||||
unsafe {
|
||||
let ud = self.lua.make_userdata(UserDataCell::new_ref(data))?;
|
||||
self.seal_userdata::<T>(&ud)?;
|
||||
Ok(ud)
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a Lua userdata object from a mutable reference to custom userdata type.
|
||||
///
|
||||
/// This is a version of [`Lua::create_userdata`] that creates a userdata which expires on
|
||||
/// scope drop, and does not require that the userdata type be Send. This method takes non-'static
|
||||
/// mutable reference to the data. See [`Lua::scope`] for more details.
|
||||
pub fn create_userdata_ref_mut<T>(&self, data: &'scope mut T) -> Result<AnyUserData<'lua>>
|
||||
where
|
||||
T: UserData + 'static,
|
||||
{
|
||||
unsafe {
|
||||
let ud = self.lua.make_userdata(UserDataCell::new_ref_mut(data))?;
|
||||
self.seal_userdata::<T>(&ud)?;
|
||||
Ok(ud)
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a Lua userdata object from a reference to custom Rust type.
|
||||
///
|
||||
/// This is a version of [`Lua::create_any_userdata`] that creates a userdata which expires on
|
||||
/// scope drop, and does not require that the Rust type be Send. This method takes non-'static
|
||||
/// reference to the data. See [`Lua::scope`] for more details.
|
||||
///
|
||||
/// Userdata created with this method will not be able to be mutated from Lua.
|
||||
pub fn create_any_userdata_ref<T>(&self, data: &'scope T) -> Result<AnyUserData<'lua>>
|
||||
where
|
||||
T: 'static,
|
||||
{
|
||||
unsafe {
|
||||
let ud = self.lua.make_any_userdata(UserDataCell::new_ref(data))?;
|
||||
self.seal_userdata::<T>(&ud)?;
|
||||
Ok(ud)
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a Lua userdata object from a mutable reference to custom Rust type.
|
||||
///
|
||||
/// This is a version of [`Lua::create_any_userdata`] that creates a userdata which expires on
|
||||
/// scope drop, and does not require that the Rust type be Send. This method takes non-'static
|
||||
/// mutable reference to the data. See [`Lua::scope`] for more details.
|
||||
pub fn create_any_userdata_ref_mut<T>(&self, data: &'scope mut T) -> Result<AnyUserData<'lua>>
|
||||
where
|
||||
T: 'static,
|
||||
{
|
||||
let lua = self.lua;
|
||||
unsafe {
|
||||
let ud = lua.make_any_userdata(UserDataCell::new_ref_mut(data))?;
|
||||
self.seal_userdata::<T>(&ud)?;
|
||||
Ok(ud)
|
||||
}
|
||||
}
|
||||
|
||||
/// Shortens the lifetime of a userdata to the lifetime of the scope.
|
||||
unsafe fn seal_userdata<T: 'static>(&self, ud: &AnyUserData<'lua>) -> Result<()> {
|
||||
#[cfg(any(feature = "lua51", feature = "luajit"))]
|
||||
let newtable = self.lua.create_table()?;
|
||||
let destructor: DestructorCallback = Box::new(move |ud| {
|
||||
let state = ud.lua.state();
|
||||
let _sg = StackGuard::new(state);
|
||||
assert_stack(state, 2);
|
||||
|
||||
// Check that userdata is not destructed (via `take()` call)
|
||||
if ud.lua.push_userdata_ref(&ud).is_err() {
|
||||
return vec![];
|
||||
}
|
||||
|
||||
// Clear associated user values
|
||||
#[cfg(feature = "lua54")]
|
||||
for i in 1..=USER_VALUE_MAXSLOT {
|
||||
ffi::lua_pushnil(state);
|
||||
ffi::lua_setiuservalue(state, -2, i as c_int);
|
||||
}
|
||||
#[cfg(any(feature = "lua53", feature = "lua52", feature = "luau"))]
|
||||
{
|
||||
ffi::lua_pushnil(state);
|
||||
ffi::lua_setuservalue(state, -2);
|
||||
}
|
||||
#[cfg(any(feature = "lua51", feature = "luajit"))]
|
||||
{
|
||||
ud.lua.push_ref(&newtable.0);
|
||||
ffi::lua_setuservalue(state, -2);
|
||||
}
|
||||
|
||||
vec![Box::new(take_userdata::<UserDataCell<T>>(state))]
|
||||
});
|
||||
self.destructors
|
||||
.borrow_mut()
|
||||
.push((ud.0.clone(), destructor));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Creates a Lua userdata object from a custom userdata type.
|
||||
///
|
||||
/// This is a version of [`Lua::create_userdata`] that creates a userdata which expires on
|
||||
/// scope drop, and does not require that the userdata type be Send or 'static. See
|
||||
@@ -251,10 +283,8 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
|
||||
/// [`UserDataMethods`]: crate::UserDataMethods
|
||||
pub fn create_nonstatic_userdata<T>(&self, data: T) -> Result<AnyUserData<'lua>>
|
||||
where
|
||||
T: 'scope + UserData,
|
||||
T: UserData + 'scope,
|
||||
{
|
||||
let data = Rc::new(RefCell::new(data));
|
||||
|
||||
// 'callback outliving 'scope is a lie to make the types work out, required due to the
|
||||
// inability to work with the more correct callback type that is universally quantified over
|
||||
// 'lua. This is safe though, because `UserData::add_methods` does not get to pick the 'lua
|
||||
@@ -262,8 +292,7 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
|
||||
// parameters.
|
||||
fn wrap_method<'scope, 'lua, 'callback: 'scope, T: 'scope>(
|
||||
scope: &Scope<'lua, 'scope>,
|
||||
data: Rc<RefCell<T>>,
|
||||
ud_ptr: *const c_void,
|
||||
ud_ptr: *const UserDataCell<T>,
|
||||
method: NonStaticMethod<'callback, T>,
|
||||
) -> Result<Function<'lua>> {
|
||||
// On methods that actually receive the userdata, we fake a type check on the passed in
|
||||
@@ -273,14 +302,15 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
|
||||
// with a type mismatch, but here without this check would proceed as though you had
|
||||
// called the method on the original value (since we otherwise completely ignore the
|
||||
// first argument).
|
||||
let check_ud_type = move |lua: &'callback Lua, value| {
|
||||
let check_ud_type = move |lua: &Lua, value| -> Result<&UserDataCell<T>> {
|
||||
if let Some(Value::UserData(ud)) = value {
|
||||
let state = lua.state();
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(lua.state);
|
||||
check_stack(lua.state, 2)?;
|
||||
let _sg = StackGuard::new(state);
|
||||
check_stack(state, 2)?;
|
||||
lua.push_userdata_ref(&ud.0)?;
|
||||
if get_userdata(lua.state, -1) as *const _ == ud_ptr {
|
||||
return Ok(());
|
||||
if get_userdata(state, -1) as *const _ == ud_ptr {
|
||||
return Ok(&*ud_ptr);
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -290,8 +320,8 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
|
||||
match method {
|
||||
NonStaticMethod::Method(method) => {
|
||||
let f = Box::new(move |lua, mut args: MultiValue<'callback>| {
|
||||
check_ud_type(lua, args.pop_front())?;
|
||||
let data = data.try_borrow().map_err(|_| Error::UserDataBorrowError)?;
|
||||
let data = check_ud_type(lua, args.pop_front())?;
|
||||
let data = data.try_borrow()?;
|
||||
method(lua, &*data, args)
|
||||
});
|
||||
unsafe { scope.create_callback(f) }
|
||||
@@ -299,13 +329,11 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
|
||||
NonStaticMethod::MethodMut(method) => {
|
||||
let method = RefCell::new(method);
|
||||
let f = Box::new(move |lua, mut args: MultiValue<'callback>| {
|
||||
check_ud_type(lua, args.pop_front())?;
|
||||
let data = check_ud_type(lua, args.pop_front())?;
|
||||
let mut method = method
|
||||
.try_borrow_mut()
|
||||
.map_err(|_| Error::RecursiveMutCallback)?;
|
||||
let mut data = data
|
||||
.try_borrow_mut()
|
||||
.map_err(|_| Error::UserDataBorrowMutError)?;
|
||||
let mut data = data.try_borrow_mut()?;
|
||||
(*method)(lua, &mut *data, args)
|
||||
});
|
||||
unsafe { scope.create_callback(f) }
|
||||
@@ -330,16 +358,16 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
|
||||
T::add_fields(&mut ud_fields);
|
||||
T::add_methods(&mut ud_methods);
|
||||
|
||||
let lua = self.lua;
|
||||
let state = lua.state();
|
||||
unsafe {
|
||||
let lua = self.lua;
|
||||
let _sg = StackGuard::new(lua.state);
|
||||
check_stack(lua.state, 13)?;
|
||||
let _sg = StackGuard::new(state);
|
||||
check_stack(state, 13)?;
|
||||
|
||||
#[cfg(not(feature = "luau"))]
|
||||
#[allow(clippy::let_and_return)]
|
||||
let ud_ptr = protect_lua!(lua.state, 0, 1, |state| {
|
||||
let ud =
|
||||
ffi::lua_newuserdata(state, mem::size_of::<UserDataCell<Rc<RefCell<T>>>>());
|
||||
let ud_ptr = protect_lua!(state, 0, 1, |state| {
|
||||
let ud = ffi::lua_newuserdata(state, mem::size_of::<UserDataCell<T>>());
|
||||
|
||||
// Set empty environment for Lua 5.1
|
||||
#[cfg(any(feature = "lua51", feature = "luajit"))]
|
||||
@@ -348,72 +376,64 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
|
||||
ffi::lua_setuservalue(state, -2);
|
||||
}
|
||||
|
||||
ud
|
||||
ud as *const UserDataCell<T>
|
||||
})?;
|
||||
#[cfg(feature = "luau")]
|
||||
let ud_ptr = {
|
||||
crate::util::push_userdata::<UserDataCell<Rc<RefCell<T>>>>(
|
||||
lua.state,
|
||||
UserDataCell::new(data.clone()),
|
||||
true,
|
||||
)?;
|
||||
ffi::lua_touserdata(lua.state, -1)
|
||||
crate::util::push_userdata(state, UserDataCell::new(data), true)?;
|
||||
ffi::lua_touserdata(state, -1) as *const UserDataCell<T>
|
||||
};
|
||||
|
||||
// Prepare metatable, add meta methods first and then meta fields
|
||||
let meta_methods_nrec = ud_methods.meta_methods.len() + ud_fields.meta_fields.len() + 1;
|
||||
push_table(lua.state, 0, meta_methods_nrec as c_int, true)?;
|
||||
push_table(state, 0, meta_methods_nrec as c_int, true)?;
|
||||
|
||||
for (k, m) in ud_methods.meta_methods {
|
||||
let data = data.clone();
|
||||
lua.push_value(Value::Function(wrap_method(self, data, ud_ptr, m)?))?;
|
||||
rawset_field(lua.state, -2, k.validate()?.name())?;
|
||||
lua.push_value(Value::Function(wrap_method(self, ud_ptr, m)?))?;
|
||||
rawset_field(state, -2, MetaMethod::validate(&k)?)?;
|
||||
}
|
||||
for (k, f) in ud_fields.meta_fields {
|
||||
lua.push_value(f(mem::transmute(lua))?)?;
|
||||
rawset_field(lua.state, -2, k.validate()?.name())?;
|
||||
rawset_field(state, -2, MetaMethod::validate(&k)?)?;
|
||||
}
|
||||
let metatable_index = ffi::lua_absindex(lua.state, -1);
|
||||
let metatable_index = ffi::lua_absindex(state, -1);
|
||||
|
||||
let mut field_getters_index = None;
|
||||
let field_getters_nrec = ud_fields.field_getters.len();
|
||||
if field_getters_nrec > 0 {
|
||||
push_table(lua.state, 0, field_getters_nrec as c_int, true)?;
|
||||
push_table(state, 0, field_getters_nrec as c_int, true)?;
|
||||
for (k, m) in ud_fields.field_getters {
|
||||
let data = data.clone();
|
||||
lua.push_value(Value::Function(wrap_method(self, data, ud_ptr, m)?))?;
|
||||
rawset_field(lua.state, -2, &k)?;
|
||||
lua.push_value(Value::Function(wrap_method(self, ud_ptr, m)?))?;
|
||||
rawset_field(state, -2, &k)?;
|
||||
}
|
||||
field_getters_index = Some(ffi::lua_absindex(lua.state, -1));
|
||||
field_getters_index = Some(ffi::lua_absindex(state, -1));
|
||||
}
|
||||
|
||||
let mut field_setters_index = None;
|
||||
let field_setters_nrec = ud_fields.field_setters.len();
|
||||
if field_setters_nrec > 0 {
|
||||
push_table(lua.state, 0, field_setters_nrec as c_int, true)?;
|
||||
push_table(state, 0, field_setters_nrec as c_int, true)?;
|
||||
for (k, m) in ud_fields.field_setters {
|
||||
let data = data.clone();
|
||||
lua.push_value(Value::Function(wrap_method(self, data, ud_ptr, m)?))?;
|
||||
rawset_field(lua.state, -2, &k)?;
|
||||
lua.push_value(Value::Function(wrap_method(self, ud_ptr, m)?))?;
|
||||
rawset_field(state, -2, &k)?;
|
||||
}
|
||||
field_setters_index = Some(ffi::lua_absindex(lua.state, -1));
|
||||
field_setters_index = Some(ffi::lua_absindex(state, -1));
|
||||
}
|
||||
|
||||
let mut methods_index = None;
|
||||
let methods_nrec = ud_methods.methods.len();
|
||||
if methods_nrec > 0 {
|
||||
// Create table used for methods lookup
|
||||
push_table(lua.state, 0, methods_nrec as c_int, true)?;
|
||||
push_table(state, 0, methods_nrec as c_int, true)?;
|
||||
for (k, m) in ud_methods.methods {
|
||||
let data = data.clone();
|
||||
lua.push_value(Value::Function(wrap_method(self, data, ud_ptr, m)?))?;
|
||||
rawset_field(lua.state, -2, &k)?;
|
||||
lua.push_value(Value::Function(wrap_method(self, ud_ptr, m)?))?;
|
||||
rawset_field(state, -2, &k)?;
|
||||
}
|
||||
methods_index = Some(ffi::lua_absindex(lua.state, -1));
|
||||
methods_index = Some(ffi::lua_absindex(state, -1));
|
||||
}
|
||||
|
||||
init_userdata_metatable::<UserDataCell<Rc<RefCell<T>>>>(
|
||||
lua.state,
|
||||
init_userdata_metatable::<UserDataCell<T>>(
|
||||
state,
|
||||
metatable_index,
|
||||
field_getters_index,
|
||||
field_setters_index,
|
||||
@@ -423,20 +443,20 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
|
||||
let count = field_getters_index.map(|_| 1).unwrap_or(0)
|
||||
+ field_setters_index.map(|_| 1).unwrap_or(0)
|
||||
+ methods_index.map(|_| 1).unwrap_or(0);
|
||||
ffi::lua_pop(lua.state, count);
|
||||
ffi::lua_pop(state, count);
|
||||
|
||||
let mt_ptr = ffi::lua_topointer(lua.state, -1);
|
||||
let mt_ptr = ffi::lua_topointer(state, -1);
|
||||
// Write userdata just before attaching metatable with `__gc` metamethod
|
||||
#[cfg(not(feature = "luau"))]
|
||||
std::ptr::write(ud_ptr as _, UserDataCell::new(data));
|
||||
ffi::lua_setmetatable(lua.state, -2);
|
||||
ffi::lua_setmetatable(state, -2);
|
||||
let ud = AnyUserData(lua.pop_ref());
|
||||
lua.register_userdata_metatable(mt_ptr, None);
|
||||
lua.register_raw_userdata_metatable(mt_ptr, None);
|
||||
|
||||
#[cfg(any(feature = "lua51", feature = "luajit"))]
|
||||
let newtable = lua.create_table()?;
|
||||
let destructor: DestructorCallback = Box::new(move |ud| {
|
||||
let state = ud.lua.state;
|
||||
let state = ud.lua.state();
|
||||
let _sg = StackGuard::new(state);
|
||||
assert_stack(state, 2);
|
||||
|
||||
@@ -449,7 +469,7 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
|
||||
ffi::lua_getmetatable(state, -1);
|
||||
let mt_ptr = ffi::lua_topointer(state, -1);
|
||||
ffi::lua_pop(state, 1);
|
||||
ud.lua.deregister_userdata_metatable(mt_ptr);
|
||||
ud.lua.deregister_raw_userdata_metatable(mt_ptr);
|
||||
|
||||
// Clear associated user values
|
||||
#[cfg(feature = "lua54")]
|
||||
@@ -474,8 +494,8 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
|
||||
mem::transmute(f)
|
||||
}
|
||||
|
||||
let ud = Box::new(seal(take_userdata::<UserDataCell<Rc<RefCell<T>>>>(state)));
|
||||
vec![ud]
|
||||
let ud = take_userdata::<UserDataCell<T>>(state);
|
||||
vec![Box::new(seal(ud))]
|
||||
});
|
||||
self.destructors
|
||||
.borrow_mut()
|
||||
@@ -498,7 +518,7 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
|
||||
let f = self.lua.create_callback(f)?;
|
||||
|
||||
let destructor: DestructorCallback = Box::new(|f| {
|
||||
let state = f.lua.state;
|
||||
let state = f.lua.state();
|
||||
let _sg = StackGuard::new(state);
|
||||
assert_stack(state, 3);
|
||||
|
||||
@@ -519,64 +539,6 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
|
||||
|
||||
Ok(f)
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
unsafe fn create_async_callback<'callback>(
|
||||
&self,
|
||||
f: AsyncCallback<'callback, 'scope>,
|
||||
) -> Result<Function<'lua>> {
|
||||
let f = mem::transmute::<AsyncCallback<'callback, 'scope>, AsyncCallback<'lua, 'static>>(f);
|
||||
let f = self.lua.create_async_callback(f)?;
|
||||
|
||||
// We need to pre-allocate strings to avoid failures in destructor.
|
||||
let get_poll_str = self.lua.create_string("get_poll")?;
|
||||
let poll_str = self.lua.create_string("poll")?;
|
||||
let destructor: DestructorCallback = Box::new(move |f| {
|
||||
let state = f.lua.state;
|
||||
let _sg = StackGuard::new(state);
|
||||
assert_stack(state, 5);
|
||||
|
||||
f.lua.push_ref(&f);
|
||||
|
||||
// We know the destructor has not run yet because we hold a reference to the callback.
|
||||
|
||||
// First, get the environment table
|
||||
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
|
||||
ffi::lua_getupvalue(state, -1, 1);
|
||||
#[cfg(any(feature = "lua51", feature = "luajit", feature = "luau"))]
|
||||
ffi::lua_getfenv(state, -1);
|
||||
|
||||
// Second, get the `get_poll()` closure using the corresponding key
|
||||
f.lua.push_ref(&get_poll_str.0);
|
||||
ffi::lua_rawget(state, -2);
|
||||
|
||||
// Destroy all upvalues
|
||||
ffi::lua_getupvalue(state, -1, 1);
|
||||
let upvalue1 = take_userdata::<AsyncCallbackUpvalue>(state);
|
||||
ffi::lua_pushnil(state);
|
||||
ffi::lua_setupvalue(state, -2, 1);
|
||||
|
||||
ffi::lua_pop(state, 1);
|
||||
let mut data: Vec<Box<dyn Any>> = vec![Box::new(upvalue1)];
|
||||
|
||||
// Finally, get polled future and destroy it
|
||||
f.lua.push_ref(&poll_str.0);
|
||||
if ffi::lua_rawget(state, -2) == ffi::LUA_TFUNCTION {
|
||||
ffi::lua_getupvalue(state, -1, 1);
|
||||
let upvalue2 = take_userdata::<AsyncPollUpvalue>(state);
|
||||
ffi::lua_pushnil(state);
|
||||
ffi::lua_setupvalue(state, -2, 1);
|
||||
data.push(Box::new(upvalue2));
|
||||
}
|
||||
|
||||
data
|
||||
});
|
||||
self.destructors
|
||||
.borrow_mut()
|
||||
.push((f.0.clone(), destructor));
|
||||
|
||||
Ok(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua, 'scope> Drop for Scope<'lua, 'scope> {
|
||||
@@ -606,8 +568,8 @@ enum NonStaticMethod<'lua, T> {
|
||||
}
|
||||
|
||||
struct NonStaticUserDataMethods<'lua, T: UserData> {
|
||||
methods: Vec<(Vec<u8>, NonStaticMethod<'lua, T>)>,
|
||||
meta_methods: Vec<(MetaMethod, NonStaticMethod<'lua, T>)>,
|
||||
methods: Vec<(String, NonStaticMethod<'lua, T>)>,
|
||||
meta_methods: Vec<(String, NonStaticMethod<'lua, T>)>,
|
||||
}
|
||||
|
||||
impl<'lua, T: UserData> Default for NonStaticUserDataMethods<'lua, T> {
|
||||
@@ -620,190 +582,162 @@ impl<'lua, T: UserData> Default for NonStaticUserDataMethods<'lua, T> {
|
||||
}
|
||||
|
||||
impl<'lua, T: UserData> UserDataMethods<'lua, T> for NonStaticUserDataMethods<'lua, T> {
|
||||
fn add_method<S, A, R, M>(&mut self, name: &S, method: M)
|
||||
fn add_method<M, A, R>(&mut self, name: impl AsRef<str>, method: M)
|
||||
where
|
||||
S: AsRef<[u8]> + ?Sized,
|
||||
M: Fn(&'lua Lua, &T, A) -> Result<R> + MaybeSend + 'static,
|
||||
A: FromLuaMulti<'lua>,
|
||||
R: ToLuaMulti<'lua>,
|
||||
M: 'static + MaybeSend + Fn(&'lua Lua, &T, A) -> Result<R>,
|
||||
R: IntoLuaMulti<'lua>,
|
||||
{
|
||||
self.methods.push((
|
||||
name.as_ref().to_vec(),
|
||||
NonStaticMethod::Method(Box::new(move |lua, ud, args| {
|
||||
method(lua, ud, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
|
||||
})),
|
||||
));
|
||||
let method = NonStaticMethod::Method(Box::new(move |lua, ud, args| {
|
||||
method(lua, ud, A::from_lua_multi(args, lua)?)?.into_lua_multi(lua)
|
||||
}));
|
||||
self.methods.push((name.as_ref().into(), method));
|
||||
}
|
||||
|
||||
fn add_method_mut<S, A, R, M>(&mut self, name: &S, mut method: M)
|
||||
fn add_method_mut<M, A, R>(&mut self, name: impl AsRef<str>, mut method: M)
|
||||
where
|
||||
S: AsRef<[u8]> + ?Sized,
|
||||
M: FnMut(&'lua Lua, &mut T, A) -> Result<R> + MaybeSend + 'static,
|
||||
A: FromLuaMulti<'lua>,
|
||||
R: ToLuaMulti<'lua>,
|
||||
M: 'static + MaybeSend + FnMut(&'lua Lua, &mut T, A) -> Result<R>,
|
||||
R: IntoLuaMulti<'lua>,
|
||||
{
|
||||
self.methods.push((
|
||||
name.as_ref().to_vec(),
|
||||
NonStaticMethod::MethodMut(Box::new(move |lua, ud, args| {
|
||||
method(lua, ud, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
|
||||
})),
|
||||
));
|
||||
let method = NonStaticMethod::MethodMut(Box::new(move |lua, ud, args| {
|
||||
method(lua, ud, A::from_lua_multi(args, lua)?)?.into_lua_multi(lua)
|
||||
}));
|
||||
self.methods.push((name.as_ref().into(), method));
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
fn add_async_method<S, A, R, M, MR>(&mut self, _name: &S, _method: M)
|
||||
fn add_async_method<M, A, MR, R>(&mut self, _name: impl AsRef<str>, _method: M)
|
||||
where
|
||||
T: Clone,
|
||||
S: AsRef<[u8]> + ?Sized,
|
||||
M: Fn(&'lua Lua, T, A) -> MR + MaybeSend + 'static,
|
||||
A: FromLuaMulti<'lua>,
|
||||
R: ToLuaMulti<'lua>,
|
||||
M: 'static + MaybeSend + Fn(&'lua Lua, T, A) -> MR,
|
||||
MR: 'lua + Future<Output = Result<R>>,
|
||||
MR: Future<Output = Result<R>> + 'lua,
|
||||
R: IntoLuaMulti<'lua>,
|
||||
{
|
||||
// The panic should never happen as async non-static code wouldn't compile
|
||||
// Non-static lifetime must be bounded to 'lua lifetime
|
||||
mlua_panic!("asynchronous methods are not supported for non-static userdata")
|
||||
panic!("asynchronous methods are not supported for non-static userdata")
|
||||
}
|
||||
|
||||
fn add_function<S, A, R, F>(&mut self, name: &S, function: F)
|
||||
fn add_function<F, A, R>(&mut self, name: impl AsRef<str>, function: F)
|
||||
where
|
||||
S: AsRef<[u8]> + ?Sized,
|
||||
F: Fn(&'lua Lua, A) -> Result<R> + MaybeSend + 'static,
|
||||
A: FromLuaMulti<'lua>,
|
||||
R: ToLuaMulti<'lua>,
|
||||
F: 'static + MaybeSend + Fn(&'lua Lua, A) -> Result<R>,
|
||||
R: IntoLuaMulti<'lua>,
|
||||
{
|
||||
self.methods.push((
|
||||
name.as_ref().to_vec(),
|
||||
NonStaticMethod::Function(Box::new(move |lua, args| {
|
||||
function(lua, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
|
||||
})),
|
||||
));
|
||||
let func = NonStaticMethod::Function(Box::new(move |lua, args| {
|
||||
function(lua, A::from_lua_multi(args, lua)?)?.into_lua_multi(lua)
|
||||
}));
|
||||
self.methods.push((name.as_ref().into(), func));
|
||||
}
|
||||
|
||||
fn add_function_mut<S, A, R, F>(&mut self, name: &S, mut function: F)
|
||||
fn add_function_mut<F, A, R>(&mut self, name: impl AsRef<str>, mut function: F)
|
||||
where
|
||||
S: AsRef<[u8]> + ?Sized,
|
||||
F: FnMut(&'lua Lua, A) -> Result<R> + MaybeSend + 'static,
|
||||
A: FromLuaMulti<'lua>,
|
||||
R: ToLuaMulti<'lua>,
|
||||
F: 'static + MaybeSend + FnMut(&'lua Lua, A) -> Result<R>,
|
||||
R: IntoLuaMulti<'lua>,
|
||||
{
|
||||
self.methods.push((
|
||||
name.as_ref().to_vec(),
|
||||
NonStaticMethod::FunctionMut(Box::new(move |lua, args| {
|
||||
function(lua, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
|
||||
})),
|
||||
));
|
||||
let func = NonStaticMethod::FunctionMut(Box::new(move |lua, args| {
|
||||
function(lua, A::from_lua_multi(args, lua)?)?.into_lua_multi(lua)
|
||||
}));
|
||||
self.methods.push((name.as_ref().into(), func));
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
fn add_async_function<S, A, R, F, FR>(&mut self, _name: &S, _function: F)
|
||||
fn add_async_function<F, A, FR, R>(&mut self, _name: impl AsRef<str>, _function: F)
|
||||
where
|
||||
S: AsRef<[u8]> + ?Sized,
|
||||
F: Fn(&'lua Lua, A) -> FR + MaybeSend + 'static,
|
||||
A: FromLuaMulti<'lua>,
|
||||
R: ToLuaMulti<'lua>,
|
||||
F: 'static + MaybeSend + Fn(&'lua Lua, A) -> FR,
|
||||
FR: 'lua + Future<Output = Result<R>>,
|
||||
FR: Future<Output = Result<R>> + 'lua,
|
||||
R: IntoLuaMulti<'lua>,
|
||||
{
|
||||
// The panic should never happen as async non-static code wouldn't compile
|
||||
// Non-static lifetime must be bounded to 'lua lifetime
|
||||
mlua_panic!("asynchronous functions are not supported for non-static userdata")
|
||||
panic!("asynchronous functions are not supported for non-static userdata")
|
||||
}
|
||||
|
||||
fn add_meta_method<S, A, R, M>(&mut self, meta: S, method: M)
|
||||
fn add_meta_method<M, A, R>(&mut self, name: impl AsRef<str>, method: M)
|
||||
where
|
||||
S: Into<MetaMethod>,
|
||||
M: Fn(&'lua Lua, &T, A) -> Result<R> + MaybeSend + 'static,
|
||||
A: FromLuaMulti<'lua>,
|
||||
R: ToLuaMulti<'lua>,
|
||||
M: 'static + MaybeSend + Fn(&'lua Lua, &T, A) -> Result<R>,
|
||||
R: IntoLuaMulti<'lua>,
|
||||
{
|
||||
self.meta_methods.push((
|
||||
meta.into(),
|
||||
NonStaticMethod::Method(Box::new(move |lua, ud, args| {
|
||||
method(lua, ud, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
|
||||
})),
|
||||
));
|
||||
let method = NonStaticMethod::Method(Box::new(move |lua, ud, args| {
|
||||
method(lua, ud, A::from_lua_multi(args, lua)?)?.into_lua_multi(lua)
|
||||
}));
|
||||
self.meta_methods.push((name.as_ref().into(), method));
|
||||
}
|
||||
|
||||
fn add_meta_method_mut<S, A, R, M>(&mut self, meta: S, mut method: M)
|
||||
fn add_meta_method_mut<M, A, R>(&mut self, name: impl AsRef<str>, mut method: M)
|
||||
where
|
||||
S: Into<MetaMethod>,
|
||||
M: FnMut(&'lua Lua, &mut T, A) -> Result<R> + MaybeSend + 'static,
|
||||
A: FromLuaMulti<'lua>,
|
||||
R: ToLuaMulti<'lua>,
|
||||
M: 'static + MaybeSend + FnMut(&'lua Lua, &mut T, A) -> Result<R>,
|
||||
R: IntoLuaMulti<'lua>,
|
||||
{
|
||||
self.meta_methods.push((
|
||||
meta.into(),
|
||||
NonStaticMethod::MethodMut(Box::new(move |lua, ud, args| {
|
||||
method(lua, ud, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
|
||||
})),
|
||||
));
|
||||
let method = NonStaticMethod::MethodMut(Box::new(move |lua, ud, args| {
|
||||
method(lua, ud, A::from_lua_multi(args, lua)?)?.into_lua_multi(lua)
|
||||
}));
|
||||
self.meta_methods.push((name.as_ref().into(), method));
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "async", not(any(feature = "lua51", feature = "luau"))))]
|
||||
fn add_async_meta_method<S, A, R, M, MR>(&mut self, _meta: S, _method: M)
|
||||
fn add_async_meta_method<M, A, MR, R>(&mut self, _name: impl AsRef<str>, _method: M)
|
||||
where
|
||||
T: Clone,
|
||||
S: Into<MetaMethod>,
|
||||
M: Fn(&'lua Lua, T, A) -> MR + MaybeSend + 'static,
|
||||
A: FromLuaMulti<'lua>,
|
||||
R: ToLuaMulti<'lua>,
|
||||
M: 'static + MaybeSend + Fn(&'lua Lua, T, A) -> MR,
|
||||
MR: 'lua + Future<Output = Result<R>>,
|
||||
MR: Future<Output = Result<R>> + 'lua,
|
||||
R: IntoLuaMulti<'lua>,
|
||||
{
|
||||
// The panic should never happen as async non-static code wouldn't compile
|
||||
// Non-static lifetime must be bounded to 'lua lifetime
|
||||
mlua_panic!("asynchronous meta methods are not supported for non-static userdata")
|
||||
panic!("asynchronous meta methods are not supported for non-static userdata")
|
||||
}
|
||||
|
||||
fn add_meta_function<S, A, R, F>(&mut self, meta: S, function: F)
|
||||
fn add_meta_function<F, A, R>(&mut self, name: impl AsRef<str>, function: F)
|
||||
where
|
||||
S: Into<MetaMethod>,
|
||||
F: Fn(&'lua Lua, A) -> Result<R> + MaybeSend + 'static,
|
||||
A: FromLuaMulti<'lua>,
|
||||
R: ToLuaMulti<'lua>,
|
||||
F: 'static + MaybeSend + Fn(&'lua Lua, A) -> Result<R>,
|
||||
R: IntoLuaMulti<'lua>,
|
||||
{
|
||||
self.meta_methods.push((
|
||||
meta.into(),
|
||||
NonStaticMethod::Function(Box::new(move |lua, args| {
|
||||
function(lua, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
|
||||
})),
|
||||
));
|
||||
let func = NonStaticMethod::Function(Box::new(move |lua, args| {
|
||||
function(lua, A::from_lua_multi(args, lua)?)?.into_lua_multi(lua)
|
||||
}));
|
||||
self.meta_methods.push((name.as_ref().into(), func));
|
||||
}
|
||||
|
||||
fn add_meta_function_mut<S, A, R, F>(&mut self, meta: S, mut function: F)
|
||||
fn add_meta_function_mut<F, A, R>(&mut self, name: impl AsRef<str>, mut function: F)
|
||||
where
|
||||
S: Into<MetaMethod>,
|
||||
F: FnMut(&'lua Lua, A) -> Result<R> + MaybeSend + 'static,
|
||||
A: FromLuaMulti<'lua>,
|
||||
R: ToLuaMulti<'lua>,
|
||||
F: 'static + MaybeSend + FnMut(&'lua Lua, A) -> Result<R>,
|
||||
R: IntoLuaMulti<'lua>,
|
||||
{
|
||||
self.meta_methods.push((
|
||||
meta.into(),
|
||||
NonStaticMethod::FunctionMut(Box::new(move |lua, args| {
|
||||
function(lua, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
|
||||
})),
|
||||
));
|
||||
let func = NonStaticMethod::FunctionMut(Box::new(move |lua, args| {
|
||||
function(lua, A::from_lua_multi(args, lua)?)?.into_lua_multi(lua)
|
||||
}));
|
||||
self.meta_methods.push((name.as_ref().into(), func));
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "async", not(any(feature = "lua51", feature = "luau"))))]
|
||||
fn add_async_meta_function<S, A, R, F, FR>(&mut self, _meta: S, _function: F)
|
||||
fn add_async_meta_function<F, A, FR, R>(&mut self, _name: impl AsRef<str>, _function: F)
|
||||
where
|
||||
S: Into<MetaMethod>,
|
||||
F: Fn(&'lua Lua, A) -> FR + MaybeSend + 'static,
|
||||
A: FromLuaMulti<'lua>,
|
||||
R: ToLuaMulti<'lua>,
|
||||
F: 'static + MaybeSend + Fn(&'lua Lua, A) -> FR,
|
||||
FR: 'lua + Future<Output = Result<R>>,
|
||||
FR: Future<Output = Result<R>> + 'lua,
|
||||
R: IntoLuaMulti<'lua>,
|
||||
{
|
||||
// The panic should never happen as async non-static code wouldn't compile
|
||||
// Non-static lifetime must be bounded to 'lua lifetime
|
||||
mlua_panic!("asynchronous meta functions are not supported for non-static userdata")
|
||||
panic!("asynchronous meta functions are not supported for non-static userdata")
|
||||
}
|
||||
}
|
||||
|
||||
struct NonStaticUserDataFields<'lua, T: UserData> {
|
||||
field_getters: Vec<(Vec<u8>, NonStaticMethod<'lua, T>)>,
|
||||
field_setters: Vec<(Vec<u8>, NonStaticMethod<'lua, T>)>,
|
||||
field_getters: Vec<(String, NonStaticMethod<'lua, T>)>,
|
||||
field_setters: Vec<(String, NonStaticMethod<'lua, T>)>,
|
||||
#[allow(clippy::type_complexity)]
|
||||
meta_fields: Vec<(MetaMethod, Box<dyn Fn(&'lua Lua) -> Result<Value<'lua>>>)>,
|
||||
meta_fields: Vec<(String, Box<dyn Fn(&'lua Lua) -> Result<Value<'lua>>>)>,
|
||||
}
|
||||
|
||||
impl<'lua, T: UserData> Default for NonStaticUserDataFields<'lua, T> {
|
||||
@@ -817,80 +751,67 @@ impl<'lua, T: UserData> Default for NonStaticUserDataFields<'lua, T> {
|
||||
}
|
||||
|
||||
impl<'lua, T: UserData> UserDataFields<'lua, T> for NonStaticUserDataFields<'lua, T> {
|
||||
fn add_field_method_get<S, R, M>(&mut self, name: &S, method: M)
|
||||
fn add_field_method_get<M, R>(&mut self, name: impl AsRef<str>, method: M)
|
||||
where
|
||||
S: AsRef<[u8]> + ?Sized,
|
||||
R: ToLua<'lua>,
|
||||
M: 'static + MaybeSend + Fn(&'lua Lua, &T) -> Result<R>,
|
||||
M: Fn(&'lua Lua, &T) -> Result<R> + MaybeSend + 'static,
|
||||
R: IntoLua<'lua>,
|
||||
{
|
||||
self.field_getters.push((
|
||||
name.as_ref().to_vec(),
|
||||
NonStaticMethod::Method(Box::new(move |lua, ud, _| {
|
||||
method(lua, ud)?.to_lua_multi(lua)
|
||||
})),
|
||||
));
|
||||
let method = NonStaticMethod::Method(Box::new(move |lua, ud, _| {
|
||||
method(lua, ud)?.into_lua_multi(lua)
|
||||
}));
|
||||
self.field_getters.push((name.as_ref().into(), method));
|
||||
}
|
||||
|
||||
fn add_field_method_set<S, A, M>(&mut self, name: &S, mut method: M)
|
||||
fn add_field_method_set<M, A>(&mut self, name: impl AsRef<str>, mut method: M)
|
||||
where
|
||||
S: AsRef<[u8]> + ?Sized,
|
||||
M: FnMut(&'lua Lua, &mut T, A) -> Result<()> + MaybeSend + 'static,
|
||||
A: FromLua<'lua>,
|
||||
M: 'static + MaybeSend + FnMut(&'lua Lua, &mut T, A) -> Result<()>,
|
||||
{
|
||||
self.field_setters.push((
|
||||
name.as_ref().to_vec(),
|
||||
NonStaticMethod::MethodMut(Box::new(move |lua, ud, args| {
|
||||
method(lua, ud, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
|
||||
})),
|
||||
));
|
||||
let method = NonStaticMethod::MethodMut(Box::new(move |lua, ud, args| {
|
||||
method(lua, ud, A::from_lua_multi(args, lua)?)?.into_lua_multi(lua)
|
||||
}));
|
||||
self.field_setters.push((name.as_ref().into(), method));
|
||||
}
|
||||
|
||||
fn add_field_function_get<S, R, F>(&mut self, name: &S, function: F)
|
||||
fn add_field_function_get<F, R>(&mut self, name: impl AsRef<str>, function: F)
|
||||
where
|
||||
S: AsRef<[u8]> + ?Sized,
|
||||
R: ToLua<'lua>,
|
||||
F: 'static + MaybeSend + Fn(&'lua Lua, AnyUserData<'lua>) -> Result<R>,
|
||||
F: Fn(&'lua Lua, AnyUserData<'lua>) -> Result<R> + MaybeSend + 'static,
|
||||
R: IntoLua<'lua>,
|
||||
{
|
||||
self.field_getters.push((
|
||||
name.as_ref().to_vec(),
|
||||
NonStaticMethod::Function(Box::new(move |lua, args| {
|
||||
function(lua, AnyUserData::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
|
||||
})),
|
||||
));
|
||||
let func = NonStaticMethod::Function(Box::new(move |lua, args| {
|
||||
function(lua, AnyUserData::from_lua_multi(args, lua)?)?.into_lua_multi(lua)
|
||||
}));
|
||||
self.field_getters.push((name.as_ref().into(), func));
|
||||
}
|
||||
|
||||
fn add_field_function_set<S, A, F>(&mut self, name: &S, mut function: F)
|
||||
fn add_field_function_set<F, A>(&mut self, name: impl AsRef<str>, mut function: F)
|
||||
where
|
||||
S: AsRef<[u8]> + ?Sized,
|
||||
F: FnMut(&'lua Lua, AnyUserData<'lua>, A) -> Result<()> + MaybeSend + 'static,
|
||||
A: FromLua<'lua>,
|
||||
F: 'static + MaybeSend + FnMut(&'lua Lua, AnyUserData<'lua>, A) -> Result<()>,
|
||||
{
|
||||
self.field_setters.push((
|
||||
name.as_ref().to_vec(),
|
||||
NonStaticMethod::FunctionMut(Box::new(move |lua, args| {
|
||||
let (ud, val) = <_>::from_lua_multi(args, lua)?;
|
||||
function(lua, ud, val)?.to_lua_multi(lua)
|
||||
})),
|
||||
));
|
||||
let func = NonStaticMethod::FunctionMut(Box::new(move |lua, args| {
|
||||
let (ud, val) = <_>::from_lua_multi(args, lua)?;
|
||||
function(lua, ud, val)?.into_lua_multi(lua)
|
||||
}));
|
||||
self.field_setters.push((name.as_ref().into(), func));
|
||||
}
|
||||
|
||||
fn add_meta_field_with<S, R, F>(&mut self, meta: S, f: F)
|
||||
fn add_meta_field_with<F, R>(&mut self, name: impl AsRef<str>, f: F)
|
||||
where
|
||||
S: Into<MetaMethod>,
|
||||
F: 'static + MaybeSend + Fn(&'lua Lua) -> Result<R>,
|
||||
R: ToLua<'lua>,
|
||||
F: Fn(&'lua Lua) -> Result<R> + MaybeSend + 'static,
|
||||
R: IntoLua<'lua>,
|
||||
{
|
||||
let meta = meta.into();
|
||||
let name = name.as_ref().to_string();
|
||||
self.meta_fields.push((
|
||||
meta.clone(),
|
||||
name.clone(),
|
||||
Box::new(move |lua| {
|
||||
let value = f(lua)?.to_lua(lua)?;
|
||||
if meta == MetaMethod::Index || meta == MetaMethod::NewIndex {
|
||||
let value = f(lua)?.into_lua(lua)?;
|
||||
if name == MetaMethod::Index || name == MetaMethod::NewIndex {
|
||||
match value {
|
||||
Value::Nil | Value::Table(_) | Value::Function(_) => {}
|
||||
_ => {
|
||||
return Err(Error::MetaMethodTypeError {
|
||||
method: meta.to_string(),
|
||||
method: name.clone(),
|
||||
type_name: value.type_name(),
|
||||
message: Some("expected nil, table or function".to_string()),
|
||||
})
|
||||
|
||||
+54
-5
@@ -9,6 +9,7 @@ use serde::de::{self, IntoDeserializer};
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
use crate::table::{Table, TablePairs, TableSequence};
|
||||
use crate::userdata::AnyUserData;
|
||||
use crate::value::Value;
|
||||
|
||||
/// A struct for deserializing Lua values into Rust values.
|
||||
@@ -131,6 +132,9 @@ impl<'lua, 'de> serde::Deserializer<'de> for Deserializer<'lua> {
|
||||
Value::Table(ref t) if t.raw_len() > 0 || t.is_array() => self.deserialize_seq(visitor),
|
||||
Value::Table(_) => self.deserialize_map(visitor),
|
||||
Value::LightUserData(ud) if ud.0.is_null() => visitor.visit_none(),
|
||||
Value::UserData(ud) if ud.is_serializable() => {
|
||||
serde_userdata(ud, |value| value.deserialize_any(visitor))
|
||||
}
|
||||
Value::Function(_)
|
||||
| Value::Thread(_)
|
||||
| Value::UserData(_)
|
||||
@@ -163,8 +167,8 @@ impl<'lua, 'de> serde::Deserializer<'de> for Deserializer<'lua> {
|
||||
#[inline]
|
||||
fn deserialize_enum<V>(
|
||||
self,
|
||||
_name: &str,
|
||||
_variants: &'static [&'static str],
|
||||
name: &'static str,
|
||||
variants: &'static [&'static str],
|
||||
visitor: V,
|
||||
) -> Result<V::Value>
|
||||
where
|
||||
@@ -198,6 +202,9 @@ impl<'lua, 'de> serde::Deserializer<'de> for Deserializer<'lua> {
|
||||
(variant, Some(value), Some(_guard))
|
||||
}
|
||||
Value::String(variant) => (variant.to_str()?.to_owned(), None, None),
|
||||
Value::UserData(ud) if ud.is_serializable() => {
|
||||
return serde_userdata(ud, |value| value.deserialize_enum(name, variants, visitor));
|
||||
}
|
||||
_ => return Err(de::Error::custom("bad enum value")),
|
||||
};
|
||||
|
||||
@@ -244,6 +251,9 @@ impl<'lua, 'de> serde::Deserializer<'de> for Deserializer<'lua> {
|
||||
))
|
||||
}
|
||||
}
|
||||
Value::UserData(ud) if ud.is_serializable() => {
|
||||
serde_userdata(ud, |value| value.deserialize_seq(visitor))
|
||||
}
|
||||
value => Err(de::Error::invalid_type(
|
||||
de::Unexpected::Other(value.type_name()),
|
||||
&"table",
|
||||
@@ -299,6 +309,9 @@ impl<'lua, 'de> serde::Deserializer<'de> for Deserializer<'lua> {
|
||||
))
|
||||
}
|
||||
}
|
||||
Value::UserData(ud) if ud.is_serializable() => {
|
||||
serde_userdata(ud, |value| value.deserialize_map(visitor))
|
||||
}
|
||||
value => Err(de::Error::invalid_type(
|
||||
de::Unexpected::Other(value.type_name()),
|
||||
&"table",
|
||||
@@ -320,16 +333,43 @@ impl<'lua, 'de> serde::Deserializer<'de> for Deserializer<'lua> {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn deserialize_newtype_struct<V>(self, _name: &'static str, visitor: V) -> Result<V::Value>
|
||||
fn deserialize_newtype_struct<V>(self, name: &'static str, visitor: V) -> Result<V::Value>
|
||||
where
|
||||
V: de::Visitor<'de>,
|
||||
{
|
||||
visitor.visit_newtype_struct(self)
|
||||
match self.value {
|
||||
Value::UserData(ud) if ud.is_serializable() => {
|
||||
serde_userdata(ud, |value| value.deserialize_newtype_struct(name, visitor))
|
||||
}
|
||||
_ => visitor.visit_newtype_struct(self),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn deserialize_unit<V>(self, visitor: V) -> Result<V::Value>
|
||||
where
|
||||
V: de::Visitor<'de>,
|
||||
{
|
||||
match self.value {
|
||||
Value::LightUserData(ud) if ud.0.is_null() => visitor.visit_unit(),
|
||||
_ => self.deserialize_any(visitor),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn deserialize_unit_struct<V>(self, _name: &'static str, visitor: V) -> Result<V::Value>
|
||||
where
|
||||
V: de::Visitor<'de>,
|
||||
{
|
||||
match self.value {
|
||||
Value::LightUserData(ud) if ud.0.is_null() => visitor.visit_unit(),
|
||||
_ => self.deserialize_any(visitor),
|
||||
}
|
||||
}
|
||||
|
||||
serde::forward_to_deserialize_any! {
|
||||
bool i8 i16 i32 i64 i128 u8 u16 u32 u64 u128 f32 f64 char str string bytes
|
||||
byte_buf unit unit_struct identifier ignored_any
|
||||
byte_buf identifier ignored_any
|
||||
}
|
||||
}
|
||||
|
||||
@@ -590,6 +630,7 @@ fn check_value_if_skip(
|
||||
return Ok(true); // skip
|
||||
}
|
||||
}
|
||||
Value::UserData(ud) if ud.is_serializable() => {}
|
||||
Value::Function(_)
|
||||
| Value::Thread(_)
|
||||
| Value::UserData(_)
|
||||
@@ -603,3 +644,11 @@ fn check_value_if_skip(
|
||||
}
|
||||
Ok(false) // do not skip
|
||||
}
|
||||
|
||||
fn serde_userdata<V>(
|
||||
ud: AnyUserData,
|
||||
f: impl FnOnce(serde_value::Value) -> std::result::Result<V, serde_value::DeserializerError>,
|
||||
) -> Result<V> {
|
||||
let value = serde_value::to_value(ud).map_err(|err| Error::SerializeError(err.to_string()))?;
|
||||
f(value).map_err(|err| Error::DeserializeError(err.to_string()))
|
||||
}
|
||||
|
||||
+2
-2
@@ -6,8 +6,8 @@ use std::ptr;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::error::Result;
|
||||
use crate::ffi;
|
||||
use crate::lua::Lua;
|
||||
use crate::private::Sealed;
|
||||
use crate::table::Table;
|
||||
use crate::types::LightUserData;
|
||||
use crate::util::check_stack;
|
||||
@@ -15,7 +15,7 @@ use crate::value::Value;
|
||||
|
||||
/// Trait for serializing/deserializing Lua values using Serde.
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "serialize")))]
|
||||
pub trait LuaSerdeExt<'lua> {
|
||||
pub trait LuaSerdeExt<'lua>: Sealed {
|
||||
/// A special value (lightuserdata) to encode/decode optional (none) values.
|
||||
///
|
||||
/// Requires `feature = "serialize"`
|
||||
|
||||
+9
-9
@@ -4,13 +4,12 @@ use serde::{ser, Serialize};
|
||||
|
||||
use super::LuaSerdeExt;
|
||||
use crate::error::{Error, Result};
|
||||
use crate::ffi;
|
||||
use crate::lua::Lua;
|
||||
use crate::string::String;
|
||||
use crate::table::Table;
|
||||
use crate::types::Integer;
|
||||
use crate::util::{check_stack, StackGuard};
|
||||
use crate::value::{ToLua, Value};
|
||||
use crate::value::{IntoLua, Value};
|
||||
|
||||
/// A struct for serializing Rust values into Lua values.
|
||||
#[derive(Debug)]
|
||||
@@ -110,7 +109,7 @@ macro_rules! lua_serialize_number {
|
||||
($name:ident, $t:ty) => {
|
||||
#[inline]
|
||||
fn $name(self, value: $t) -> Result<Value<'lua>> {
|
||||
value.to_lua(self.lua)
|
||||
value.into_lua(self.lua)
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -320,20 +319,21 @@ impl<'lua> ser::SerializeSeq for SerializeVec<'lua> {
|
||||
T: Serialize + ?Sized,
|
||||
{
|
||||
let lua = self.table.0.lua;
|
||||
let state = lua.state();
|
||||
let value = lua.to_value_with(value, self.options)?;
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(lua.state);
|
||||
check_stack(lua.state, 4)?;
|
||||
let _sg = StackGuard::new(state);
|
||||
check_stack(state, 4)?;
|
||||
|
||||
lua.push_ref(&self.table.0);
|
||||
lua.push_value(value)?;
|
||||
if lua.unlikely_memory_error() {
|
||||
let len = ffi::lua_rawlen(lua.state, -2) as Integer;
|
||||
ffi::lua_rawseti(lua.state, -2, len + 1);
|
||||
ffi::lua_pop(lua.state, 1);
|
||||
let len = ffi::lua_rawlen(state, -2) as Integer;
|
||||
ffi::lua_rawseti(state, -2, len + 1);
|
||||
ffi::lua_pop(state, 1);
|
||||
Ok(())
|
||||
} else {
|
||||
protect_lua!(lua.state, 2, 0, fn(state) {
|
||||
protect_lua!(state, 2, 0, fn(state) {
|
||||
let len = ffi::lua_rawlen(state, -2) as Integer;
|
||||
ffi::lua_rawseti(state, -2, len + 1);
|
||||
})
|
||||
|
||||
+99
-4
@@ -2,7 +2,7 @@ use std::borrow::{Borrow, Cow};
|
||||
use std::hash::{Hash, Hasher};
|
||||
use std::os::raw::c_void;
|
||||
use std::string::String as StdString;
|
||||
use std::{slice, str};
|
||||
use std::{fmt, slice, str};
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
use {
|
||||
@@ -11,15 +11,35 @@ use {
|
||||
};
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
use crate::ffi;
|
||||
use crate::types::LuaRef;
|
||||
|
||||
/// Handle to an internal Lua string.
|
||||
///
|
||||
/// Unlike Rust strings, Lua strings may not be valid UTF-8.
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(Clone)]
|
||||
pub struct String<'lua>(pub(crate) LuaRef<'lua>);
|
||||
|
||||
/// Owned handle to an internal Lua string.
|
||||
///
|
||||
/// The owned handle holds a *strong* reference to the current Lua instance.
|
||||
/// Be warned, if you place it into a Lua type (eg. [`UserData`] or a Rust callback), it is *very easy*
|
||||
/// to accidentally cause reference cycles that would prevent destroying Lua instance.
|
||||
///
|
||||
/// [`UserData`]: crate::UserData
|
||||
#[cfg(feature = "unstable")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "unstable")))]
|
||||
#[derive(Clone)]
|
||||
pub struct OwnedString(pub(crate) crate::types::LuaOwnedRef);
|
||||
|
||||
#[cfg(feature = "unstable")]
|
||||
impl OwnedString {
|
||||
/// Get borrowed handle to the underlying Lua string.
|
||||
#[cfg_attr(feature = "send", allow(unused))]
|
||||
pub const fn to_ref(&self) -> String {
|
||||
String(self.0.to_ref())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> String<'lua> {
|
||||
/// Get a `&str` slice if the Lua string is valid UTF-8.
|
||||
///
|
||||
@@ -122,6 +142,43 @@ impl<'lua> String<'lua> {
|
||||
let ref_thread = self.0.lua.ref_thread();
|
||||
unsafe { ffi::lua_topointer(ref_thread, self.0.index) }
|
||||
}
|
||||
|
||||
/// Convert this handle to owned version.
|
||||
#[cfg(all(feature = "unstable", any(not(feature = "send"), doc)))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all(feature = "unstable", not(feature = "send")))))]
|
||||
#[inline]
|
||||
pub fn into_owned(self) -> OwnedString {
|
||||
OwnedString(self.0.into_owned())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> fmt::Debug for String<'lua> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
let bytes = self.as_bytes();
|
||||
// Check if the string is valid utf8
|
||||
if let Ok(s) = str::from_utf8(bytes) {
|
||||
return s.fmt(f);
|
||||
}
|
||||
|
||||
// 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(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> AsRef<[u8]> for String<'lua> {
|
||||
@@ -146,7 +203,7 @@ impl<'lua> Borrow<[u8]> for String<'lua> {
|
||||
// in other ways.
|
||||
impl<'lua, T> PartialEq<T> for String<'lua>
|
||||
where
|
||||
T: AsRef<[u8]>,
|
||||
T: AsRef<[u8]> + ?Sized,
|
||||
{
|
||||
fn eq(&self, other: &T) -> bool {
|
||||
self.as_bytes() == other.as_ref()
|
||||
@@ -173,3 +230,41 @@ impl<'lua> Serialize for String<'lua> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Additional shortcuts
|
||||
#[cfg(feature = "unstable")]
|
||||
impl OwnedString {
|
||||
/// Get a `&str` slice if the Lua string is valid UTF-8.
|
||||
///
|
||||
/// This is a shortcut for [`String::to_str()`].
|
||||
#[inline]
|
||||
pub fn to_str(&self) -> Result<&str> {
|
||||
let s = self.to_ref();
|
||||
// Reattach lifetime to &self
|
||||
unsafe { std::mem::transmute(s.to_str()) }
|
||||
}
|
||||
|
||||
/// Get the bytes that make up this string.
|
||||
///
|
||||
/// This is a shortcut for [`String::as_bytes()`].
|
||||
#[inline]
|
||||
pub fn as_bytes(&self) -> &[u8] {
|
||||
let s = self.to_ref();
|
||||
// Reattach lifetime to &self
|
||||
unsafe { std::mem::transmute(s.as_bytes()) }
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "unstable")]
|
||||
impl fmt::Debug for OwnedString {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
self.to_ref().fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod assertions {
|
||||
use super::*;
|
||||
|
||||
static_assertions::assert_not_impl_any!(String: Send);
|
||||
}
|
||||
|
||||
+238
-103
@@ -1,3 +1,5 @@
|
||||
use std::collections::HashSet;
|
||||
use std::fmt;
|
||||
use std::marker::PhantomData;
|
||||
use std::os::raw::c_void;
|
||||
|
||||
@@ -9,19 +11,40 @@ use {
|
||||
};
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
use crate::ffi;
|
||||
use crate::function::Function;
|
||||
use crate::private::Sealed;
|
||||
use crate::types::{Integer, LuaRef};
|
||||
use crate::util::{assert_stack, check_stack, StackGuard};
|
||||
use crate::value::{FromLua, FromLuaMulti, Nil, ToLua, ToLuaMulti, Value};
|
||||
use crate::value::{FromLua, FromLuaMulti, IntoLua, IntoLuaMulti, Nil, Value};
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
use {futures_core::future::LocalBoxFuture, futures_util::future};
|
||||
|
||||
/// Handle to an internal Lua table.
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(Clone)]
|
||||
pub struct Table<'lua>(pub(crate) LuaRef<'lua>);
|
||||
|
||||
/// Owned handle to an internal Lua table.
|
||||
///
|
||||
/// The owned handle holds a *strong* reference to the current Lua instance.
|
||||
/// Be warned, if you place it into a Lua type (eg. [`UserData`] or a Rust callback), it is *very easy*
|
||||
/// to accidentally cause reference cycles that would prevent destroying Lua instance.
|
||||
///
|
||||
/// [`UserData`]: crate::UserData
|
||||
#[cfg(feature = "unstable")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "unstable")))]
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct OwnedTable(pub(crate) crate::types::LuaOwnedRef);
|
||||
|
||||
#[cfg(feature = "unstable")]
|
||||
impl OwnedTable {
|
||||
/// Get borrowed handle to the underlying Lua table.
|
||||
#[cfg_attr(feature = "send", allow(unused))]
|
||||
pub const fn to_ref(&self) -> Table {
|
||||
Table(self.0.to_ref())
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::len_without_is_empty)]
|
||||
impl<'lua> Table<'lua> {
|
||||
/// Sets a key-value pair in the table.
|
||||
@@ -57,24 +80,25 @@ impl<'lua> Table<'lua> {
|
||||
/// ```
|
||||
///
|
||||
/// [`raw_set`]: #method.raw_set
|
||||
pub fn set<K: ToLua<'lua>, V: ToLua<'lua>>(&self, key: K, value: V) -> Result<()> {
|
||||
pub fn set<K: IntoLua<'lua>, V: IntoLua<'lua>>(&self, key: K, value: V) -> Result<()> {
|
||||
// Fast track
|
||||
if !self.has_metatable() {
|
||||
return self.raw_set(key, value);
|
||||
}
|
||||
|
||||
let lua = self.0.lua;
|
||||
let key = key.to_lua(lua)?;
|
||||
let value = value.to_lua(lua)?;
|
||||
let key = key.into_lua(lua)?;
|
||||
let value = value.into_lua(lua)?;
|
||||
|
||||
let state = lua.state();
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(lua.state);
|
||||
check_stack(lua.state, 5)?;
|
||||
let _sg = StackGuard::new(state);
|
||||
check_stack(state, 5)?;
|
||||
|
||||
lua.push_ref(&self.0);
|
||||
lua.push_value(key)?;
|
||||
lua.push_value(value)?;
|
||||
protect_lua!(lua.state, 3, 0, fn(state) ffi::lua_settable(state, -3))
|
||||
protect_lua!(state, 3, 0, fn(state) ffi::lua_settable(state, -3))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -102,22 +126,23 @@ impl<'lua> Table<'lua> {
|
||||
/// ```
|
||||
///
|
||||
/// [`raw_get`]: #method.raw_get
|
||||
pub fn get<K: ToLua<'lua>, V: FromLua<'lua>>(&self, key: K) -> Result<V> {
|
||||
pub fn get<K: IntoLua<'lua>, V: FromLua<'lua>>(&self, key: K) -> Result<V> {
|
||||
// Fast track
|
||||
if !self.has_metatable() {
|
||||
return self.raw_get(key);
|
||||
}
|
||||
|
||||
let lua = self.0.lua;
|
||||
let key = key.to_lua(lua)?;
|
||||
let state = lua.state();
|
||||
let key = key.into_lua(lua)?;
|
||||
|
||||
let value = unsafe {
|
||||
let _sg = StackGuard::new(lua.state);
|
||||
check_stack(lua.state, 4)?;
|
||||
let _sg = StackGuard::new(state);
|
||||
check_stack(state, 4)?;
|
||||
|
||||
lua.push_ref(&self.0);
|
||||
lua.push_value(key)?;
|
||||
protect_lua!(lua.state, 2, 1, fn(state) ffi::lua_gettable(state, -2))?;
|
||||
protect_lua!(state, 2, 1, fn(state) ffi::lua_gettable(state, -2))?;
|
||||
|
||||
lua.pop_value()
|
||||
};
|
||||
@@ -125,26 +150,27 @@ impl<'lua> Table<'lua> {
|
||||
}
|
||||
|
||||
/// Checks whether the table contains a non-nil value for `key`.
|
||||
pub fn contains_key<K: ToLua<'lua>>(&self, key: K) -> Result<bool> {
|
||||
pub fn contains_key<K: IntoLua<'lua>>(&self, key: K) -> Result<bool> {
|
||||
Ok(self.get::<_, Value>(key)? != Value::Nil)
|
||||
}
|
||||
|
||||
/// Appends a value to the back of the table.
|
||||
pub fn push<V: ToLua<'lua>>(&self, value: V) -> Result<()> {
|
||||
pub fn push<V: IntoLua<'lua>>(&self, value: V) -> Result<()> {
|
||||
// Fast track
|
||||
if !self.has_metatable() {
|
||||
return self.raw_push(value);
|
||||
}
|
||||
|
||||
let lua = self.0.lua;
|
||||
let value = value.to_lua(lua)?;
|
||||
let state = lua.state();
|
||||
let value = value.into_lua(lua)?;
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(lua.state);
|
||||
check_stack(lua.state, 4)?;
|
||||
let _sg = StackGuard::new(state);
|
||||
check_stack(state, 4)?;
|
||||
|
||||
lua.push_ref(&self.0);
|
||||
lua.push_value(value)?;
|
||||
protect_lua!(lua.state, 2, 0, fn(state) {
|
||||
protect_lua!(state, 2, 0, fn(state) {
|
||||
let len = ffi::luaL_len(state, -2) as Integer;
|
||||
ffi::lua_seti(state, -2, len + 1);
|
||||
})?
|
||||
@@ -160,12 +186,13 @@ impl<'lua> Table<'lua> {
|
||||
}
|
||||
|
||||
let lua = self.0.lua;
|
||||
let state = lua.state();
|
||||
let value = unsafe {
|
||||
let _sg = StackGuard::new(lua.state);
|
||||
check_stack(lua.state, 4)?;
|
||||
let _sg = StackGuard::new(state);
|
||||
check_stack(state, 4)?;
|
||||
|
||||
lua.push_ref(&self.0);
|
||||
protect_lua!(lua.state, 1, 1, fn(state) {
|
||||
protect_lua!(state, 1, 1, fn(state) {
|
||||
let len = ffi::luaL_len(state, -1) as Integer;
|
||||
ffi::lua_geti(state, -1, len);
|
||||
ffi::lua_pushnil(state);
|
||||
@@ -233,44 +260,46 @@ impl<'lua> Table<'lua> {
|
||||
}
|
||||
|
||||
/// Sets a key-value pair without invoking metamethods.
|
||||
pub fn raw_set<K: ToLua<'lua>, V: ToLua<'lua>>(&self, key: K, value: V) -> Result<()> {
|
||||
pub fn raw_set<K: IntoLua<'lua>, V: IntoLua<'lua>>(&self, key: K, value: V) -> Result<()> {
|
||||
#[cfg(feature = "luau")]
|
||||
self.check_readonly_write()?;
|
||||
|
||||
let lua = self.0.lua;
|
||||
let key = key.to_lua(lua)?;
|
||||
let value = value.to_lua(lua)?;
|
||||
let state = lua.state();
|
||||
let key = key.into_lua(lua)?;
|
||||
let value = value.into_lua(lua)?;
|
||||
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(lua.state);
|
||||
check_stack(lua.state, 5)?;
|
||||
let _sg = StackGuard::new(state);
|
||||
check_stack(state, 5)?;
|
||||
|
||||
lua.push_ref(&self.0);
|
||||
lua.push_value(key)?;
|
||||
lua.push_value(value)?;
|
||||
|
||||
if lua.unlikely_memory_error() {
|
||||
ffi::lua_rawset(lua.state, -3);
|
||||
ffi::lua_pop(lua.state, 1);
|
||||
ffi::lua_rawset(state, -3);
|
||||
ffi::lua_pop(state, 1);
|
||||
Ok(())
|
||||
} else {
|
||||
protect_lua!(lua.state, 3, 0, fn(state) ffi::lua_rawset(state, -3))
|
||||
protect_lua!(state, 3, 0, fn(state) ffi::lua_rawset(state, -3))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets the value associated to `key` without invoking metamethods.
|
||||
pub fn raw_get<K: ToLua<'lua>, V: FromLua<'lua>>(&self, key: K) -> Result<V> {
|
||||
pub fn raw_get<K: IntoLua<'lua>, V: FromLua<'lua>>(&self, key: K) -> Result<V> {
|
||||
let lua = self.0.lua;
|
||||
let key = key.to_lua(lua)?;
|
||||
let state = lua.state();
|
||||
let key = key.into_lua(lua)?;
|
||||
|
||||
let value = unsafe {
|
||||
let _sg = StackGuard::new(lua.state);
|
||||
check_stack(lua.state, 3)?;
|
||||
let _sg = StackGuard::new(state);
|
||||
check_stack(state, 3)?;
|
||||
|
||||
lua.push_ref(&self.0);
|
||||
lua.push_value(key)?;
|
||||
ffi::lua_rawget(lua.state, -2);
|
||||
ffi::lua_rawget(state, -2);
|
||||
|
||||
lua.pop_value()
|
||||
};
|
||||
@@ -279,21 +308,23 @@ impl<'lua> Table<'lua> {
|
||||
|
||||
/// Inserts element value at position `idx` to the table, shifting up the elements from `table[idx]`.
|
||||
/// The worst case complexity is O(n), where n is the table length.
|
||||
pub fn raw_insert<V: ToLua<'lua>>(&self, idx: Integer, value: V) -> Result<()> {
|
||||
pub fn raw_insert<V: IntoLua<'lua>>(&self, idx: Integer, value: V) -> Result<()> {
|
||||
let lua = self.0.lua;
|
||||
let state = lua.state();
|
||||
|
||||
let size = self.raw_len();
|
||||
if idx < 1 || idx > size + 1 {
|
||||
return Err(Error::RuntimeError("index out of bounds".to_string()));
|
||||
}
|
||||
|
||||
let value = value.to_lua(lua)?;
|
||||
let value = value.into_lua(lua)?;
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(lua.state);
|
||||
check_stack(lua.state, 5)?;
|
||||
let _sg = StackGuard::new(state);
|
||||
check_stack(state, 5)?;
|
||||
|
||||
lua.push_ref(&self.0);
|
||||
lua.push_value(value)?;
|
||||
protect_lua!(lua.state, 2, 0, |state| {
|
||||
protect_lua!(state, 2, 0, |state| {
|
||||
for i in (idx..=size).rev() {
|
||||
// table[i+1] = table[i]
|
||||
ffi::lua_rawgeti(state, -2, i);
|
||||
@@ -305,16 +336,17 @@ impl<'lua> Table<'lua> {
|
||||
}
|
||||
|
||||
/// Appends a value to the back of the table without invoking metamethods.
|
||||
pub fn raw_push<V: ToLua<'lua>>(&self, value: V) -> Result<()> {
|
||||
pub fn raw_push<V: IntoLua<'lua>>(&self, value: V) -> Result<()> {
|
||||
#[cfg(feature = "luau")]
|
||||
self.check_readonly_write()?;
|
||||
|
||||
let lua = self.0.lua;
|
||||
let value = value.to_lua(lua)?;
|
||||
let state = lua.state();
|
||||
let value = value.into_lua(lua)?;
|
||||
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(lua.state);
|
||||
check_stack(lua.state, 4)?;
|
||||
let _sg = StackGuard::new(state);
|
||||
check_stack(state, 4)?;
|
||||
|
||||
lua.push_ref(&self.0);
|
||||
lua.push_value(value)?;
|
||||
@@ -325,9 +357,9 @@ impl<'lua> Table<'lua> {
|
||||
}
|
||||
|
||||
if lua.unlikely_memory_error() {
|
||||
callback(lua.state);
|
||||
callback(state);
|
||||
} else {
|
||||
protect_lua!(lua.state, 2, 0, fn(state) callback(state))?;
|
||||
protect_lua!(state, 2, 0, fn(state) callback(state))?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
@@ -339,16 +371,17 @@ impl<'lua> Table<'lua> {
|
||||
self.check_readonly_write()?;
|
||||
|
||||
let lua = self.0.lua;
|
||||
let state = lua.state();
|
||||
let value = unsafe {
|
||||
let _sg = StackGuard::new(lua.state);
|
||||
check_stack(lua.state, 3)?;
|
||||
let _sg = StackGuard::new(state);
|
||||
check_stack(state, 3)?;
|
||||
|
||||
lua.push_ref(&self.0);
|
||||
let len = ffi::lua_rawlen(lua.state, -1) as Integer;
|
||||
ffi::lua_rawgeti(lua.state, -1, len);
|
||||
let len = ffi::lua_rawlen(state, -1) as Integer;
|
||||
ffi::lua_rawgeti(state, -1, len);
|
||||
// Set slot to nil (it must be safe to do)
|
||||
ffi::lua_pushnil(lua.state);
|
||||
ffi::lua_rawseti(lua.state, -3, len);
|
||||
ffi::lua_pushnil(state);
|
||||
ffi::lua_rawseti(state, -3, len);
|
||||
lua.pop_value()
|
||||
};
|
||||
V::from_lua(value, lua)
|
||||
@@ -361,9 +394,10 @@ impl<'lua> Table<'lua> {
|
||||
/// where n is the table length.
|
||||
///
|
||||
/// For other key types this is equivalent to setting `table[key] = nil`.
|
||||
pub fn raw_remove<K: ToLua<'lua>>(&self, key: K) -> Result<()> {
|
||||
pub fn raw_remove<K: IntoLua<'lua>>(&self, key: K) -> Result<()> {
|
||||
let lua = self.0.lua;
|
||||
let key = key.to_lua(lua)?;
|
||||
let state = lua.state();
|
||||
let key = key.into_lua(lua)?;
|
||||
match key {
|
||||
Value::Integer(idx) => {
|
||||
let size = self.raw_len();
|
||||
@@ -371,11 +405,11 @@ impl<'lua> Table<'lua> {
|
||||
return Err(Error::RuntimeError("index out of bounds".to_string()));
|
||||
}
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(lua.state);
|
||||
check_stack(lua.state, 4)?;
|
||||
let _sg = StackGuard::new(state);
|
||||
check_stack(state, 4)?;
|
||||
|
||||
lua.push_ref(&self.0);
|
||||
protect_lua!(lua.state, 1, 0, |state| {
|
||||
protect_lua!(state, 1, 0, |state| {
|
||||
for i in idx..size {
|
||||
ffi::lua_rawgeti(state, -1, i + 1);
|
||||
ffi::lua_rawseti(state, -2, i);
|
||||
@@ -389,6 +423,47 @@ impl<'lua> Table<'lua> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Clears the table, removing all keys and values from array and hash parts,
|
||||
/// without invoking metamethods.
|
||||
///
|
||||
/// This method is useful to clear the table while keeping its capacity.
|
||||
pub fn clear(&self) -> Result<()> {
|
||||
#[cfg(feature = "luau")]
|
||||
self.check_readonly_write()?;
|
||||
|
||||
let lua = self.0.lua;
|
||||
unsafe {
|
||||
#[cfg(feature = "luau")]
|
||||
ffi::lua_cleartable(lua.ref_thread(), self.0.index);
|
||||
|
||||
#[cfg(not(feature = "luau"))]
|
||||
{
|
||||
let state = lua.state();
|
||||
check_stack(state, 4)?;
|
||||
|
||||
lua.push_ref(&self.0);
|
||||
|
||||
// Clear array part
|
||||
for i in 1..=ffi::lua_rawlen(state, -1) {
|
||||
ffi::lua_pushnil(state);
|
||||
ffi::lua_rawseti(state, -2, i as Integer);
|
||||
}
|
||||
|
||||
// Clear hash part
|
||||
// It must be safe as long as we don't use invalid keys
|
||||
ffi::lua_pushnil(state);
|
||||
while ffi::lua_next(state, -2) != 0 {
|
||||
ffi::lua_pop(state, 1); // pop value
|
||||
ffi::lua_pushvalue(state, -1); // copy key
|
||||
ffi::lua_pushnil(state);
|
||||
ffi::lua_rawset(state, -4);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Returns the result of the Lua `#` operator.
|
||||
///
|
||||
/// This might invoke the `__len` metamethod. Use the [`raw_len`] method if that is not desired.
|
||||
@@ -401,12 +476,13 @@ impl<'lua> Table<'lua> {
|
||||
}
|
||||
|
||||
let lua = self.0.lua;
|
||||
let state = lua.state();
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(lua.state);
|
||||
check_stack(lua.state, 4)?;
|
||||
let _sg = StackGuard::new(state);
|
||||
check_stack(state, 4)?;
|
||||
|
||||
lua.push_ref(&self.0);
|
||||
protect_lua!(lua.state, 1, 0, |state| ffi::luaL_len(state, -1))
|
||||
protect_lua!(state, 1, 0, |state| ffi::luaL_len(state, -1))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -421,12 +497,13 @@ impl<'lua> Table<'lua> {
|
||||
/// Unlike the `getmetatable` Lua function, this method ignores the `__metatable` field.
|
||||
pub fn get_metatable(&self) -> Option<Table<'lua>> {
|
||||
let lua = self.0.lua;
|
||||
let state = lua.state();
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(lua.state);
|
||||
assert_stack(lua.state, 2);
|
||||
let _sg = StackGuard::new(state);
|
||||
assert_stack(state, 2);
|
||||
|
||||
lua.push_ref(&self.0);
|
||||
if ffi::lua_getmetatable(lua.state, -1) == 0 {
|
||||
if ffi::lua_getmetatable(state, -1) == 0 {
|
||||
None
|
||||
} else {
|
||||
Some(Table(lua.pop_ref()))
|
||||
@@ -446,17 +523,18 @@ impl<'lua> Table<'lua> {
|
||||
}
|
||||
|
||||
let lua = self.0.lua;
|
||||
let state = lua.state();
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(lua.state);
|
||||
assert_stack(lua.state, 2);
|
||||
let _sg = StackGuard::new(state);
|
||||
assert_stack(state, 2);
|
||||
|
||||
lua.push_ref(&self.0);
|
||||
if let Some(metatable) = metatable {
|
||||
lua.push_ref(&metatable.0);
|
||||
} else {
|
||||
ffi::lua_pushnil(lua.state);
|
||||
ffi::lua_pushnil(state);
|
||||
}
|
||||
ffi::lua_setmetatable(lua.state, -2);
|
||||
ffi::lua_setmetatable(state, -2);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -512,6 +590,14 @@ impl<'lua> Table<'lua> {
|
||||
unsafe { ffi::lua_topointer(ref_thread, self.0.index) }
|
||||
}
|
||||
|
||||
/// Convert this handle to owned version.
|
||||
#[cfg(all(feature = "unstable", any(not(feature = "send"), doc)))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all(feature = "unstable", not(feature = "send")))))]
|
||||
#[inline]
|
||||
pub fn into_owned(self) -> OwnedTable {
|
||||
OwnedTable(self.0.into_owned())
|
||||
}
|
||||
|
||||
/// Consume this table and return an iterator over the pairs of the table.
|
||||
///
|
||||
/// This works like the Lua `pairs` function, but does not invoke the `__pairs` metamethod.
|
||||
@@ -637,16 +723,17 @@ impl<'lua> Table<'lua> {
|
||||
#[cfg(feature = "serialize")]
|
||||
pub(crate) fn is_array(&self) -> bool {
|
||||
let lua = self.0.lua;
|
||||
let state = lua.state();
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(lua.state);
|
||||
assert_stack(lua.state, 3);
|
||||
let _sg = StackGuard::new(state);
|
||||
assert_stack(state, 3);
|
||||
|
||||
lua.push_ref(&self.0);
|
||||
if ffi::lua_getmetatable(lua.state, -1) == 0 {
|
||||
if ffi::lua_getmetatable(state, -1) == 0 {
|
||||
return false;
|
||||
}
|
||||
crate::serde::push_array_metatable(lua.state);
|
||||
ffi::lua_rawequal(lua.state, -1, -2) != 0
|
||||
crate::serde::push_array_metatable(state);
|
||||
ffi::lua_rawequal(state, -1, -2) != 0
|
||||
}
|
||||
}
|
||||
|
||||
@@ -659,6 +746,42 @@ impl<'lua> Table<'lua> {
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn fmt_pretty(
|
||||
&self,
|
||||
fmt: &mut fmt::Formatter,
|
||||
ident: usize,
|
||||
visited: &mut HashSet<*const c_void>,
|
||||
) -> fmt::Result {
|
||||
visited.insert(self.to_pointer());
|
||||
|
||||
let t = self.clone();
|
||||
// Collect key/value pairs into a vector so we can sort them
|
||||
let mut pairs = t.pairs::<Value, Value>().flatten().collect::<Vec<_>>();
|
||||
// Sort keys
|
||||
pairs.sort_by(|(a, _), (b, _)| a.cmp(b));
|
||||
if pairs.is_empty() {
|
||||
return write!(fmt, "{{}}");
|
||||
}
|
||||
writeln!(fmt, "{{")?;
|
||||
for (key, value) in pairs {
|
||||
write!(fmt, "{}[", " ".repeat(ident + 2))?;
|
||||
key.fmt_pretty(fmt, false, ident + 2, visited)?;
|
||||
write!(fmt, "] = ")?;
|
||||
value.fmt_pretty(fmt, true, ident + 2, visited)?;
|
||||
writeln!(fmt, ",")?;
|
||||
}
|
||||
write!(fmt, "{}}}", " ".repeat(ident))
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Table<'_> {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
if fmt.alternate() {
|
||||
return self.fmt_pretty(fmt, 0, &mut HashSet::new());
|
||||
}
|
||||
fmt.write_fmt(format_args!("Table({:?})", self.0))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> PartialEq for Table<'lua> {
|
||||
@@ -675,13 +798,13 @@ impl<'lua> AsRef<Table<'lua>> for Table<'lua> {
|
||||
}
|
||||
|
||||
/// An extension trait for `Table`s that provides a variety of convenient functionality.
|
||||
pub trait TableExt<'lua> {
|
||||
pub trait TableExt<'lua>: Sealed {
|
||||
/// Calls the table as function assuming it has `__call` metamethod.
|
||||
///
|
||||
/// The metamethod is called with the table as its first argument, followed by the passed arguments.
|
||||
fn call<A, R>(&self, args: A) -> Result<R>
|
||||
where
|
||||
A: ToLuaMulti<'lua>,
|
||||
A: IntoLuaMulti<'lua>,
|
||||
R: FromLuaMulti<'lua>;
|
||||
|
||||
/// Asynchronously calls the table as function assuming it has `__call` metamethod.
|
||||
@@ -692,7 +815,7 @@ pub trait TableExt<'lua> {
|
||||
fn call_async<'fut, A, R>(&self, args: A) -> LocalBoxFuture<'fut, Result<R>>
|
||||
where
|
||||
'lua: 'fut,
|
||||
A: ToLuaMulti<'lua>,
|
||||
A: IntoLuaMulti<'lua>,
|
||||
R: FromLuaMulti<'lua> + 'fut;
|
||||
|
||||
/// Gets the function associated to `key` from the table and executes it,
|
||||
@@ -704,8 +827,8 @@ pub trait TableExt<'lua> {
|
||||
/// This might invoke the `__index` metamethod.
|
||||
fn call_method<K, A, R>(&self, key: K, args: A) -> Result<R>
|
||||
where
|
||||
K: ToLua<'lua>,
|
||||
A: ToLuaMulti<'lua>,
|
||||
K: IntoLua<'lua>,
|
||||
A: IntoLuaMulti<'lua>,
|
||||
R: FromLuaMulti<'lua>;
|
||||
|
||||
/// Gets the function associated to `key` from the table and executes it,
|
||||
@@ -717,8 +840,8 @@ pub trait TableExt<'lua> {
|
||||
/// This might invoke the `__index` metamethod.
|
||||
fn call_function<K, A, R>(&self, key: K, args: A) -> Result<R>
|
||||
where
|
||||
K: ToLua<'lua>,
|
||||
A: ToLuaMulti<'lua>,
|
||||
K: IntoLua<'lua>,
|
||||
A: IntoLuaMulti<'lua>,
|
||||
R: FromLuaMulti<'lua>;
|
||||
|
||||
/// Gets the function associated to `key` from the table and asynchronously executes it,
|
||||
@@ -732,8 +855,8 @@ pub trait TableExt<'lua> {
|
||||
fn call_async_method<'fut, K, A, R>(&self, key: K, args: A) -> LocalBoxFuture<'fut, Result<R>>
|
||||
where
|
||||
'lua: 'fut,
|
||||
K: ToLua<'lua>,
|
||||
A: ToLuaMulti<'lua>,
|
||||
K: IntoLua<'lua>,
|
||||
A: IntoLuaMulti<'lua>,
|
||||
R: FromLuaMulti<'lua> + 'fut;
|
||||
|
||||
/// Gets the function associated to `key` from the table and asynchronously executes it,
|
||||
@@ -751,15 +874,15 @@ pub trait TableExt<'lua> {
|
||||
) -> LocalBoxFuture<'fut, Result<R>>
|
||||
where
|
||||
'lua: 'fut,
|
||||
K: ToLua<'lua>,
|
||||
A: ToLuaMulti<'lua>,
|
||||
K: IntoLua<'lua>,
|
||||
A: IntoLuaMulti<'lua>,
|
||||
R: FromLuaMulti<'lua> + 'fut;
|
||||
}
|
||||
|
||||
impl<'lua> TableExt<'lua> for Table<'lua> {
|
||||
fn call<A, R>(&self, args: A) -> Result<R>
|
||||
where
|
||||
A: ToLuaMulti<'lua>,
|
||||
A: IntoLuaMulti<'lua>,
|
||||
R: FromLuaMulti<'lua>,
|
||||
{
|
||||
// Convert table to a function and call via pcall that respects the `__call` metamethod.
|
||||
@@ -770,7 +893,7 @@ impl<'lua> TableExt<'lua> for Table<'lua> {
|
||||
fn call_async<'fut, A, R>(&self, args: A) -> LocalBoxFuture<'fut, Result<R>>
|
||||
where
|
||||
'lua: 'fut,
|
||||
A: ToLuaMulti<'lua>,
|
||||
A: IntoLuaMulti<'lua>,
|
||||
R: FromLuaMulti<'lua> + 'fut,
|
||||
{
|
||||
Function(self.0.clone()).call_async(args)
|
||||
@@ -778,20 +901,20 @@ impl<'lua> TableExt<'lua> for Table<'lua> {
|
||||
|
||||
fn call_method<K, A, R>(&self, key: K, args: A) -> Result<R>
|
||||
where
|
||||
K: ToLua<'lua>,
|
||||
A: ToLuaMulti<'lua>,
|
||||
K: IntoLua<'lua>,
|
||||
A: IntoLuaMulti<'lua>,
|
||||
R: FromLuaMulti<'lua>,
|
||||
{
|
||||
let lua = self.0.lua;
|
||||
let mut args = args.to_lua_multi(lua)?;
|
||||
let mut args = args.into_lua_multi(lua)?;
|
||||
args.push_front(Value::Table(self.clone()));
|
||||
self.get::<_, Function>(key)?.call(args)
|
||||
}
|
||||
|
||||
fn call_function<K, A, R>(&self, key: K, args: A) -> Result<R>
|
||||
where
|
||||
K: ToLua<'lua>,
|
||||
A: ToLuaMulti<'lua>,
|
||||
K: IntoLua<'lua>,
|
||||
A: IntoLuaMulti<'lua>,
|
||||
R: FromLuaMulti<'lua>,
|
||||
{
|
||||
self.get::<_, Function>(key)?.call(args)
|
||||
@@ -801,12 +924,12 @@ impl<'lua> TableExt<'lua> for Table<'lua> {
|
||||
fn call_async_method<'fut, K, A, R>(&self, key: K, args: A) -> LocalBoxFuture<'fut, Result<R>>
|
||||
where
|
||||
'lua: 'fut,
|
||||
K: ToLua<'lua>,
|
||||
A: ToLuaMulti<'lua>,
|
||||
K: IntoLua<'lua>,
|
||||
A: IntoLuaMulti<'lua>,
|
||||
R: FromLuaMulti<'lua> + 'fut,
|
||||
{
|
||||
let lua = self.0.lua;
|
||||
let mut args = match args.to_lua_multi(lua) {
|
||||
let mut args = match args.into_lua_multi(lua) {
|
||||
Ok(args) => args,
|
||||
Err(e) => return Box::pin(future::err(e)),
|
||||
};
|
||||
@@ -818,8 +941,8 @@ impl<'lua> TableExt<'lua> for Table<'lua> {
|
||||
fn call_async_function<'fut, K, A, R>(&self, key: K, args: A) -> LocalBoxFuture<'fut, Result<R>>
|
||||
where
|
||||
'lua: 'fut,
|
||||
K: ToLua<'lua>,
|
||||
A: ToLuaMulti<'lua>,
|
||||
K: IntoLua<'lua>,
|
||||
A: IntoLuaMulti<'lua>,
|
||||
R: FromLuaMulti<'lua> + 'fut,
|
||||
{
|
||||
match self.get::<_, Function>(key) {
|
||||
@@ -894,15 +1017,16 @@ where
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
if let Some(prev_key) = self.key.take() {
|
||||
let lua = self.table.lua;
|
||||
let state = lua.state();
|
||||
|
||||
let res = (|| unsafe {
|
||||
let _sg = StackGuard::new(lua.state);
|
||||
check_stack(lua.state, 5)?;
|
||||
let _sg = StackGuard::new(state);
|
||||
check_stack(state, 5)?;
|
||||
|
||||
lua.push_ref(&self.table);
|
||||
lua.push_value(prev_key)?;
|
||||
|
||||
let next = protect_lua!(lua.state, 2, ffi::LUA_MULTRET, |state| {
|
||||
let next = protect_lua!(state, 2, ffi::LUA_MULTRET, |state| {
|
||||
ffi::lua_next(state, -2)
|
||||
})?;
|
||||
if next != 0 {
|
||||
@@ -954,16 +1078,17 @@ where
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
if let Some(index) = self.index.take() {
|
||||
let lua = self.table.lua;
|
||||
let state = lua.state();
|
||||
|
||||
let res = (|| unsafe {
|
||||
let _sg = StackGuard::new(lua.state);
|
||||
check_stack(lua.state, 1 + if self.raw { 0 } else { 3 })?;
|
||||
let _sg = StackGuard::new(state);
|
||||
check_stack(state, 1 + if self.raw { 0 } else { 3 })?;
|
||||
|
||||
lua.push_ref(&self.table);
|
||||
let res = if self.raw {
|
||||
ffi::lua_rawgeti(lua.state, -1, index)
|
||||
ffi::lua_rawgeti(state, -1, index)
|
||||
} else {
|
||||
protect_lua!(lua.state, 1, 1, |state| ffi::lua_geti(state, -1, index))?
|
||||
protect_lua!(state, 1, 1, |state| ffi::lua_geti(state, -1, index))?
|
||||
};
|
||||
match res {
|
||||
ffi::LUA_TNIL if index > self.len.unwrap_or(0) => Ok(None),
|
||||
@@ -984,3 +1109,13 @@ where
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod assertions {
|
||||
use super::*;
|
||||
|
||||
static_assertions::assert_not_impl_any!(Table: Send);
|
||||
|
||||
#[cfg(feature = "unstable")]
|
||||
static_assertions::assert_not_impl_any!(OwnedTable: Send);
|
||||
}
|
||||
|
||||
+96
-46
@@ -2,10 +2,11 @@ use std::cmp;
|
||||
use std::os::raw::c_int;
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
use crate::ffi;
|
||||
#[allow(unused)]
|
||||
use crate::lua::Lua;
|
||||
use crate::types::LuaRef;
|
||||
use crate::util::{check_stack, error_traceback_thread, pop_error, StackGuard};
|
||||
use crate::value::{FromLuaMulti, ToLuaMulti};
|
||||
use crate::value::{FromLuaMulti, IntoLuaMulti};
|
||||
|
||||
#[cfg(any(
|
||||
feature = "lua54",
|
||||
@@ -14,17 +15,23 @@ use crate::value::{FromLuaMulti, ToLuaMulti};
|
||||
))]
|
||||
use crate::function::Function;
|
||||
|
||||
#[cfg(not(feature = "luau"))]
|
||||
use crate::{
|
||||
hook::{Debug, HookTriggers},
|
||||
types::MaybeSend,
|
||||
};
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
use {
|
||||
crate::{
|
||||
lua::{Lua, ASYNC_POLL_PENDING},
|
||||
lua::ASYNC_POLL_PENDING,
|
||||
value::{MultiValue, Value},
|
||||
},
|
||||
futures_core::{future::Future, stream::Stream},
|
||||
std::{
|
||||
cell::RefCell,
|
||||
marker::PhantomData,
|
||||
pin::Pin,
|
||||
ptr::NonNull,
|
||||
task::{Context, Poll, Waker},
|
||||
},
|
||||
};
|
||||
@@ -56,10 +63,10 @@ pub struct Thread<'lua>(pub(crate) LuaRef<'lua>);
|
||||
/// [`Stream`]: futures_core::stream::Stream
|
||||
#[cfg(feature = "async")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
|
||||
#[derive(Debug)]
|
||||
#[must_use = "futures do nothing unless you `.await` or poll them"]
|
||||
pub struct AsyncThread<'lua, R> {
|
||||
thread: Thread<'lua>,
|
||||
args0: RefCell<Option<Result<MultiValue<'lua>>>>,
|
||||
args0: Option<Result<MultiValue<'lua>>>,
|
||||
ret: PhantomData<R>,
|
||||
recycle: bool,
|
||||
}
|
||||
@@ -108,15 +115,17 @@ impl<'lua> Thread<'lua> {
|
||||
/// ```
|
||||
pub fn resume<A, R>(&self, args: A) -> Result<R>
|
||||
where
|
||||
A: ToLuaMulti<'lua>,
|
||||
A: IntoLuaMulti<'lua>,
|
||||
R: FromLuaMulti<'lua>,
|
||||
{
|
||||
let lua = self.0.lua;
|
||||
let mut args = args.to_lua_multi(lua)?;
|
||||
let state = lua.state();
|
||||
|
||||
let mut args = args.into_lua_multi(lua)?;
|
||||
let nargs = args.len() as c_int;
|
||||
let results = unsafe {
|
||||
let _sg = StackGuard::new(lua.state);
|
||||
check_stack(lua.state, cmp::max(nargs + 1, 3))?;
|
||||
let _sg = StackGuard::new(state);
|
||||
check_stack(state, cmp::max(nargs + 1, 3))?;
|
||||
|
||||
let thread_state = ffi::lua_tothread(lua.ref_thread(), self.0.index);
|
||||
|
||||
@@ -129,23 +138,27 @@ impl<'lua> Thread<'lua> {
|
||||
for arg in args.drain_all() {
|
||||
lua.push_value(arg)?;
|
||||
}
|
||||
ffi::lua_xmove(lua.state, thread_state, nargs);
|
||||
ffi::lua_xmove(state, thread_state, nargs);
|
||||
|
||||
let mut nresults = 0;
|
||||
|
||||
let ret = ffi::lua_resume(thread_state, lua.state, nargs, &mut nresults as *mut c_int);
|
||||
let ret = ffi::lua_resume(thread_state, state, nargs, &mut nresults as *mut c_int);
|
||||
if ret != ffi::LUA_OK && ret != ffi::LUA_YIELD {
|
||||
check_stack(lua.state, 3)?;
|
||||
protect_lua!(lua.state, 0, 1, |state| error_traceback_thread(
|
||||
if ret == ffi::LUA_ERRMEM {
|
||||
// Don't call error handler for memory errors
|
||||
return Err(pop_error(thread_state, ret));
|
||||
}
|
||||
check_stack(state, 3)?;
|
||||
protect_lua!(state, 0, 1, |state| error_traceback_thread(
|
||||
state,
|
||||
thread_state
|
||||
))?;
|
||||
return Err(pop_error(lua.state, ret));
|
||||
return Err(pop_error(state, ret));
|
||||
}
|
||||
|
||||
let mut results = args; // Reuse MultiValue container
|
||||
check_stack(lua.state, nresults + 2)?; // 2 is extra for `lua.pop_value()` below
|
||||
ffi::lua_xmove(thread_state, lua.state, nresults);
|
||||
check_stack(state, nresults + 2)?; // 2 is extra for `lua.pop_value()` below
|
||||
ffi::lua_xmove(thread_state, state, nresults);
|
||||
|
||||
for _ in 0..nresults {
|
||||
results.push_front(lua.pop_value());
|
||||
@@ -172,6 +185,23 @@ impl<'lua> Thread<'lua> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets a 'hook' function that will periodically be called as Lua code executes.
|
||||
///
|
||||
/// This function is similar or [`Lua::set_hook()`] except that it sets for the thread.
|
||||
/// To remove a hook call [`Lua::remove_hook()`].
|
||||
#[cfg(not(feature = "luau"))]
|
||||
#[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,
|
||||
{
|
||||
let lua = self.0.lua;
|
||||
unsafe {
|
||||
let thread_state = ffi::lua_tothread(lua.ref_thread(), self.0.index);
|
||||
lua.set_thread_hook(thread_state, triggers, callback);
|
||||
}
|
||||
}
|
||||
|
||||
/// Resets a thread
|
||||
///
|
||||
/// In [Lua 5.4]: cleans its call stack and closes all pending to-be-closed variables.
|
||||
@@ -194,12 +224,13 @@ impl<'lua> Thread<'lua> {
|
||||
))]
|
||||
pub fn reset(&self, func: Function<'lua>) -> Result<()> {
|
||||
let lua = self.0.lua;
|
||||
let state = lua.state();
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(lua.state);
|
||||
check_stack(lua.state, 2)?;
|
||||
let _sg = StackGuard::new(state);
|
||||
check_stack(state, 2)?;
|
||||
|
||||
lua.push_ref(&self.0);
|
||||
let thread_state = ffi::lua_tothread(lua.state, -1);
|
||||
let thread_state = ffi::lua_tothread(state, -1);
|
||||
|
||||
#[cfg(feature = "lua54")]
|
||||
let status = ffi::lua_resetthread(thread_state);
|
||||
@@ -208,17 +239,17 @@ impl<'lua> Thread<'lua> {
|
||||
return Err(pop_error(thread_state, status));
|
||||
}
|
||||
#[cfg(all(feature = "luajit", feature = "vendored"))]
|
||||
ffi::lua_resetthread(lua.state, thread_state);
|
||||
ffi::lua_resetthread(state, thread_state);
|
||||
#[cfg(feature = "luau")]
|
||||
ffi::lua_resetthread(thread_state);
|
||||
|
||||
lua.push_ref(&func.0);
|
||||
ffi::lua_xmove(lua.state, thread_state, 1);
|
||||
ffi::lua_xmove(state, thread_state, 1);
|
||||
|
||||
#[cfg(feature = "luau")]
|
||||
{
|
||||
// Inherit `LUA_GLOBALSINDEX` from the caller
|
||||
ffi::lua_xpush(lua.state, thread_state, ffi::LUA_GLOBALSINDEX);
|
||||
ffi::lua_xpush(state, thread_state, ffi::LUA_GLOBALSINDEX);
|
||||
ffi::lua_replace(thread_state, ffi::LUA_GLOBALSINDEX);
|
||||
}
|
||||
|
||||
@@ -275,13 +306,13 @@ impl<'lua> Thread<'lua> {
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
|
||||
pub fn into_async<A, R>(self, args: A) -> AsyncThread<'lua, R>
|
||||
where
|
||||
A: ToLuaMulti<'lua>,
|
||||
A: IntoLuaMulti<'lua>,
|
||||
R: FromLuaMulti<'lua>,
|
||||
{
|
||||
let args = args.to_lua_multi(self.0.lua);
|
||||
let args = args.into_lua_multi(self.0.lua);
|
||||
AsyncThread {
|
||||
thread: self,
|
||||
args0: RefCell::new(Some(args)),
|
||||
args0: Some(args),
|
||||
ret: PhantomData,
|
||||
recycle: false,
|
||||
}
|
||||
@@ -323,14 +354,15 @@ impl<'lua> Thread<'lua> {
|
||||
#[doc(hidden)]
|
||||
pub fn sandbox(&self) -> Result<()> {
|
||||
let lua = self.0.lua;
|
||||
let state = lua.state();
|
||||
unsafe {
|
||||
let thread = ffi::lua_tothread(lua.ref_thread(), self.0.index);
|
||||
check_stack(thread, 1)?;
|
||||
check_stack(lua.state, 3)?;
|
||||
check_stack(state, 3)?;
|
||||
// Inherit `LUA_GLOBALSINDEX` from the caller
|
||||
ffi::lua_xpush(lua.state, thread, ffi::LUA_GLOBALSINDEX);
|
||||
ffi::lua_xpush(state, thread, ffi::LUA_GLOBALSINDEX);
|
||||
ffi::lua_replace(thread, ffi::LUA_GLOBALSINDEX);
|
||||
protect_lua!(lua.state, 0, 0, |_| ffi::luaL_sandboxthread(thread))
|
||||
protect_lua!(state, 0, 0, |_| ffi::luaL_sandboxthread(thread))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -388,11 +420,14 @@ where
|
||||
_ => return Poll::Ready(None),
|
||||
};
|
||||
|
||||
let _wg = WakerGuard::new(lua, cx.waker().clone());
|
||||
let ret: MultiValue = if let Some(args) = self.args0.borrow_mut().take() {
|
||||
self.thread.resume(args?)?
|
||||
let _wg = WakerGuard::new(lua, cx.waker());
|
||||
|
||||
// This is safe as we are not moving the whole struct
|
||||
let this = unsafe { self.get_unchecked_mut() };
|
||||
let ret: MultiValue = if let Some(args) = this.args0.take() {
|
||||
this.thread.resume(args?)?
|
||||
} else {
|
||||
self.thread.resume(())?
|
||||
this.thread.resume(())?
|
||||
};
|
||||
|
||||
if is_poll_pending(&ret) {
|
||||
@@ -419,18 +454,21 @@ where
|
||||
_ => return Poll::Ready(Err(Error::CoroutineInactive)),
|
||||
};
|
||||
|
||||
let _wg = WakerGuard::new(lua, cx.waker().clone());
|
||||
let ret: MultiValue = if let Some(args) = self.args0.borrow_mut().take() {
|
||||
self.thread.resume(args?)?
|
||||
let _wg = WakerGuard::new(lua, cx.waker());
|
||||
|
||||
// This is safe as we are not moving the whole struct
|
||||
let this = unsafe { self.get_unchecked_mut() };
|
||||
let ret: MultiValue = if let Some(args) = this.args0.take() {
|
||||
this.thread.resume(args?)?
|
||||
} else {
|
||||
self.thread.resume(())?
|
||||
this.thread.resume(())?
|
||||
};
|
||||
|
||||
if is_poll_pending(&ret) {
|
||||
return Poll::Pending;
|
||||
}
|
||||
|
||||
if let ThreadStatus::Resumable = self.thread.status() {
|
||||
if let ThreadStatus::Resumable = this.thread.status() {
|
||||
// Ignore value returned via yield()
|
||||
cx.waker().wake_by_ref();
|
||||
return Poll::Pending;
|
||||
@@ -452,27 +490,39 @@ fn is_poll_pending(val: &MultiValue) -> bool {
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
struct WakerGuard<'lua> {
|
||||
struct WakerGuard<'lua, 'a> {
|
||||
lua: &'lua Lua,
|
||||
prev: Option<Waker>,
|
||||
prev: NonNull<Waker>,
|
||||
_phantom: PhantomData<&'a ()>,
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
impl<'lua> WakerGuard<'lua> {
|
||||
impl<'lua, 'a> WakerGuard<'lua, 'a> {
|
||||
#[inline]
|
||||
pub fn new(lua: &Lua, waker: Waker) -> Result<WakerGuard> {
|
||||
pub fn new(lua: &'lua Lua, waker: &'a Waker) -> Result<WakerGuard<'lua, 'a>> {
|
||||
unsafe {
|
||||
let prev = lua.set_waker(Some(waker));
|
||||
Ok(WakerGuard { lua, prev })
|
||||
let prev = lua.set_waker(NonNull::from(waker));
|
||||
Ok(WakerGuard {
|
||||
lua,
|
||||
prev,
|
||||
_phantom: PhantomData,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
impl<'lua> Drop for WakerGuard<'lua> {
|
||||
impl<'lua, 'a> Drop for WakerGuard<'lua, 'a> {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
self.lua.set_waker(self.prev.take());
|
||||
self.lua.set_waker(self.prev);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod assertions {
|
||||
use super::*;
|
||||
|
||||
static_assertions::assert_not_impl_any!(Thread: Send);
|
||||
}
|
||||
|
||||
+90
-8
@@ -12,13 +12,15 @@ use std::ffi::CStr;
|
||||
use futures_core::future::LocalBoxFuture;
|
||||
|
||||
use crate::error::Result;
|
||||
use crate::ffi;
|
||||
#[cfg(not(feature = "luau"))]
|
||||
use crate::hook::Debug;
|
||||
use crate::lua::{ExtraData, Lua};
|
||||
use crate::util::{assert_stack, StackGuard};
|
||||
use crate::value::MultiValue;
|
||||
|
||||
#[cfg(feature = "unstable")]
|
||||
use {crate::lua::LuaInner, std::marker::PhantomData};
|
||||
|
||||
/// Type of Lua integer numbers.
|
||||
pub type Integer = ffi::lua_Integer;
|
||||
/// Type of Lua floating point numbers.
|
||||
@@ -63,10 +65,10 @@ pub(crate) type HookCallback = Arc<dyn Fn(&Lua, Debug) -> Result<()> + Send>;
|
||||
pub(crate) type HookCallback = Arc<dyn Fn(&Lua, Debug) -> Result<()>>;
|
||||
|
||||
#[cfg(all(feature = "luau", feature = "send"))]
|
||||
pub(crate) type InterruptCallback = Arc<dyn Fn() -> Result<VmState> + Send>;
|
||||
pub(crate) type InterruptCallback = Arc<dyn Fn(&Lua) -> Result<VmState> + Send>;
|
||||
|
||||
#[cfg(all(feature = "luau", not(feature = "send")))]
|
||||
pub(crate) type InterruptCallback = Arc<dyn Fn() -> Result<VmState>>;
|
||||
pub(crate) type InterruptCallback = Arc<dyn Fn(&Lua) -> Result<VmState>>;
|
||||
|
||||
#[cfg(all(feature = "send", feature = "lua54"))]
|
||||
pub(crate) type WarnCallback = Box<dyn Fn(&Lua, &CStr, bool) -> Result<()> + Send>;
|
||||
@@ -180,6 +182,26 @@ impl RegistryKey {
|
||||
pub(crate) struct LuaRef<'lua> {
|
||||
pub(crate) lua: &'lua Lua,
|
||||
pub(crate) index: c_int,
|
||||
pub(crate) drop: bool,
|
||||
}
|
||||
|
||||
impl<'lua> LuaRef<'lua> {
|
||||
pub(crate) const fn new(lua: &'lua Lua, index: c_int) -> Self {
|
||||
LuaRef {
|
||||
lua,
|
||||
index,
|
||||
drop: true,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "unstable")]
|
||||
#[inline]
|
||||
pub(crate) fn into_owned(self) -> LuaOwnedRef {
|
||||
assert!(self.drop, "Cannot turn non-drop reference into owned");
|
||||
let owned_ref = LuaOwnedRef::new(self.lua.clone(), self.index);
|
||||
mem::forget(self);
|
||||
owned_ref
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> fmt::Debug for LuaRef<'lua> {
|
||||
@@ -196,8 +218,8 @@ impl<'lua> Clone for LuaRef<'lua> {
|
||||
|
||||
impl<'lua> Drop for LuaRef<'lua> {
|
||||
fn drop(&mut self) {
|
||||
if self.index > 0 {
|
||||
self.lua.drop_ref(self);
|
||||
if self.drop {
|
||||
self.lua.drop_ref_index(self.index);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -205,12 +227,72 @@ impl<'lua> Drop for LuaRef<'lua> {
|
||||
impl<'lua> PartialEq for LuaRef<'lua> {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
let lua = self.lua;
|
||||
let state = lua.state();
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(lua.state);
|
||||
assert_stack(lua.state, 2);
|
||||
let _sg = StackGuard::new(state);
|
||||
assert_stack(state, 2);
|
||||
lua.push_ref(self);
|
||||
lua.push_ref(other);
|
||||
ffi::lua_rawequal(lua.state, -1, -2) == 1
|
||||
ffi::lua_rawequal(state, -1, -2) == 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "unstable")]
|
||||
pub(crate) struct LuaOwnedRef {
|
||||
pub(crate) inner: Arc<LuaInner>,
|
||||
pub(crate) index: c_int,
|
||||
_non_send: PhantomData<*const ()>,
|
||||
}
|
||||
|
||||
#[cfg(feature = "unstable")]
|
||||
impl fmt::Debug for LuaOwnedRef {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "OwnedRef({})", self.index)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "unstable")]
|
||||
impl Clone for LuaOwnedRef {
|
||||
fn clone(&self) -> Self {
|
||||
self.to_ref().clone().into_owned()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "unstable")]
|
||||
impl Drop for LuaOwnedRef {
|
||||
fn drop(&mut self) {
|
||||
let lua: &Lua = unsafe { mem::transmute(&self.inner) };
|
||||
lua.drop_ref_index(self.index);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "unstable")]
|
||||
impl LuaOwnedRef {
|
||||
pub(crate) const fn new(inner: Arc<LuaInner>, index: c_int) -> Self {
|
||||
LuaOwnedRef {
|
||||
inner,
|
||||
index,
|
||||
_non_send: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) const fn to_ref(&self) -> LuaRef {
|
||||
LuaRef {
|
||||
lua: unsafe { mem::transmute(&self.inner) },
|
||||
index: self.index,
|
||||
drop: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod assertions {
|
||||
use super::*;
|
||||
|
||||
static_assertions::assert_impl_all!(RegistryKey: Send, Sync);
|
||||
static_assertions::assert_not_impl_any!(LuaRef: Send);
|
||||
|
||||
#[cfg(feature = "unstable")]
|
||||
static_assertions::assert_not_impl_any!(LuaOwnedRef: Send);
|
||||
}
|
||||
|
||||
+361
-287
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,205 @@
|
||||
use crate::error::{Error, Result};
|
||||
use crate::private::Sealed;
|
||||
use crate::userdata::{AnyUserData, MetaMethod};
|
||||
use crate::value::{FromLua, FromLuaMulti, IntoLua, IntoLuaMulti, Value};
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
use {futures_core::future::LocalBoxFuture, futures_util::future};
|
||||
|
||||
/// An extension trait for [`AnyUserData`] that provides a variety of convenient functionality.
|
||||
pub trait AnyUserDataExt<'lua>: Sealed {
|
||||
/// Gets the value associated to `key` from the userdata, assuming it has `__index` metamethod.
|
||||
fn get<K: IntoLua<'lua>, V: FromLua<'lua>>(&self, key: K) -> Result<V>;
|
||||
|
||||
/// Sets the value associated to `key` in the userdata, assuming it has `__newindex` metamethod.
|
||||
fn set<K: IntoLua<'lua>, V: IntoLua<'lua>>(&self, key: K, value: V) -> Result<()>;
|
||||
|
||||
/// Calls the userdata as a function assuming it has `__call` metamethod.
|
||||
///
|
||||
/// The metamethod is called with the userdata as its first argument, followed by the passed arguments.
|
||||
fn call<A, R>(&self, args: A) -> Result<R>
|
||||
where
|
||||
A: IntoLuaMulti<'lua>,
|
||||
R: FromLuaMulti<'lua>;
|
||||
|
||||
/// Asynchronously calls the userdata as a function assuming it has `__call` metamethod.
|
||||
///
|
||||
/// The metamethod is called with the userdata as its first argument, followed by the passed arguments.
|
||||
#[cfg(feature = "async")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
|
||||
fn call_async<'fut, A, R>(&self, args: A) -> LocalBoxFuture<'fut, Result<R>>
|
||||
where
|
||||
'lua: 'fut,
|
||||
A: IntoLuaMulti<'lua>,
|
||||
R: FromLuaMulti<'lua> + 'fut;
|
||||
|
||||
/// Calls the userdata method, assuming it has `__index` metamethod
|
||||
/// and a function associated to `name`.
|
||||
fn call_method<A, R>(&self, name: impl AsRef<str>, args: A) -> Result<R>
|
||||
where
|
||||
A: IntoLuaMulti<'lua>,
|
||||
R: FromLuaMulti<'lua>;
|
||||
|
||||
/// Gets the function associated to `key` from the table and asynchronously executes it,
|
||||
/// passing the table itself along with `args` as function arguments and returning Future.
|
||||
///
|
||||
/// Requires `feature = "async"`
|
||||
///
|
||||
/// This might invoke the `__index` metamethod.
|
||||
#[cfg(feature = "async")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
|
||||
fn call_async_method<'fut, A, R>(
|
||||
&self,
|
||||
name: impl AsRef<str>,
|
||||
args: A,
|
||||
) -> LocalBoxFuture<'fut, Result<R>>
|
||||
where
|
||||
'lua: 'fut,
|
||||
A: IntoLuaMulti<'lua>,
|
||||
R: FromLuaMulti<'lua> + 'fut;
|
||||
|
||||
/// Gets the function associated to `key` from the table and executes it,
|
||||
/// passing `args` as function arguments.
|
||||
///
|
||||
/// This is a shortcut for
|
||||
/// `table.get::<_, Function>(key)?.call(args)`
|
||||
///
|
||||
/// This might invoke the `__index` metamethod.
|
||||
fn call_function<A, R>(&self, name: impl AsRef<str>, args: A) -> Result<R>
|
||||
where
|
||||
A: IntoLuaMulti<'lua>,
|
||||
R: FromLuaMulti<'lua>;
|
||||
|
||||
/// Gets the function associated to `key` from the table and asynchronously executes it,
|
||||
/// passing `args` as function arguments and returning Future.
|
||||
///
|
||||
/// Requires `feature = "async"`
|
||||
///
|
||||
/// This might invoke the `__index` metamethod.
|
||||
#[cfg(feature = "async")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
|
||||
fn call_async_function<'fut, A, R>(
|
||||
&self,
|
||||
name: impl AsRef<str>,
|
||||
args: A,
|
||||
) -> LocalBoxFuture<'fut, Result<R>>
|
||||
where
|
||||
'lua: 'fut,
|
||||
A: IntoLuaMulti<'lua>,
|
||||
R: FromLuaMulti<'lua> + 'fut;
|
||||
}
|
||||
|
||||
impl<'lua> AnyUserDataExt<'lua> for AnyUserData<'lua> {
|
||||
fn get<K: IntoLua<'lua>, V: FromLua<'lua>>(&self, key: K) -> Result<V> {
|
||||
let metatable = self.get_metatable()?;
|
||||
match metatable.get::<Value>(MetaMethod::Index)? {
|
||||
Value::Table(table) => table.raw_get(key),
|
||||
Value::Function(func) => func.call((self.clone(), key)),
|
||||
_ => Err(Error::RuntimeError(
|
||||
"attempt to index a userdata value".to_string(),
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
fn set<K: IntoLua<'lua>, V: IntoLua<'lua>>(&self, key: K, value: V) -> Result<()> {
|
||||
let metatable = self.get_metatable()?;
|
||||
match metatable.get::<Value>(MetaMethod::NewIndex)? {
|
||||
Value::Table(table) => table.raw_set(key, value),
|
||||
Value::Function(func) => func.call((self.clone(), key, value)),
|
||||
_ => Err(Error::RuntimeError(
|
||||
"attempt to index a userdata value".to_string(),
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
fn call<A, R>(&self, args: A) -> Result<R>
|
||||
where
|
||||
A: IntoLuaMulti<'lua>,
|
||||
R: FromLuaMulti<'lua>,
|
||||
{
|
||||
let metatable = self.get_metatable()?;
|
||||
match metatable.get::<Value>(MetaMethod::Call)? {
|
||||
Value::Function(func) => func.call((self.clone(), args)),
|
||||
_ => Err(Error::RuntimeError(
|
||||
"attempt to call a userdata value".to_string(),
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
fn call_async<'fut, A, R>(&self, args: A) -> LocalBoxFuture<'fut, Result<R>>
|
||||
where
|
||||
'lua: 'fut,
|
||||
A: IntoLuaMulti<'lua>,
|
||||
R: FromLuaMulti<'lua> + 'fut,
|
||||
{
|
||||
let metatable = match self.get_metatable() {
|
||||
Ok(metatable) => metatable,
|
||||
Err(err) => return Box::pin(future::err(err)),
|
||||
};
|
||||
match metatable.get::<Value>(MetaMethod::Call) {
|
||||
Ok(Value::Function(func)) => func.call_async((self.clone(), args)),
|
||||
Ok(_) => Box::pin(future::err(Error::RuntimeError(
|
||||
"attempt to call a userdata value".to_string(),
|
||||
))),
|
||||
Err(err) => Box::pin(future::err(err)),
|
||||
}
|
||||
}
|
||||
|
||||
fn call_method<A, R>(&self, name: impl AsRef<str>, args: A) -> Result<R>
|
||||
where
|
||||
A: IntoLuaMulti<'lua>,
|
||||
R: FromLuaMulti<'lua>,
|
||||
{
|
||||
self.call_function(name, (self.clone(), args))
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
fn call_async_method<'fut, A, R>(
|
||||
&self,
|
||||
name: impl AsRef<str>,
|
||||
args: A,
|
||||
) -> LocalBoxFuture<'fut, Result<R>>
|
||||
where
|
||||
'lua: 'fut,
|
||||
A: IntoLuaMulti<'lua>,
|
||||
R: FromLuaMulti<'lua> + 'fut,
|
||||
{
|
||||
self.call_async_function(name, (self.clone(), args))
|
||||
}
|
||||
|
||||
fn call_function<A, R>(&self, name: impl AsRef<str>, args: A) -> Result<R>
|
||||
where
|
||||
A: IntoLuaMulti<'lua>,
|
||||
R: FromLuaMulti<'lua>,
|
||||
{
|
||||
match self.get(name.as_ref())? {
|
||||
Value::Function(func) => func.call(args),
|
||||
val => Err(Error::RuntimeError(format!(
|
||||
"attempt to call a {} value",
|
||||
val.type_name()
|
||||
))),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
fn call_async_function<'fut, A, R>(
|
||||
&self,
|
||||
name: impl AsRef<str>,
|
||||
args: A,
|
||||
) -> LocalBoxFuture<'fut, Result<R>>
|
||||
where
|
||||
'lua: 'fut,
|
||||
A: IntoLuaMulti<'lua>,
|
||||
R: FromLuaMulti<'lua> + 'fut,
|
||||
{
|
||||
match self.get(name.as_ref()) {
|
||||
Ok(Value::Function(func)) => func.call_async(args),
|
||||
Ok(val) => Box::pin(future::err(Error::RuntimeError(format!(
|
||||
"attempt to call a {} value",
|
||||
val.type_name()
|
||||
)))),
|
||||
Err(err) => Box::pin(future::err(err)),
|
||||
}
|
||||
}
|
||||
}
|
||||
+406
-363
@@ -1,17 +1,17 @@
|
||||
use std::any::TypeId;
|
||||
use std::any::{self, TypeId};
|
||||
use std::cell::{Ref, RefCell, RefMut};
|
||||
use std::marker::PhantomData;
|
||||
use std::string::String as StdString;
|
||||
use std::sync::{Arc, Mutex, RwLock};
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
use crate::ffi;
|
||||
use crate::lua::Lua;
|
||||
use crate::types::{Callback, MaybeSend};
|
||||
use crate::userdata::{
|
||||
AnyUserData, MetaMethod, UserData, UserDataCell, UserDataFields, UserDataMethods,
|
||||
};
|
||||
use crate::util::{check_stack, get_userdata, StackGuard};
|
||||
use crate::value::{FromLua, FromLuaMulti, ToLua, ToLuaMulti, Value};
|
||||
use crate::value::{FromLua, FromLuaMulti, IntoLua, IntoLuaMulti, Value};
|
||||
|
||||
#[cfg(not(feature = "send"))]
|
||||
use std::rc::Rc;
|
||||
@@ -19,23 +19,37 @@ use std::rc::Rc;
|
||||
#[cfg(feature = "async")]
|
||||
use {
|
||||
crate::types::AsyncCallback,
|
||||
futures_core::future::Future,
|
||||
futures_util::future::{self, TryFutureExt},
|
||||
std::future::Future,
|
||||
};
|
||||
|
||||
pub(crate) struct StaticUserDataMethods<'lua, T: 'static + UserData> {
|
||||
pub(crate) methods: Vec<(Vec<u8>, Callback<'lua, 'static>)>,
|
||||
pub struct UserDataRegistrar<'lua, T: 'static> {
|
||||
// Fields
|
||||
pub(crate) field_getters: Vec<(String, Callback<'lua, 'static>)>,
|
||||
pub(crate) field_setters: Vec<(String, Callback<'lua, 'static>)>,
|
||||
#[allow(clippy::type_complexity)]
|
||||
pub(crate) meta_fields: Vec<(
|
||||
String,
|
||||
Box<dyn Fn(&'lua Lua) -> Result<Value<'lua>> + 'static>,
|
||||
)>,
|
||||
|
||||
// Methods
|
||||
pub(crate) methods: Vec<(String, Callback<'lua, 'static>)>,
|
||||
#[cfg(feature = "async")]
|
||||
pub(crate) async_methods: Vec<(Vec<u8>, AsyncCallback<'lua, 'static>)>,
|
||||
pub(crate) meta_methods: Vec<(MetaMethod, Callback<'lua, 'static>)>,
|
||||
pub(crate) async_methods: Vec<(String, AsyncCallback<'lua, 'static>)>,
|
||||
pub(crate) meta_methods: Vec<(String, Callback<'lua, 'static>)>,
|
||||
#[cfg(feature = "async")]
|
||||
pub(crate) async_meta_methods: Vec<(MetaMethod, AsyncCallback<'lua, 'static>)>,
|
||||
pub(crate) async_meta_methods: Vec<(String, AsyncCallback<'lua, 'static>)>,
|
||||
|
||||
_type: PhantomData<T>,
|
||||
}
|
||||
|
||||
impl<'lua, T: 'static + UserData> Default for StaticUserDataMethods<'lua, T> {
|
||||
fn default() -> StaticUserDataMethods<'lua, T> {
|
||||
StaticUserDataMethods {
|
||||
impl<'lua, T: 'static> UserDataRegistrar<'lua, T> {
|
||||
pub(crate) const fn new() -> Self {
|
||||
UserDataRegistrar {
|
||||
field_getters: Vec::new(),
|
||||
field_setters: Vec::new(),
|
||||
meta_fields: Vec::new(),
|
||||
methods: Vec::new(),
|
||||
#[cfg(feature = "async")]
|
||||
async_methods: Vec::new(),
|
||||
@@ -45,507 +59,372 @@ impl<'lua, T: 'static + UserData> Default for StaticUserDataMethods<'lua, T> {
|
||||
_type: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua, T: 'static + UserData> UserDataMethods<'lua, T> for StaticUserDataMethods<'lua, T> {
|
||||
fn add_method<S, A, R, M>(&mut self, name: &S, method: M)
|
||||
fn box_method<M, A, R>(name: &str, method: M) -> Callback<'lua, 'static>
|
||||
where
|
||||
S: AsRef<[u8]> + ?Sized,
|
||||
M: Fn(&'lua Lua, &T, A) -> Result<R> + MaybeSend + 'static,
|
||||
A: FromLuaMulti<'lua>,
|
||||
R: ToLuaMulti<'lua>,
|
||||
M: 'static + MaybeSend + Fn(&'lua Lua, &T, A) -> Result<R>,
|
||||
R: IntoLuaMulti<'lua>,
|
||||
{
|
||||
self.methods
|
||||
.push((name.as_ref().to_vec(), Self::box_method(method)));
|
||||
}
|
||||
let name = get_function_name::<T>(name);
|
||||
macro_rules! try_self_arg {
|
||||
($res:expr) => {
|
||||
$res.map_err(|err| Error::bad_self_argument(&name, err))?
|
||||
};
|
||||
($res:expr, $err:expr) => {
|
||||
$res.map_err(|_| Error::bad_self_argument(&name, $err))?
|
||||
};
|
||||
}
|
||||
|
||||
fn add_method_mut<S, A, R, M>(&mut self, name: &S, method: M)
|
||||
where
|
||||
S: AsRef<[u8]> + ?Sized,
|
||||
A: FromLuaMulti<'lua>,
|
||||
R: ToLuaMulti<'lua>,
|
||||
M: 'static + MaybeSend + FnMut(&'lua Lua, &mut T, A) -> Result<R>,
|
||||
{
|
||||
self.methods
|
||||
.push((name.as_ref().to_vec(), Self::box_method_mut(method)));
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
fn add_async_method<S, A, R, M, MR>(&mut self, name: &S, method: M)
|
||||
where
|
||||
T: Clone,
|
||||
S: AsRef<[u8]> + ?Sized,
|
||||
A: FromLuaMulti<'lua>,
|
||||
R: ToLuaMulti<'lua>,
|
||||
M: 'static + MaybeSend + Fn(&'lua Lua, T, A) -> MR,
|
||||
MR: 'lua + Future<Output = Result<R>>,
|
||||
{
|
||||
self.async_methods
|
||||
.push((name.as_ref().to_vec(), Self::box_async_method(method)));
|
||||
}
|
||||
|
||||
fn add_function<S, A, R, F>(&mut self, name: &S, function: F)
|
||||
where
|
||||
S: AsRef<[u8]> + ?Sized,
|
||||
A: FromLuaMulti<'lua>,
|
||||
R: ToLuaMulti<'lua>,
|
||||
F: 'static + MaybeSend + Fn(&'lua Lua, A) -> Result<R>,
|
||||
{
|
||||
self.methods
|
||||
.push((name.as_ref().to_vec(), Self::box_function(function)));
|
||||
}
|
||||
|
||||
fn add_function_mut<S, A, R, F>(&mut self, name: &S, function: F)
|
||||
where
|
||||
S: AsRef<[u8]> + ?Sized,
|
||||
A: FromLuaMulti<'lua>,
|
||||
R: ToLuaMulti<'lua>,
|
||||
F: 'static + MaybeSend + FnMut(&'lua Lua, A) -> Result<R>,
|
||||
{
|
||||
self.methods
|
||||
.push((name.as_ref().to_vec(), Self::box_function_mut(function)));
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
fn add_async_function<S, A, R, F, FR>(&mut self, name: &S, function: F)
|
||||
where
|
||||
S: AsRef<[u8]> + ?Sized,
|
||||
A: FromLuaMulti<'lua>,
|
||||
R: ToLuaMulti<'lua>,
|
||||
F: 'static + MaybeSend + Fn(&'lua Lua, A) -> FR,
|
||||
FR: 'lua + Future<Output = Result<R>>,
|
||||
{
|
||||
self.async_methods
|
||||
.push((name.as_ref().to_vec(), Self::box_async_function(function)));
|
||||
}
|
||||
|
||||
fn add_meta_method<S, A, R, M>(&mut self, meta: S, method: M)
|
||||
where
|
||||
S: Into<MetaMethod>,
|
||||
A: FromLuaMulti<'lua>,
|
||||
R: ToLuaMulti<'lua>,
|
||||
M: 'static + MaybeSend + Fn(&'lua Lua, &T, A) -> Result<R>,
|
||||
{
|
||||
self.meta_methods
|
||||
.push((meta.into(), Self::box_method(method)));
|
||||
}
|
||||
|
||||
fn add_meta_method_mut<S, A, R, M>(&mut self, meta: S, method: M)
|
||||
where
|
||||
S: Into<MetaMethod>,
|
||||
A: FromLuaMulti<'lua>,
|
||||
R: ToLuaMulti<'lua>,
|
||||
M: 'static + MaybeSend + FnMut(&'lua Lua, &mut T, A) -> Result<R>,
|
||||
{
|
||||
self.meta_methods
|
||||
.push((meta.into(), Self::box_method_mut(method)));
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "async", not(any(feature = "lua51", feature = "luau"))))]
|
||||
fn add_async_meta_method<S, A, R, M, MR>(&mut self, meta: S, method: M)
|
||||
where
|
||||
T: Clone,
|
||||
S: Into<MetaMethod>,
|
||||
A: FromLuaMulti<'lua>,
|
||||
R: ToLuaMulti<'lua>,
|
||||
M: 'static + MaybeSend + Fn(&'lua Lua, T, A) -> MR,
|
||||
MR: 'lua + Future<Output = Result<R>>,
|
||||
{
|
||||
self.async_meta_methods
|
||||
.push((meta.into(), Self::box_async_method(method)));
|
||||
}
|
||||
|
||||
fn add_meta_function<S, A, R, F>(&mut self, meta: S, function: F)
|
||||
where
|
||||
S: Into<MetaMethod>,
|
||||
A: FromLuaMulti<'lua>,
|
||||
R: ToLuaMulti<'lua>,
|
||||
F: 'static + MaybeSend + Fn(&'lua Lua, A) -> Result<R>,
|
||||
{
|
||||
self.meta_methods
|
||||
.push((meta.into(), Self::box_function(function)));
|
||||
}
|
||||
|
||||
fn add_meta_function_mut<S, A, R, F>(&mut self, meta: S, function: F)
|
||||
where
|
||||
S: Into<MetaMethod>,
|
||||
A: FromLuaMulti<'lua>,
|
||||
R: ToLuaMulti<'lua>,
|
||||
F: 'static + MaybeSend + FnMut(&'lua Lua, A) -> Result<R>,
|
||||
{
|
||||
self.meta_methods
|
||||
.push((meta.into(), Self::box_function_mut(function)));
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "async", not(any(feature = "lua51", feature = "luau"))))]
|
||||
fn add_async_meta_function<S, A, R, F, FR>(&mut self, meta: S, function: F)
|
||||
where
|
||||
S: Into<MetaMethod>,
|
||||
A: FromLuaMulti<'lua>,
|
||||
R: ToLuaMulti<'lua>,
|
||||
F: 'static + MaybeSend + Fn(&'lua Lua, A) -> FR,
|
||||
FR: 'lua + Future<Output = Result<R>>,
|
||||
{
|
||||
self.async_meta_methods
|
||||
.push((meta.into(), Self::box_async_function(function)));
|
||||
}
|
||||
|
||||
// Below are internal methods used in generated code
|
||||
|
||||
fn add_callback(&mut self, name: Vec<u8>, callback: Callback<'lua, 'static>) {
|
||||
self.methods.push((name, callback));
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
fn add_async_callback(&mut self, name: Vec<u8>, callback: AsyncCallback<'lua, 'static>) {
|
||||
self.async_methods.push((name, callback));
|
||||
}
|
||||
|
||||
fn add_meta_callback(&mut self, meta: MetaMethod, callback: Callback<'lua, 'static>) {
|
||||
self.meta_methods.push((meta, callback));
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
fn add_async_meta_callback(
|
||||
&mut self,
|
||||
meta: MetaMethod,
|
||||
callback: AsyncCallback<'lua, 'static>,
|
||||
) {
|
||||
self.async_meta_methods.push((meta, callback))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua, T: 'static + UserData> StaticUserDataMethods<'lua, T> {
|
||||
fn box_method<A, R, M>(method: M) -> Callback<'lua, 'static>
|
||||
where
|
||||
A: FromLuaMulti<'lua>,
|
||||
R: ToLuaMulti<'lua>,
|
||||
M: 'static + MaybeSend + Fn(&'lua Lua, &T, A) -> Result<R>,
|
||||
{
|
||||
Box::new(move |lua, mut args| {
|
||||
if let Some(front) = args.pop_front() {
|
||||
let userdata = AnyUserData::from_lua(front, lua)?;
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(lua.state);
|
||||
check_stack(lua.state, 2)?;
|
||||
let front = args.pop_front();
|
||||
let call = |ud| {
|
||||
// Self was at index 1, so we pass 2 here
|
||||
let args = A::from_lua_multi_args(args, 2, Some(&name), lua)?;
|
||||
method(lua, ud, args)?.into_lua_multi(lua)
|
||||
};
|
||||
|
||||
let type_id = lua.push_userdata_ref(&userdata.0)?;
|
||||
if let Some(front) = front {
|
||||
let state = lua.state();
|
||||
let userdata = try_self_arg!(AnyUserData::from_lua(front, lua));
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(state);
|
||||
check_stack(state, 2)?;
|
||||
|
||||
let type_id = try_self_arg!(lua.push_userdata_ref(&userdata.0));
|
||||
match type_id {
|
||||
Some(id) if id == TypeId::of::<T>() => {
|
||||
let ud = get_userdata_ref::<T>(lua.state)?;
|
||||
method(lua, &ud, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
|
||||
let ud = try_self_arg!(get_userdata_ref::<T>(state));
|
||||
call(&ud)
|
||||
}
|
||||
#[cfg(not(feature = "send"))]
|
||||
Some(id) if id == TypeId::of::<Rc<RefCell<T>>>() => {
|
||||
let ud = get_userdata_ref::<Rc<RefCell<T>>>(lua.state)?;
|
||||
let ud = ud.try_borrow().map_err(|_| Error::UserDataBorrowError)?;
|
||||
method(lua, &ud, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
|
||||
let ud = try_self_arg!(get_userdata_ref::<Rc<RefCell<T>>>(state));
|
||||
let ud = try_self_arg!(ud.try_borrow(), Error::UserDataBorrowError);
|
||||
call(&ud)
|
||||
}
|
||||
Some(id) if id == TypeId::of::<Arc<Mutex<T>>>() => {
|
||||
let ud = get_userdata_ref::<Arc<Mutex<T>>>(lua.state)?;
|
||||
let ud = ud.try_lock().map_err(|_| Error::UserDataBorrowError)?;
|
||||
method(lua, &ud, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
|
||||
let ud = try_self_arg!(get_userdata_ref::<Arc<Mutex<T>>>(state));
|
||||
let ud = try_self_arg!(ud.try_lock(), Error::UserDataBorrowError);
|
||||
call(&ud)
|
||||
}
|
||||
#[cfg(feature = "parking_lot")]
|
||||
Some(id) if id == TypeId::of::<Arc<parking_lot::Mutex<T>>>() => {
|
||||
let ud = get_userdata_ref::<Arc<parking_lot::Mutex<T>>>(lua.state)?;
|
||||
let ud = ud.try_lock().ok_or(Error::UserDataBorrowError)?;
|
||||
method(lua, &ud, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
|
||||
let ud = get_userdata_ref::<Arc<parking_lot::Mutex<T>>>(state);
|
||||
let ud = try_self_arg!(ud);
|
||||
let ud = try_self_arg!(ud.try_lock().ok_or(Error::UserDataBorrowError));
|
||||
call(&ud)
|
||||
}
|
||||
Some(id) if id == TypeId::of::<Arc<RwLock<T>>>() => {
|
||||
let ud = get_userdata_ref::<Arc<RwLock<T>>>(lua.state)?;
|
||||
let ud = ud.try_read().map_err(|_| Error::UserDataBorrowError)?;
|
||||
method(lua, &ud, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
|
||||
let ud = try_self_arg!(get_userdata_ref::<Arc<RwLock<T>>>(state));
|
||||
let ud = try_self_arg!(ud.try_read(), Error::UserDataBorrowError);
|
||||
call(&ud)
|
||||
}
|
||||
#[cfg(feature = "parking_lot")]
|
||||
Some(id) if id == TypeId::of::<Arc<parking_lot::RwLock<T>>>() => {
|
||||
let ud = get_userdata_ref::<Arc<parking_lot::RwLock<T>>>(lua.state)?;
|
||||
let ud = ud.try_read().ok_or(Error::UserDataBorrowError)?;
|
||||
method(lua, &ud, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
|
||||
let ud = get_userdata_ref::<Arc<parking_lot::RwLock<T>>>(state);
|
||||
let ud = try_self_arg!(ud);
|
||||
let ud = try_self_arg!(ud.try_read().ok_or(Error::UserDataBorrowError));
|
||||
call(&ud)
|
||||
}
|
||||
_ => Err(Error::UserDataTypeMismatch),
|
||||
_ => Err(Error::bad_self_argument(&name, Error::UserDataTypeMismatch)),
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Err(Error::FromLuaConversionError {
|
||||
from: "missing argument",
|
||||
to: "userdata",
|
||||
message: None,
|
||||
})
|
||||
let err = Error::from_lua_conversion("missing argument", "userdata", None);
|
||||
Err(Error::bad_self_argument(&name, err))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn box_method_mut<A, R, M>(method: M) -> Callback<'lua, 'static>
|
||||
fn box_method_mut<M, A, R>(name: &str, method: M) -> Callback<'lua, 'static>
|
||||
where
|
||||
M: FnMut(&'lua Lua, &mut T, A) -> Result<R> + MaybeSend + 'static,
|
||||
A: FromLuaMulti<'lua>,
|
||||
R: ToLuaMulti<'lua>,
|
||||
M: 'static + MaybeSend + FnMut(&'lua Lua, &mut T, A) -> Result<R>,
|
||||
R: IntoLuaMulti<'lua>,
|
||||
{
|
||||
let name = get_function_name::<T>(name);
|
||||
macro_rules! try_self_arg {
|
||||
($res:expr) => {
|
||||
$res.map_err(|err| Error::bad_self_argument(&name, err))?
|
||||
};
|
||||
($res:expr, $err:expr) => {
|
||||
$res.map_err(|_| Error::bad_self_argument(&name, $err))?
|
||||
};
|
||||
}
|
||||
|
||||
let method = RefCell::new(method);
|
||||
Box::new(move |lua, mut args| {
|
||||
if let Some(front) = args.pop_front() {
|
||||
let userdata = AnyUserData::from_lua(front, lua)?;
|
||||
let mut method = method
|
||||
.try_borrow_mut()
|
||||
.map_err(|_| Error::RecursiveMutCallback)?;
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(lua.state);
|
||||
check_stack(lua.state, 2)?;
|
||||
let mut method = method
|
||||
.try_borrow_mut()
|
||||
.map_err(|_| Error::RecursiveMutCallback)?;
|
||||
let front = args.pop_front();
|
||||
let call = |ud| {
|
||||
// Self was at index 1, so we pass 2 here
|
||||
let args = A::from_lua_multi_args(args, 2, Some(&name), lua)?;
|
||||
method(lua, ud, args)?.into_lua_multi(lua)
|
||||
};
|
||||
|
||||
let type_id = lua.push_userdata_ref(&userdata.0)?;
|
||||
if let Some(front) = front {
|
||||
let state = lua.state();
|
||||
let userdata = try_self_arg!(AnyUserData::from_lua(front, lua));
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(state);
|
||||
check_stack(state, 2)?;
|
||||
|
||||
let type_id = try_self_arg!(lua.push_userdata_ref(&userdata.0));
|
||||
match type_id {
|
||||
Some(id) if id == TypeId::of::<T>() => {
|
||||
let mut ud = get_userdata_mut::<T>(lua.state)?;
|
||||
method(lua, &mut ud, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
|
||||
let mut ud = try_self_arg!(get_userdata_mut::<T>(state));
|
||||
call(&mut ud)
|
||||
}
|
||||
#[cfg(not(feature = "send"))]
|
||||
Some(id) if id == TypeId::of::<Rc<RefCell<T>>>() => {
|
||||
let ud = get_userdata_mut::<Rc<RefCell<T>>>(lua.state)?;
|
||||
let mut ud = ud
|
||||
.try_borrow_mut()
|
||||
.map_err(|_| Error::UserDataBorrowMutError)?;
|
||||
method(lua, &mut ud, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
|
||||
let ud = try_self_arg!(get_userdata_mut::<Rc<RefCell<T>>>(state));
|
||||
let mut ud =
|
||||
try_self_arg!(ud.try_borrow_mut(), Error::UserDataBorrowMutError);
|
||||
call(&mut ud)
|
||||
}
|
||||
Some(id) if id == TypeId::of::<Arc<Mutex<T>>>() => {
|
||||
let ud = get_userdata_mut::<Arc<Mutex<T>>>(lua.state)?;
|
||||
let ud = try_self_arg!(get_userdata_mut::<Arc<Mutex<T>>>(state));
|
||||
let mut ud =
|
||||
ud.try_lock().map_err(|_| Error::UserDataBorrowMutError)?;
|
||||
method(lua, &mut ud, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
|
||||
try_self_arg!(ud.try_lock(), Error::UserDataBorrowMutError);
|
||||
call(&mut ud)
|
||||
}
|
||||
#[cfg(feature = "parking_lot")]
|
||||
Some(id) if id == TypeId::of::<Arc<parking_lot::Mutex<T>>>() => {
|
||||
let ud = get_userdata_mut::<Arc<parking_lot::Mutex<T>>>(lua.state)?;
|
||||
let mut ud = ud.try_lock().ok_or(Error::UserDataBorrowMutError)?;
|
||||
method(lua, &mut ud, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
|
||||
let ud = get_userdata_mut::<Arc<parking_lot::Mutex<T>>>(state);
|
||||
let ud = try_self_arg!(ud);
|
||||
let mut ud =
|
||||
try_self_arg!(ud.try_lock().ok_or(Error::UserDataBorrowMutError));
|
||||
call(&mut ud)
|
||||
}
|
||||
Some(id) if id == TypeId::of::<Arc<RwLock<T>>>() => {
|
||||
let ud = get_userdata_mut::<Arc<RwLock<T>>>(lua.state)?;
|
||||
let ud = try_self_arg!(get_userdata_mut::<Arc<RwLock<T>>>(state));
|
||||
let mut ud =
|
||||
ud.try_write().map_err(|_| Error::UserDataBorrowMutError)?;
|
||||
method(lua, &mut ud, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
|
||||
try_self_arg!(ud.try_write(), Error::UserDataBorrowMutError);
|
||||
call(&mut ud)
|
||||
}
|
||||
#[cfg(feature = "parking_lot")]
|
||||
Some(id) if id == TypeId::of::<Arc<parking_lot::RwLock<T>>>() => {
|
||||
let ud = get_userdata_mut::<Arc<parking_lot::RwLock<T>>>(lua.state)?;
|
||||
let mut ud = ud.try_write().ok_or(Error::UserDataBorrowMutError)?;
|
||||
method(lua, &mut ud, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
|
||||
let ud = get_userdata_mut::<Arc<parking_lot::RwLock<T>>>(state);
|
||||
let ud = try_self_arg!(ud);
|
||||
let mut ud =
|
||||
try_self_arg!(ud.try_write().ok_or(Error::UserDataBorrowMutError));
|
||||
call(&mut ud)
|
||||
}
|
||||
_ => Err(Error::UserDataTypeMismatch),
|
||||
_ => Err(Error::bad_self_argument(&name, Error::UserDataTypeMismatch)),
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Err(Error::FromLuaConversionError {
|
||||
from: "missing argument",
|
||||
to: "userdata",
|
||||
message: None,
|
||||
})
|
||||
let err = Error::from_lua_conversion("missing argument", "userdata", None);
|
||||
Err(Error::bad_self_argument(&name, err))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
fn box_async_method<A, R, M, MR>(method: M) -> AsyncCallback<'lua, 'static>
|
||||
fn box_async_method<M, A, MR, R>(name: &str, method: M) -> AsyncCallback<'lua, 'static>
|
||||
where
|
||||
T: Clone,
|
||||
M: Fn(&'lua Lua, T, A) -> MR + MaybeSend + 'static,
|
||||
A: FromLuaMulti<'lua>,
|
||||
R: ToLuaMulti<'lua>,
|
||||
M: 'static + MaybeSend + Fn(&'lua Lua, T, A) -> MR,
|
||||
MR: 'lua + Future<Output = Result<R>>,
|
||||
MR: Future<Output = Result<R>> + 'lua,
|
||||
R: IntoLuaMulti<'lua>,
|
||||
{
|
||||
let name = get_function_name::<T>(name);
|
||||
macro_rules! try_self_arg {
|
||||
($res:expr) => {
|
||||
$res.map_err(|err| Error::bad_self_argument(&name, err))?
|
||||
};
|
||||
($res:expr, $err:expr) => {
|
||||
$res.map_err(|_| Error::bad_self_argument(&name, $err))?
|
||||
};
|
||||
}
|
||||
|
||||
Box::new(move |lua, mut args| {
|
||||
let front = args.pop_front();
|
||||
let call = |ud| {
|
||||
// Self was at index 1, so we pass 2 here
|
||||
let args = A::from_lua_multi_args(args, 2, Some(&name), lua)?;
|
||||
Ok(method(lua, ud, args))
|
||||
};
|
||||
|
||||
let fut_res = || {
|
||||
if let Some(front) = args.pop_front() {
|
||||
if let Some(front) = front {
|
||||
let state = lua.state();
|
||||
let userdata = AnyUserData::from_lua(front, lua)?;
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(lua.state);
|
||||
check_stack(lua.state, 2)?;
|
||||
let _sg = StackGuard::new(state);
|
||||
check_stack(state, 2)?;
|
||||
|
||||
let type_id = lua.push_userdata_ref(&userdata.0)?;
|
||||
let type_id = try_self_arg!(lua.push_userdata_ref(&userdata.0));
|
||||
match type_id {
|
||||
Some(id) if id == TypeId::of::<T>() => {
|
||||
let ud = get_userdata_ref::<T>(lua.state)?;
|
||||
Ok(method(lua, ud.clone(), A::from_lua_multi(args, lua)?))
|
||||
let ud = get_userdata_ref::<T>(state)?;
|
||||
call(ud.clone())
|
||||
}
|
||||
#[cfg(not(feature = "send"))]
|
||||
Some(id) if id == TypeId::of::<Rc<RefCell<T>>>() => {
|
||||
let ud = get_userdata_ref::<Rc<RefCell<T>>>(lua.state)?;
|
||||
let ud = ud.try_borrow().map_err(|_| Error::UserDataBorrowError)?;
|
||||
Ok(method(lua, ud.clone(), A::from_lua_multi(args, lua)?))
|
||||
let ud = try_self_arg!(get_userdata_ref::<Rc<RefCell<T>>>(state));
|
||||
let ud = try_self_arg!(ud.try_borrow(), Error::UserDataBorrowError);
|
||||
call(ud.clone())
|
||||
}
|
||||
Some(id) if id == TypeId::of::<Arc<Mutex<T>>>() => {
|
||||
let ud = get_userdata_ref::<Arc<Mutex<T>>>(lua.state)?;
|
||||
let ud = ud.try_lock().map_err(|_| Error::UserDataBorrowError)?;
|
||||
Ok(method(lua, ud.clone(), A::from_lua_multi(args, lua)?))
|
||||
let ud = try_self_arg!(get_userdata_ref::<Arc<Mutex<T>>>(state));
|
||||
let ud = try_self_arg!(ud.try_lock(), Error::UserDataBorrowError);
|
||||
call(ud.clone())
|
||||
}
|
||||
#[cfg(feature = "parking_lot")]
|
||||
Some(id) if id == TypeId::of::<Arc<parking_lot::Mutex<T>>>() => {
|
||||
let ud = get_userdata_ref::<Arc<parking_lot::Mutex<T>>>(lua.state)?;
|
||||
let ud = ud.try_lock().ok_or(Error::UserDataBorrowError)?;
|
||||
Ok(method(lua, ud.clone(), A::from_lua_multi(args, lua)?))
|
||||
let ud = get_userdata_ref::<Arc<parking_lot::Mutex<T>>>(state);
|
||||
let ud = try_self_arg!(ud);
|
||||
let ud =
|
||||
try_self_arg!(ud.try_lock().ok_or(Error::UserDataBorrowError));
|
||||
call(ud.clone())
|
||||
}
|
||||
Some(id) if id == TypeId::of::<Arc<RwLock<T>>>() => {
|
||||
let ud = get_userdata_ref::<Arc<RwLock<T>>>(lua.state)?;
|
||||
let ud = ud.try_read().map_err(|_| Error::UserDataBorrowError)?;
|
||||
Ok(method(lua, ud.clone(), A::from_lua_multi(args, lua)?))
|
||||
let ud = try_self_arg!(get_userdata_ref::<Arc<RwLock<T>>>(state));
|
||||
let ud = try_self_arg!(ud.try_read(), Error::UserDataBorrowError);
|
||||
call(ud.clone())
|
||||
}
|
||||
#[cfg(feature = "parking_lot")]
|
||||
Some(id) if id == TypeId::of::<Arc<parking_lot::RwLock<T>>>() => {
|
||||
let ud = get_userdata_ref::<Arc<parking_lot::RwLock<T>>>(state);
|
||||
let ud = try_self_arg!(ud);
|
||||
let ud =
|
||||
get_userdata_ref::<Arc<parking_lot::RwLock<T>>>(lua.state)?;
|
||||
let ud = ud.try_read().ok_or(Error::UserDataBorrowError)?;
|
||||
Ok(method(lua, ud.clone(), A::from_lua_multi(args, lua)?))
|
||||
try_self_arg!(ud.try_read().ok_or(Error::UserDataBorrowError));
|
||||
call(ud.clone())
|
||||
}
|
||||
_ => Err(Error::UserDataTypeMismatch),
|
||||
_ => Err(Error::bad_self_argument(&name, Error::UserDataTypeMismatch)),
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Err(Error::FromLuaConversionError {
|
||||
from: "missing argument",
|
||||
to: "userdata",
|
||||
message: None,
|
||||
})
|
||||
let err = Error::from_lua_conversion("missing argument", "userdata", None);
|
||||
Err(Error::bad_self_argument(&name, err))
|
||||
}
|
||||
};
|
||||
match fut_res() {
|
||||
Ok(fut) => Box::pin(fut.and_then(move |ret| future::ready(ret.to_lua_multi(lua)))),
|
||||
Ok(fut) => {
|
||||
Box::pin(fut.and_then(move |ret| future::ready(ret.into_lua_multi(lua))))
|
||||
}
|
||||
Err(e) => Box::pin(future::err(e)),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn box_function<A, R, F>(function: F) -> Callback<'lua, 'static>
|
||||
fn box_function<F, A, R>(name: &str, function: F) -> Callback<'lua, 'static>
|
||||
where
|
||||
F: Fn(&'lua Lua, A) -> Result<R> + MaybeSend + 'static,
|
||||
A: FromLuaMulti<'lua>,
|
||||
R: ToLuaMulti<'lua>,
|
||||
F: 'static + MaybeSend + Fn(&'lua Lua, A) -> Result<R>,
|
||||
R: IntoLuaMulti<'lua>,
|
||||
{
|
||||
Box::new(move |lua, args| function(lua, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua))
|
||||
let name = get_function_name::<T>(name);
|
||||
Box::new(move |lua, args| {
|
||||
function(lua, A::from_lua_multi_args(args, 1, Some(&name), lua)?)?.into_lua_multi(lua)
|
||||
})
|
||||
}
|
||||
|
||||
fn box_function_mut<A, R, F>(function: F) -> Callback<'lua, 'static>
|
||||
fn box_function_mut<F, A, R>(name: &str, function: F) -> Callback<'lua, 'static>
|
||||
where
|
||||
F: FnMut(&'lua Lua, A) -> Result<R> + MaybeSend + 'static,
|
||||
A: FromLuaMulti<'lua>,
|
||||
R: ToLuaMulti<'lua>,
|
||||
F: 'static + MaybeSend + FnMut(&'lua Lua, A) -> Result<R>,
|
||||
R: IntoLuaMulti<'lua>,
|
||||
{
|
||||
let name = get_function_name::<T>(name);
|
||||
let function = RefCell::new(function);
|
||||
Box::new(move |lua, args| {
|
||||
let function = &mut *function
|
||||
.try_borrow_mut()
|
||||
.map_err(|_| Error::RecursiveMutCallback)?;
|
||||
function(lua, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
|
||||
function(lua, A::from_lua_multi_args(args, 1, Some(&name), lua)?)?.into_lua_multi(lua)
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
fn box_async_function<A, R, F, FR>(function: F) -> AsyncCallback<'lua, 'static>
|
||||
fn box_async_function<F, A, FR, R>(name: &str, function: F) -> AsyncCallback<'lua, 'static>
|
||||
where
|
||||
F: Fn(&'lua Lua, A) -> FR + MaybeSend + 'static,
|
||||
A: FromLuaMulti<'lua>,
|
||||
R: ToLuaMulti<'lua>,
|
||||
F: 'static + MaybeSend + Fn(&'lua Lua, A) -> FR,
|
||||
FR: 'lua + Future<Output = Result<R>>,
|
||||
FR: Future<Output = Result<R>> + 'lua,
|
||||
R: IntoLuaMulti<'lua>,
|
||||
{
|
||||
let name = get_function_name::<T>(name);
|
||||
Box::new(move |lua, args| {
|
||||
let args = match A::from_lua_multi(args, lua) {
|
||||
let args = match A::from_lua_multi_args(args, 1, Some(&name), lua) {
|
||||
Ok(args) => args,
|
||||
Err(e) => return Box::pin(future::err(e)),
|
||||
};
|
||||
Box::pin(function(lua, args).and_then(move |ret| future::ready(ret.to_lua_multi(lua))))
|
||||
Box::pin(
|
||||
function(lua, args).and_then(move |ret| future::ready(ret.into_lua_multi(lua))),
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct StaticUserDataFields<'lua, T: 'static + UserData> {
|
||||
pub(crate) field_getters: Vec<(Vec<u8>, Callback<'lua, 'static>)>,
|
||||
pub(crate) field_setters: Vec<(Vec<u8>, Callback<'lua, 'static>)>,
|
||||
#[allow(clippy::type_complexity)]
|
||||
pub(crate) meta_fields: Vec<(
|
||||
MetaMethod,
|
||||
Box<dyn Fn(&'lua Lua) -> Result<Value<'lua>> + 'static>,
|
||||
)>,
|
||||
_type: PhantomData<T>,
|
||||
// Returns function name for the type `T`, without the module path
|
||||
fn get_function_name<T: 'static>(name: &str) -> StdString {
|
||||
let type_name = any::type_name::<T>().rsplit("::").next().unwrap();
|
||||
format!("{type_name}.{name}",)
|
||||
}
|
||||
|
||||
impl<'lua, T: 'static + UserData> Default for StaticUserDataFields<'lua, T> {
|
||||
fn default() -> StaticUserDataFields<'lua, T> {
|
||||
StaticUserDataFields {
|
||||
field_getters: Vec::new(),
|
||||
field_setters: Vec::new(),
|
||||
meta_fields: Vec::new(),
|
||||
_type: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua, T: 'static + UserData> UserDataFields<'lua, T> for StaticUserDataFields<'lua, T> {
|
||||
fn add_field_method_get<S, R, M>(&mut self, name: &S, method: M)
|
||||
impl<'lua, T: 'static> UserDataFields<'lua, T> for UserDataRegistrar<'lua, T> {
|
||||
fn add_field_method_get<M, R>(&mut self, name: impl AsRef<str>, method: M)
|
||||
where
|
||||
S: AsRef<[u8]> + ?Sized,
|
||||
R: ToLua<'lua>,
|
||||
M: 'static + MaybeSend + Fn(&'lua Lua, &T) -> Result<R>,
|
||||
M: Fn(&'lua Lua, &T) -> Result<R> + MaybeSend + 'static,
|
||||
R: IntoLua<'lua>,
|
||||
{
|
||||
self.field_getters.push((
|
||||
name.as_ref().to_vec(),
|
||||
StaticUserDataMethods::box_method(move |lua, data, ()| method(lua, data)),
|
||||
));
|
||||
let name = name.as_ref();
|
||||
let method = Self::box_method(name, move |lua, data, ()| method(lua, data));
|
||||
self.field_getters.push((name.into(), method));
|
||||
}
|
||||
|
||||
fn add_field_method_set<S, A, M>(&mut self, name: &S, method: M)
|
||||
fn add_field_method_set<M, A>(&mut self, name: impl AsRef<str>, method: M)
|
||||
where
|
||||
S: AsRef<[u8]> + ?Sized,
|
||||
M: FnMut(&'lua Lua, &mut T, A) -> Result<()> + MaybeSend + 'static,
|
||||
A: FromLua<'lua>,
|
||||
M: 'static + MaybeSend + FnMut(&'lua Lua, &mut T, A) -> Result<()>,
|
||||
{
|
||||
self.field_setters.push((
|
||||
name.as_ref().to_vec(),
|
||||
StaticUserDataMethods::box_method_mut(method),
|
||||
));
|
||||
let name = name.as_ref();
|
||||
let method = Self::box_method_mut(name, method);
|
||||
self.field_setters.push((name.into(), method));
|
||||
}
|
||||
|
||||
fn add_field_function_get<S, R, F>(&mut self, name: &S, function: F)
|
||||
fn add_field_function_get<F, R>(&mut self, name: impl AsRef<str>, function: F)
|
||||
where
|
||||
S: AsRef<[u8]> + ?Sized,
|
||||
R: ToLua<'lua>,
|
||||
F: 'static + MaybeSend + Fn(&'lua Lua, AnyUserData<'lua>) -> Result<R>,
|
||||
F: Fn(&'lua Lua, AnyUserData<'lua>) -> Result<R> + MaybeSend + 'static,
|
||||
R: IntoLua<'lua>,
|
||||
{
|
||||
self.field_getters.push((
|
||||
name.as_ref().to_vec(),
|
||||
StaticUserDataMethods::<T>::box_function(function),
|
||||
));
|
||||
let name = name.as_ref();
|
||||
let func = Self::box_function(name, function);
|
||||
self.field_getters.push((name.into(), func));
|
||||
}
|
||||
|
||||
fn add_field_function_set<S, A, F>(&mut self, name: &S, mut function: F)
|
||||
fn add_field_function_set<F, A>(&mut self, name: impl AsRef<str>, mut function: F)
|
||||
where
|
||||
S: AsRef<[u8]> + ?Sized,
|
||||
F: FnMut(&'lua Lua, AnyUserData<'lua>, A) -> Result<()> + MaybeSend + 'static,
|
||||
A: FromLua<'lua>,
|
||||
F: 'static + MaybeSend + FnMut(&'lua Lua, AnyUserData<'lua>, A) -> Result<()>,
|
||||
{
|
||||
self.field_setters.push((
|
||||
name.as_ref().to_vec(),
|
||||
StaticUserDataMethods::<T>::box_function_mut(move |lua, (data, val)| {
|
||||
function(lua, data, val)
|
||||
}),
|
||||
));
|
||||
let name = name.as_ref();
|
||||
let func = Self::box_function_mut(name, move |lua, (data, val)| function(lua, data, val));
|
||||
self.field_setters.push((name.into(), func));
|
||||
}
|
||||
|
||||
fn add_meta_field_with<S, R, F>(&mut self, meta: S, f: F)
|
||||
fn add_meta_field_with<F, R>(&mut self, name: impl AsRef<str>, f: F)
|
||||
where
|
||||
S: Into<MetaMethod>,
|
||||
R: ToLua<'lua>,
|
||||
F: 'static + MaybeSend + Fn(&'lua Lua) -> Result<R>,
|
||||
F: Fn(&'lua Lua) -> Result<R> + MaybeSend + 'static,
|
||||
R: IntoLua<'lua>,
|
||||
{
|
||||
let meta = meta.into();
|
||||
let name = name.as_ref().to_string();
|
||||
self.meta_fields.push((
|
||||
meta.clone(),
|
||||
name.clone(),
|
||||
Box::new(move |lua| {
|
||||
let value = f(lua)?.to_lua(lua)?;
|
||||
if meta == MetaMethod::Index || meta == MetaMethod::NewIndex {
|
||||
let value = f(lua)?.into_lua(lua)?;
|
||||
if name == MetaMethod::Index || name == MetaMethod::NewIndex {
|
||||
match value {
|
||||
Value::Nil | Value::Table(_) | Value::Function(_) => {}
|
||||
_ => {
|
||||
return Err(Error::MetaMethodTypeError {
|
||||
method: meta.to_string(),
|
||||
method: name.clone(),
|
||||
type_name: value.type_name(),
|
||||
message: Some("expected nil, table or function".to_string()),
|
||||
})
|
||||
@@ -559,15 +438,179 @@ impl<'lua, T: 'static + UserData> UserDataFields<'lua, T> for StaticUserDataFiel
|
||||
|
||||
// Below are internal methods
|
||||
|
||||
fn add_field_getter(&mut self, name: Vec<u8>, callback: Callback<'lua, 'static>) {
|
||||
fn add_field_getter(&mut self, name: String, callback: Callback<'lua, 'static>) {
|
||||
self.field_getters.push((name, callback));
|
||||
}
|
||||
|
||||
fn add_field_setter(&mut self, name: Vec<u8>, callback: Callback<'lua, 'static>) {
|
||||
fn add_field_setter(&mut self, name: String, callback: Callback<'lua, 'static>) {
|
||||
self.field_setters.push((name, callback));
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua, T: 'static> UserDataMethods<'lua, T> for UserDataRegistrar<'lua, T> {
|
||||
fn add_method<M, A, R>(&mut self, name: impl AsRef<str>, method: M)
|
||||
where
|
||||
M: Fn(&'lua Lua, &T, A) -> Result<R> + MaybeSend + 'static,
|
||||
A: FromLuaMulti<'lua>,
|
||||
R: IntoLuaMulti<'lua>,
|
||||
{
|
||||
let name = name.as_ref();
|
||||
self.methods
|
||||
.push((name.into(), Self::box_method(name, method)));
|
||||
}
|
||||
|
||||
fn add_method_mut<M, A, R>(&mut self, name: impl AsRef<str>, method: M)
|
||||
where
|
||||
M: FnMut(&'lua Lua, &mut T, A) -> Result<R> + MaybeSend + 'static,
|
||||
A: FromLuaMulti<'lua>,
|
||||
R: IntoLuaMulti<'lua>,
|
||||
{
|
||||
let name = name.as_ref();
|
||||
self.methods
|
||||
.push((name.into(), Self::box_method_mut(name, method)));
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
fn add_async_method<M, A, MR, R>(&mut self, name: impl AsRef<str>, method: M)
|
||||
where
|
||||
T: Clone,
|
||||
M: Fn(&'lua Lua, T, A) -> MR + MaybeSend + 'static,
|
||||
A: FromLuaMulti<'lua>,
|
||||
MR: Future<Output = Result<R>> + 'lua,
|
||||
R: IntoLuaMulti<'lua>,
|
||||
{
|
||||
let name = name.as_ref();
|
||||
self.async_methods
|
||||
.push((name.into(), Self::box_async_method(name, method)));
|
||||
}
|
||||
|
||||
fn add_function<F, A, R>(&mut self, name: impl AsRef<str>, function: F)
|
||||
where
|
||||
F: Fn(&'lua Lua, A) -> Result<R> + MaybeSend + 'static,
|
||||
A: FromLuaMulti<'lua>,
|
||||
R: IntoLuaMulti<'lua>,
|
||||
{
|
||||
let name = name.as_ref();
|
||||
self.methods
|
||||
.push((name.into(), Self::box_function(name, function)));
|
||||
}
|
||||
|
||||
fn add_function_mut<F, A, R>(&mut self, name: impl AsRef<str>, function: F)
|
||||
where
|
||||
F: FnMut(&'lua Lua, A) -> Result<R> + MaybeSend + 'static,
|
||||
A: FromLuaMulti<'lua>,
|
||||
R: IntoLuaMulti<'lua>,
|
||||
{
|
||||
let name = name.as_ref();
|
||||
self.methods
|
||||
.push((name.into(), Self::box_function_mut(name, function)));
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
fn add_async_function<F, A, FR, R>(&mut self, name: impl AsRef<str>, function: F)
|
||||
where
|
||||
F: Fn(&'lua Lua, A) -> FR + MaybeSend + 'static,
|
||||
A: FromLuaMulti<'lua>,
|
||||
FR: Future<Output = Result<R>> + 'lua,
|
||||
R: IntoLuaMulti<'lua>,
|
||||
{
|
||||
let name = name.as_ref();
|
||||
self.async_methods
|
||||
.push((name.into(), Self::box_async_function(name, function)));
|
||||
}
|
||||
|
||||
fn add_meta_method<M, A, R>(&mut self, name: impl AsRef<str>, method: M)
|
||||
where
|
||||
M: Fn(&'lua Lua, &T, A) -> Result<R> + MaybeSend + 'static,
|
||||
A: FromLuaMulti<'lua>,
|
||||
R: IntoLuaMulti<'lua>,
|
||||
{
|
||||
let name = name.as_ref();
|
||||
self.meta_methods
|
||||
.push((name.into(), Self::box_method(name, method)));
|
||||
}
|
||||
|
||||
fn add_meta_method_mut<M, A, R>(&mut self, name: impl AsRef<str>, method: M)
|
||||
where
|
||||
M: FnMut(&'lua Lua, &mut T, A) -> Result<R> + MaybeSend + 'static,
|
||||
A: FromLuaMulti<'lua>,
|
||||
R: IntoLuaMulti<'lua>,
|
||||
{
|
||||
let name = name.as_ref();
|
||||
self.meta_methods
|
||||
.push((name.into(), Self::box_method_mut(name, method)));
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "async", not(any(feature = "lua51", feature = "luau"))))]
|
||||
fn add_async_meta_method<M, A, MR, R>(&mut self, name: impl AsRef<str>, method: M)
|
||||
where
|
||||
T: Clone,
|
||||
M: Fn(&'lua Lua, T, A) -> MR + MaybeSend + 'static,
|
||||
A: FromLuaMulti<'lua>,
|
||||
MR: Future<Output = Result<R>> + 'lua,
|
||||
R: IntoLuaMulti<'lua>,
|
||||
{
|
||||
let name = name.as_ref();
|
||||
self.async_meta_methods
|
||||
.push((name.into(), Self::box_async_method(name, method)));
|
||||
}
|
||||
|
||||
fn add_meta_function<F, A, R>(&mut self, name: impl AsRef<str>, function: F)
|
||||
where
|
||||
F: Fn(&'lua Lua, A) -> Result<R> + MaybeSend + 'static,
|
||||
A: FromLuaMulti<'lua>,
|
||||
R: IntoLuaMulti<'lua>,
|
||||
{
|
||||
let name = name.as_ref();
|
||||
self.meta_methods
|
||||
.push((name.into(), Self::box_function(name, function)));
|
||||
}
|
||||
|
||||
fn add_meta_function_mut<F, A, R>(&mut self, name: impl AsRef<str>, function: F)
|
||||
where
|
||||
F: FnMut(&'lua Lua, A) -> Result<R> + MaybeSend + 'static,
|
||||
A: FromLuaMulti<'lua>,
|
||||
R: IntoLuaMulti<'lua>,
|
||||
{
|
||||
let name = name.as_ref();
|
||||
self.meta_methods
|
||||
.push((name.into(), Self::box_function_mut(name, function)));
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "async", not(any(feature = "lua51", feature = "luau"))))]
|
||||
fn add_async_meta_function<F, A, FR, R>(&mut self, name: impl AsRef<str>, function: F)
|
||||
where
|
||||
F: Fn(&'lua Lua, A) -> FR + MaybeSend + 'static,
|
||||
A: FromLuaMulti<'lua>,
|
||||
FR: Future<Output = Result<R>> + 'lua,
|
||||
R: IntoLuaMulti<'lua>,
|
||||
{
|
||||
let name = name.as_ref();
|
||||
self.async_meta_methods
|
||||
.push((name.into(), Self::box_async_function(name, function)));
|
||||
}
|
||||
|
||||
// Below are internal methods used in generated code
|
||||
|
||||
fn add_callback(&mut self, name: String, callback: Callback<'lua, 'static>) {
|
||||
self.methods.push((name, callback));
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
fn add_async_callback(&mut self, name: String, callback: AsyncCallback<'lua, 'static>) {
|
||||
self.async_methods.push((name, callback));
|
||||
}
|
||||
|
||||
fn add_meta_callback(&mut self, name: String, callback: Callback<'lua, 'static>) {
|
||||
self.meta_methods.push((name, callback));
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
fn add_async_meta_callback(&mut self, meta: String, callback: AsyncCallback<'lua, 'static>) {
|
||||
self.async_meta_methods.push((meta, callback))
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn get_userdata_ref<'a, T>(state: *mut ffi::lua_State) -> Result<Ref<'a, T>> {
|
||||
(*get_userdata::<UserDataCell<T>>(state, -1)).try_borrow()
|
||||
@@ -580,9 +623,9 @@ unsafe fn get_userdata_mut<'a, T>(state: *mut ffi::lua_State) -> Result<RefMut<'
|
||||
|
||||
macro_rules! lua_userdata_impl {
|
||||
($type:ty) => {
|
||||
impl<T: 'static + UserData> UserData for $type {
|
||||
impl<T: UserData + 'static> UserData for $type {
|
||||
fn add_fields<'lua, F: UserDataFields<'lua, Self>>(fields: &mut F) {
|
||||
let mut orig_fields = StaticUserDataFields::default();
|
||||
let mut orig_fields = UserDataRegistrar::new();
|
||||
T::add_fields(&mut orig_fields);
|
||||
for (name, callback) in orig_fields.field_getters {
|
||||
fields.add_field_getter(name, callback);
|
||||
@@ -593,7 +636,7 @@ macro_rules! lua_userdata_impl {
|
||||
}
|
||||
|
||||
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
|
||||
let mut orig_methods = StaticUserDataMethods::default();
|
||||
let mut orig_methods = UserDataRegistrar::new();
|
||||
T::add_methods(&mut orig_methods);
|
||||
for (name, callback) in orig_methods.methods {
|
||||
methods.add_callback(name, callback);
|
||||
|
||||
+34
-35
@@ -11,7 +11,7 @@ use once_cell::sync::Lazy;
|
||||
use rustc_hash::FxHashMap;
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
use crate::ffi;
|
||||
use crate::memory::MemoryState;
|
||||
|
||||
static METATABLE_CACHE: Lazy<FxHashMap<TypeId, u8>> = Lazy::new(|| {
|
||||
let mut map = FxHashMap::with_capacity_and_hasher(32, Default::default());
|
||||
@@ -47,7 +47,6 @@ pub unsafe fn check_stack(state: *mut ffi::lua_State, amount: c_int) -> Result<(
|
||||
pub struct StackGuard {
|
||||
state: *mut ffi::lua_State,
|
||||
top: c_int,
|
||||
extra: c_int,
|
||||
}
|
||||
|
||||
impl StackGuard {
|
||||
@@ -59,17 +58,6 @@ impl StackGuard {
|
||||
StackGuard {
|
||||
state,
|
||||
top: ffi::lua_gettop(state),
|
||||
extra: 0,
|
||||
}
|
||||
}
|
||||
|
||||
// Similar to `new`, but checks and keeps `extra` elements from top of the stack on Drop.
|
||||
#[inline]
|
||||
pub unsafe fn new_extra(state: *mut ffi::lua_State, extra: c_int) -> StackGuard {
|
||||
StackGuard {
|
||||
state,
|
||||
top: ffi::lua_gettop(state),
|
||||
extra,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -78,14 +66,11 @@ impl Drop for StackGuard {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
let top = ffi::lua_gettop(self.state);
|
||||
if top < self.top + self.extra {
|
||||
if top < self.top {
|
||||
mlua_panic!("{} too many stack values popped", self.top - top)
|
||||
}
|
||||
if top > self.top + self.extra {
|
||||
if self.extra > 0 {
|
||||
ffi::lua_rotate(self.state, self.top + 1, self.extra);
|
||||
}
|
||||
ffi::lua_settop(self.state, self.top + self.extra);
|
||||
if top > self.top {
|
||||
ffi::lua_settop(self.state, self.top);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -104,8 +89,10 @@ pub unsafe fn protect_lua_call(
|
||||
) -> Result<()> {
|
||||
let stack_start = ffi::lua_gettop(state) - nargs;
|
||||
|
||||
ffi::lua_pushcfunction(state, error_traceback);
|
||||
ffi::lua_pushcfunction(state, f);
|
||||
MemoryState::relax_limit_with(state, || {
|
||||
ffi::lua_pushcfunction(state, error_traceback);
|
||||
ffi::lua_pushcfunction(state, f);
|
||||
});
|
||||
if nargs > 0 {
|
||||
ffi::lua_rotate(state, stack_start + 1, 2);
|
||||
}
|
||||
@@ -162,8 +149,10 @@ where
|
||||
|
||||
let stack_start = ffi::lua_gettop(state) - nargs;
|
||||
|
||||
ffi::lua_pushcfunction(state, error_traceback);
|
||||
ffi::lua_pushcfunction(state, do_call::<F, R>);
|
||||
MemoryState::relax_limit_with(state, || {
|
||||
ffi::lua_pushcfunction(state, error_traceback);
|
||||
ffi::lua_pushcfunction(state, do_call::<F, R>);
|
||||
});
|
||||
if nargs > 0 {
|
||||
ffi::lua_rotate(state, stack_start + 1, 2);
|
||||
}
|
||||
@@ -245,7 +234,8 @@ pub unsafe fn pop_error(state: *mut ffi::lua_State, err_code: c_int) -> Error {
|
||||
// Uses 3 (or 1 if unprotected) stack spaces, does not call checkstack.
|
||||
#[inline(always)]
|
||||
pub unsafe fn push_string(state: *mut ffi::lua_State, s: &[u8], protect: bool) -> Result<()> {
|
||||
if protect {
|
||||
// Always use protected mode if the string is too long
|
||||
if protect || s.len() > (1 << 30) {
|
||||
protect_lua!(state, 0, 1, |state| {
|
||||
ffi::lua_pushlstring(state, s.as_ptr() as *const c_char, s.len());
|
||||
})
|
||||
@@ -272,11 +262,7 @@ pub unsafe fn push_table(
|
||||
}
|
||||
|
||||
// Uses 4 stack spaces, does not call checkstack.
|
||||
pub unsafe fn rawset_field<S>(state: *mut ffi::lua_State, table: c_int, field: &S) -> Result<()>
|
||||
where
|
||||
S: AsRef<[u8]> + ?Sized,
|
||||
{
|
||||
let field = field.as_ref();
|
||||
pub unsafe fn rawset_field(state: *mut ffi::lua_State, table: c_int, field: &str) -> Result<()> {
|
||||
ffi::lua_pushvalue(state, table);
|
||||
protect_lua!(state, 2, 0, |state| {
|
||||
ffi::lua_pushlstring(state, field.as_ptr() as *const c_char, field.len());
|
||||
@@ -427,11 +413,17 @@ unsafe fn init_userdata_metatable_index(state: *mut ffi::lua_State) -> Result<()
|
||||
}
|
||||
ffi::lua_pop(state, 1);
|
||||
|
||||
// Create and cache `__index` helper
|
||||
// Create and cache `__index` generator
|
||||
let code = cstr!(
|
||||
r#"
|
||||
local error, isfunction = ...
|
||||
return function (__index, field_getters, methods)
|
||||
-- Fastpath to return methods table for index access
|
||||
if __index == nil and field_getters == nil then
|
||||
return methods
|
||||
end
|
||||
|
||||
-- Alternatively return a function for index access
|
||||
return function (self, key)
|
||||
if field_getters ~= nil then
|
||||
local field_getter = field_getters[key]
|
||||
@@ -481,7 +473,7 @@ pub unsafe fn init_userdata_metatable_newindex(state: *mut ffi::lua_State) -> Re
|
||||
}
|
||||
ffi::lua_pop(state, 1);
|
||||
|
||||
// Create and cache `__newindex` helper
|
||||
// Create and cache `__newindex` generator
|
||||
let code = cstr!(
|
||||
r#"
|
||||
local error, isfunction = ...
|
||||
@@ -674,6 +666,13 @@ where
|
||||
}
|
||||
|
||||
pub unsafe extern "C" fn error_traceback(state: *mut ffi::lua_State) -> c_int {
|
||||
// Luau calls error handler for memory allocation errors, skip it
|
||||
// See https://github.com/Roblox/luau/issues/880
|
||||
#[cfg(feature = "luau")]
|
||||
if MemoryState::limit_reached(state) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ffi::lua_checkstack(state, 2) == 0 {
|
||||
// If we don't have enough stack space to even check the error type, do
|
||||
// nothing so we don't risk shadowing a rust panic.
|
||||
@@ -862,7 +861,7 @@ pub unsafe fn init_error_registry(state: *mut ffi::lua_State) -> Result<()> {
|
||||
// Depending on how the API is used and what error types scripts are given, it may
|
||||
// be possible to make this consume arbitrary amounts of memory (for example, some
|
||||
// kind of recursive error structure?)
|
||||
let _ = write!(&mut (*err_buf), "{}", error);
|
||||
let _ = write!(&mut (*err_buf), "{error}");
|
||||
Ok(err_buf)
|
||||
}
|
||||
Some(WrappedFailure::Panic(Some(ref panic))) => {
|
||||
@@ -873,9 +872,9 @@ pub unsafe fn init_error_registry(state: *mut ffi::lua_State) -> Result<()> {
|
||||
ffi::lua_pop(state, 2);
|
||||
|
||||
if let Some(msg) = panic.downcast_ref::<&str>() {
|
||||
let _ = write!(&mut (*err_buf), "{}", msg);
|
||||
let _ = write!(&mut (*err_buf), "{msg}");
|
||||
} else if let Some(msg) = panic.downcast_ref::<String>() {
|
||||
let _ = write!(&mut (*err_buf), "{}", msg);
|
||||
let _ = write!(&mut (*err_buf), "{msg}");
|
||||
} else {
|
||||
let _ = write!(&mut (*err_buf), "<panic>");
|
||||
};
|
||||
@@ -1024,7 +1023,7 @@ pub(crate) unsafe fn to_string(state: *mut ffi::lua_State, index: c_int) -> Stri
|
||||
let v = ffi::lua_tovector(state, index);
|
||||
mlua_debug_assert!(!v.is_null(), "vector is null");
|
||||
let (x, y, z) = (*v, *v.add(1), *v.add(2));
|
||||
format!("vector({},{},{})", x, y, z)
|
||||
format!("vector({x},{y},{z})")
|
||||
}
|
||||
ffi::LUA_TSTRING => {
|
||||
let mut size = 0;
|
||||
|
||||
+158
-15
@@ -1,7 +1,10 @@
|
||||
use std::cmp::Ordering;
|
||||
use std::collections::HashSet;
|
||||
use std::iter::{self, FromIterator};
|
||||
use std::ops::Index;
|
||||
use std::os::raw::c_void;
|
||||
use std::{ptr, slice, str, vec};
|
||||
use std::sync::Arc;
|
||||
use std::{fmt, ptr, slice, str, vec};
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
use {
|
||||
@@ -11,7 +14,6 @@ use {
|
||||
};
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
use crate::ffi;
|
||||
use crate::function::Function;
|
||||
use crate::lua::Lua;
|
||||
use crate::string::String;
|
||||
@@ -23,7 +25,7 @@ use crate::userdata::AnyUserData;
|
||||
/// A dynamically typed Lua value. The `String`, `Table`, `Function`, `Thread`, and `UserData`
|
||||
/// variants contain handle types into the internal Lua state. It is a logic error to mix handle
|
||||
/// types between separate `Lua` instances, and doing so will result in a panic.
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Clone)]
|
||||
pub enum Value<'lua> {
|
||||
/// The Lua value `nil`.
|
||||
Nil,
|
||||
@@ -93,7 +95,7 @@ impl<'lua> Value<'lua> {
|
||||
match (self, other.as_ref()) {
|
||||
(Value::Table(a), Value::Table(b)) => a.equals(b),
|
||||
(Value::UserData(a), Value::UserData(b)) => a.equals(b),
|
||||
_ => Ok(self == other.as_ref()),
|
||||
(a, b) => Ok(a == b),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -120,6 +122,99 @@ impl<'lua> Value<'lua> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Compares two values.
|
||||
// Used to sort values for Debug printing.
|
||||
pub(crate) fn cmp(&self, other: &Self) -> Ordering {
|
||||
fn cmp_num(a: Number, b: Number) -> Ordering {
|
||||
match (a, b) {
|
||||
_ if a < b => Ordering::Less,
|
||||
_ if a > b => Ordering::Greater,
|
||||
_ => Ordering::Equal,
|
||||
}
|
||||
}
|
||||
|
||||
match (self, other) {
|
||||
// Nil
|
||||
(Value::Nil, Value::Nil) => Ordering::Equal,
|
||||
(Value::Nil, _) => Ordering::Less,
|
||||
(_, Value::Nil) => Ordering::Greater,
|
||||
// Null (a special case)
|
||||
(Value::LightUserData(ud1), Value::LightUserData(ud2)) if ud1 == ud2 => Ordering::Equal,
|
||||
(Value::LightUserData(ud1), _) if ud1.0.is_null() => Ordering::Less,
|
||||
(_, Value::LightUserData(ud2)) if ud2.0.is_null() => Ordering::Greater,
|
||||
// Boolean
|
||||
(Value::Boolean(a), Value::Boolean(b)) => a.cmp(b),
|
||||
(Value::Boolean(_), _) => Ordering::Less,
|
||||
(_, Value::Boolean(_)) => Ordering::Greater,
|
||||
// Integer && Number
|
||||
(Value::Integer(a), Value::Integer(b)) => a.cmp(b),
|
||||
(&Value::Integer(a), &Value::Number(b)) => cmp_num(a as Number, b),
|
||||
(&Value::Number(a), &Value::Integer(b)) => cmp_num(a, b as Number),
|
||||
(&Value::Number(a), &Value::Number(b)) => cmp_num(a, b),
|
||||
(Value::Integer(_) | Value::Number(_), _) => Ordering::Less,
|
||||
(_, Value::Integer(_) | Value::Number(_)) => Ordering::Greater,
|
||||
// String
|
||||
(Value::String(a), Value::String(b)) => a.as_bytes().cmp(b.as_bytes()),
|
||||
(Value::String(_), _) => Ordering::Less,
|
||||
(_, Value::String(_)) => Ordering::Greater,
|
||||
// Other variants can be randomly ordered
|
||||
(a, b) => a.to_pointer().cmp(&b.to_pointer()),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn fmt_pretty(
|
||||
&self,
|
||||
fmt: &mut fmt::Formatter,
|
||||
recursive: bool,
|
||||
ident: usize,
|
||||
visited: &mut HashSet<*const c_void>,
|
||||
) -> fmt::Result {
|
||||
match self {
|
||||
Value::Nil => write!(fmt, "nil"),
|
||||
Value::Boolean(b) => write!(fmt, "{b}"),
|
||||
Value::LightUserData(ud) if ud.0.is_null() => write!(fmt, "null"),
|
||||
Value::LightUserData(ud) => write!(fmt, "<lightuserdata {:?}>", ud.0),
|
||||
Value::Integer(i) => write!(fmt, "{i}"),
|
||||
Value::Number(n) => write!(fmt, "{n}"),
|
||||
#[cfg(feature = "luau")]
|
||||
Value::Vector(x, y, z) => write!(fmt, "vector({x}, {y}, {z})"),
|
||||
Value::String(s) => write!(fmt, "{s:?}"),
|
||||
Value::Table(t) if recursive && !visited.contains(&t.to_pointer()) => {
|
||||
t.fmt_pretty(fmt, ident, visited)
|
||||
}
|
||||
t @ Value::Table(_) => write!(fmt, "<table {:?}>", t.to_pointer()),
|
||||
f @ Value::Function(_) => write!(fmt, "<function {:?}>", f.to_pointer()),
|
||||
t @ Value::Thread(_) => write!(fmt, "<thread {:?}>", t.to_pointer()),
|
||||
// TODO: Show type name for registered userdata
|
||||
u @ Value::UserData(_) => write!(fmt, "<userdata {:?}>", u.to_pointer()),
|
||||
Value::Error(e) if recursive => write!(fmt, "{e:?}"),
|
||||
Value::Error(_) => write!(fmt, "<error>"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Value<'_> {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
if fmt.alternate() {
|
||||
return self.fmt_pretty(fmt, true, 0, &mut HashSet::new());
|
||||
}
|
||||
match self {
|
||||
Value::Nil => write!(fmt, "Nil"),
|
||||
Value::Boolean(b) => write!(fmt, "Boolean({b})"),
|
||||
Value::LightUserData(ud) => write!(fmt, "{ud:?}"),
|
||||
Value::Integer(i) => write!(fmt, "Integer({i})"),
|
||||
Value::Number(n) => write!(fmt, "Number({n})"),
|
||||
#[cfg(feature = "luau")]
|
||||
Value::Vector(x, y, z) => write!(fmt, "Vector({x}, {y}, {z})"),
|
||||
Value::String(s) => write!(fmt, "String({s:?})"),
|
||||
Value::Table(t) => write!(fmt, "{t:?}"),
|
||||
Value::Function(f) => write!(fmt, "{f:?}"),
|
||||
Value::Thread(t) => write!(fmt, "{t:?}"),
|
||||
Value::UserData(ud) => write!(fmt, "{ud:?}"),
|
||||
Value::Error(e) => write!(fmt, "Error({e:?})"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> PartialEq for Value<'lua> {
|
||||
@@ -162,8 +257,7 @@ impl<'lua> Serialize for Value<'lua> {
|
||||
Value::Boolean(b) => serializer.serialize_bool(*b),
|
||||
#[allow(clippy::useless_conversion)]
|
||||
Value::Integer(i) => serializer
|
||||
.serialize_i64((*i).try_into().expect("cannot convert lua_Integer to i64")),
|
||||
#[allow(clippy::useless_conversion)]
|
||||
.serialize_i64((*i).try_into().expect("cannot convert Lua Integer to i64")),
|
||||
Value::Number(n) => serializer.serialize_f64(*n),
|
||||
#[cfg(feature = "luau")]
|
||||
Value::Vector(x, y, z) => (x, y, z).serialize(serializer),
|
||||
@@ -180,15 +274,34 @@ impl<'lua> Serialize for Value<'lua> {
|
||||
}
|
||||
|
||||
/// Trait for types convertible to `Value`.
|
||||
pub trait ToLua<'lua> {
|
||||
pub trait IntoLua<'lua> {
|
||||
/// Performs the conversion.
|
||||
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>>;
|
||||
fn into_lua(self, lua: &'lua Lua) -> Result<Value<'lua>>;
|
||||
}
|
||||
|
||||
/// Trait for types convertible from `Value`.
|
||||
pub trait FromLua<'lua>: Sized {
|
||||
/// Performs the conversion.
|
||||
fn from_lua(lua_value: Value<'lua>, lua: &'lua Lua) -> Result<Self>;
|
||||
fn from_lua(value: Value<'lua>, lua: &'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)]
|
||||
fn from_lua_arg(
|
||||
value: Value<'lua>,
|
||||
i: usize,
|
||||
to: Option<&str>,
|
||||
lua: &'lua Lua,
|
||||
) -> Result<Self> {
|
||||
Self::from_lua(value, 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.
|
||||
@@ -203,8 +316,14 @@ impl<'lua> MultiValue<'lua> {
|
||||
|
||||
/// Similar to `new` but can return previously used container with allocated capacity.
|
||||
#[inline]
|
||||
pub(crate) fn new_or_cached(lua: &'lua Lua) -> MultiValue<'lua> {
|
||||
lua.new_or_cached_multivalue()
|
||||
pub(crate) fn new_or_pooled(lua: &'lua Lua) -> MultiValue<'lua> {
|
||||
lua.new_multivalue_from_pool()
|
||||
}
|
||||
|
||||
/// Clears and returns previously allocated multivalue container to the pool.
|
||||
#[inline]
|
||||
pub(crate) fn return_to_pool(multivalue: Self, lua: &Lua) {
|
||||
lua.return_multivalue_to_pool(multivalue);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -337,11 +456,11 @@ impl<'lua> MultiValue<'lua> {
|
||||
|
||||
/// Trait for types convertible to any number of Lua values.
|
||||
///
|
||||
/// This is a generalization of `ToLua`, allowing any number of resulting Lua values instead of just
|
||||
/// one. Any type that implements `ToLua` will automatically implement this trait.
|
||||
pub trait ToLuaMulti<'lua> {
|
||||
/// This is a generalization of `IntoLua`, allowing any number of resulting Lua values instead of just
|
||||
/// one. Any type that implements `IntoLua` will automatically implement this trait.
|
||||
pub trait IntoLuaMulti<'lua> {
|
||||
/// Performs the conversion.
|
||||
fn to_lua_multi(self, lua: &'lua Lua) -> Result<MultiValue<'lua>>;
|
||||
fn into_lua_multi(self, lua: &'lua Lua) -> Result<MultiValue<'lua>>;
|
||||
}
|
||||
|
||||
/// Trait for types that can be created from an arbitrary number of Lua values.
|
||||
@@ -356,4 +475,28 @@ pub trait FromLuaMulti<'lua>: Sized {
|
||||
/// assigning values. Similarly, if not enough values are given, conversions should assume that
|
||||
/// any missing values are nil.
|
||||
fn from_lua_multi(values: MultiValue<'lua>, lua: &'lua Lua) -> Result<Self>;
|
||||
|
||||
/// 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_multi_args(
|
||||
values: MultiValue<'lua>,
|
||||
i: usize,
|
||||
to: Option<&str>,
|
||||
lua: &'lua Lua,
|
||||
) -> Result<Self> {
|
||||
let _ = (i, to);
|
||||
Self::from_lua_multi(values, lua)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod assertions {
|
||||
use super::*;
|
||||
|
||||
static_assertions::assert_not_impl_any!(Value: Send);
|
||||
static_assertions::assert_not_impl_any!(MultiValue: Send);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
[lua54_coverage]
|
||||
features = "lua54,vendored,async,serialize,macros,unstable"
|
||||
|
||||
[lua51_coverage]
|
||||
features = "lua51,vendored,async,serialize,macros,unstable"
|
||||
|
||||
[luau_coverage]
|
||||
features = "luau,async,serialize,macros,unstable"
|
||||
+57
-122
@@ -1,18 +1,14 @@
|
||||
#![cfg(feature = "async")]
|
||||
|
||||
use std::cell::Cell;
|
||||
use std::rc::Rc;
|
||||
use std::sync::{
|
||||
atomic::{AtomicI64, AtomicU64, Ordering},
|
||||
Arc,
|
||||
};
|
||||
use std::sync::atomic::{AtomicU64, Ordering};
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
|
||||
use futures_timer::Delay;
|
||||
use futures_util::stream::TryStreamExt;
|
||||
|
||||
use mlua::{
|
||||
Error, Function, Lua, LuaOptions, Result, StdLib, Table, TableExt, Thread, UserData,
|
||||
AnyUserDataExt, Error, Function, Lua, LuaOptions, Result, StdLib, Table, TableExt, UserData,
|
||||
UserDataMethods, Value,
|
||||
};
|
||||
|
||||
@@ -30,6 +26,19 @@ async fn test_async_function() -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_async_function_wrap() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let f = Function::wrap_async(|_, s: String| async move { 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_sleep() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
@@ -261,9 +270,27 @@ async fn test_async_thread() -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_async_thread_capture() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let f = lua.create_async_function(move |_lua, v: Value| async move {
|
||||
tokio::task::yield_now().await;
|
||||
drop(v);
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
let thread = lua.create_thread(f)?;
|
||||
// After first resume, `v: Value` is captured in the coroutine
|
||||
thread.resume::<_, ()>("abc").unwrap();
|
||||
drop(thread);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_async_table() -> Result<()> {
|
||||
let options = LuaOptions::new().thread_cache_size(4);
|
||||
let options = LuaOptions::new().thread_pool_size(4);
|
||||
let lua = Lua::new_with(StdLib::ALL_SAFE, options)?;
|
||||
|
||||
let table = lua.create_table()?;
|
||||
@@ -311,8 +338,8 @@ async fn test_async_table() -> Result<()> {
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_async_thread_cache() -> Result<()> {
|
||||
let options = LuaOptions::new().thread_cache_size(4);
|
||||
async fn test_async_thread_pool() -> Result<()> {
|
||||
let options = LuaOptions::new().thread_pool_size(4);
|
||||
let lua = Lua::new_with(StdLib::ALL_SAFE, options)?;
|
||||
|
||||
let error_f = lua.create_async_function(|_, ()| async move {
|
||||
@@ -423,6 +450,14 @@ async fn test_async_userdata() -> Result<()> {
|
||||
.exec_async()
|
||||
.await?;
|
||||
|
||||
userdata.call_async_method("set_value", 24).await?;
|
||||
let n: u64 = userdata.call_async_method("get_value", ()).await?;
|
||||
assert_eq!(n, 24);
|
||||
userdata.call_async_function("sleep", 15).await?;
|
||||
|
||||
#[cfg(not(any(feature = "lua51", feature = "luau")))]
|
||||
assert_eq!(userdata.call_async::<_, String>(()).await?, "elapsed:24ms");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -439,7 +474,7 @@ async fn test_async_thread_error() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
let result = lua
|
||||
.load("function x(...) error(...) end x(...)")
|
||||
.set_name("chunk")?
|
||||
.set_name("chunk")
|
||||
.call_async::<_, ()>(MyUserData)
|
||||
.await;
|
||||
assert!(
|
||||
@@ -450,120 +485,20 @@ async fn test_async_thread_error() -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "unstable", not(feature = "send")))]
|
||||
#[tokio::test]
|
||||
async fn test_async_scope() -> Result<()> {
|
||||
let ref lua = Lua::new();
|
||||
async fn test_owned_async_call() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let ref rc = Rc::new(Cell::new(0));
|
||||
let hello = lua
|
||||
.create_async_function(|_, name: String| async move {
|
||||
Delay::new(Duration::from_millis(10)).await;
|
||||
Ok(format!("hello, {}!", name))
|
||||
})?
|
||||
.into_owned();
|
||||
drop(lua);
|
||||
|
||||
let fut = lua.async_scope(|scope| async move {
|
||||
let f = scope.create_async_function(move |_, n: u64| {
|
||||
let rc2 = rc.clone();
|
||||
async move {
|
||||
rc2.set(42);
|
||||
Delay::new(Duration::from_millis(n)).await;
|
||||
assert_eq!(Rc::strong_count(&rc2), 2);
|
||||
Ok(())
|
||||
}
|
||||
})?;
|
||||
|
||||
lua.globals().set("f", f.clone())?;
|
||||
|
||||
assert_eq!(Rc::strong_count(rc), 1);
|
||||
let _ = f.call_async::<u64, ()>(10).await?;
|
||||
assert_eq!(Rc::strong_count(rc), 1);
|
||||
|
||||
// Create future in partialy polled state (Poll::Pending)
|
||||
let g = lua.create_thread(f)?;
|
||||
g.resume::<u64, ()>(10)?;
|
||||
lua.globals().set("g", g)?;
|
||||
assert_eq!(Rc::strong_count(rc), 2);
|
||||
|
||||
Ok(())
|
||||
});
|
||||
|
||||
assert_eq!(Rc::strong_count(rc), 1);
|
||||
let _ = fut.await?;
|
||||
assert_eq!(Rc::strong_count(rc), 1);
|
||||
|
||||
match lua
|
||||
.globals()
|
||||
.get::<_, Function>("f")?
|
||||
.call_async::<_, ()>(10)
|
||||
.await
|
||||
{
|
||||
Err(Error::CallbackError { ref cause, .. }) => match cause.as_ref() {
|
||||
Error::CallbackDestructed => {}
|
||||
e => panic!("expected `CallbackDestructed` error cause, got {:?}", e),
|
||||
},
|
||||
r => panic!("improper return for destructed function: {:?}", r),
|
||||
};
|
||||
|
||||
match lua.globals().get::<_, Thread>("g")?.resume::<_, Value>(()) {
|
||||
Err(Error::CallbackError { ref cause, .. }) => match cause.as_ref() {
|
||||
Error::CallbackDestructed => {}
|
||||
e => panic!("expected `CallbackDestructed` error cause, got {:?}", e),
|
||||
},
|
||||
r => panic!("improper return for destructed function: {:?}", r),
|
||||
};
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_async_scope_userdata() -> Result<()> {
|
||||
#[derive(Clone)]
|
||||
struct MyUserData(Arc<AtomicI64>);
|
||||
|
||||
impl UserData for MyUserData {
|
||||
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
|
||||
methods.add_async_method("get_value", |_, data, ()| async move {
|
||||
Delay::new(Duration::from_millis(10)).await;
|
||||
Ok(data.0.load(Ordering::Relaxed))
|
||||
});
|
||||
|
||||
methods.add_async_method("set_value", |_, data, n| async move {
|
||||
Delay::new(Duration::from_millis(10)).await;
|
||||
data.0.store(n, Ordering::Relaxed);
|
||||
Ok(())
|
||||
});
|
||||
|
||||
methods.add_async_function("sleep", |_, n| async move {
|
||||
Delay::new(Duration::from_millis(n)).await;
|
||||
Ok(format!("elapsed:{}ms", n))
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
let ref lua = Lua::new();
|
||||
|
||||
let ref arc = Arc::new(AtomicI64::new(11));
|
||||
|
||||
lua.async_scope(|scope| async move {
|
||||
let ud = scope.create_userdata(MyUserData(arc.clone()))?;
|
||||
lua.globals().set("userdata", ud)?;
|
||||
lua.load(
|
||||
r#"
|
||||
assert(userdata:get_value() == 11)
|
||||
userdata:set_value(12)
|
||||
assert(userdata.sleep(5) == "elapsed:5ms")
|
||||
assert(userdata:get_value() == 12)
|
||||
"#,
|
||||
)
|
||||
.exec_async()
|
||||
.await
|
||||
})
|
||||
.await?;
|
||||
|
||||
assert_eq!(Arc::strong_count(arc), 1);
|
||||
|
||||
match lua.load("userdata:get_value()").exec_async().await {
|
||||
Err(Error::CallbackError { ref cause, .. }) => match cause.as_ref() {
|
||||
Error::CallbackDestructed => {}
|
||||
e => panic!("expected `CallbackDestructed` error cause, got {:?}", e),
|
||||
},
|
||||
r => panic!("improper return for destructed userdata: {:?}", r),
|
||||
};
|
||||
assert_eq!(hello.call_async::<_, String>("alex").await?, "hello, alex!");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
+4
-5
@@ -1,7 +1,7 @@
|
||||
use std::fs;
|
||||
use std::io;
|
||||
|
||||
use mlua::{Error, Lua, Result};
|
||||
use mlua::{Lua, Result};
|
||||
|
||||
#[test]
|
||||
fn test_chunk_path() -> Result<()> {
|
||||
@@ -14,12 +14,11 @@ fn test_chunk_path() -> Result<()> {
|
||||
return 321
|
||||
"#,
|
||||
)?;
|
||||
let i: i32 = lua.load(&temp_dir.path().join("module.lua")).eval()?;
|
||||
let i: i32 = lua.load(&*temp_dir.path().join("module.lua")).eval()?;
|
||||
assert_eq!(i, 321);
|
||||
|
||||
match lua.load(&temp_dir.path().join("module2.lua")).exec() {
|
||||
Err(Error::ExternalError(err))
|
||||
if err.downcast_ref::<io::Error>().unwrap().kind() == io::ErrorKind::NotFound => {}
|
||||
match lua.load(&*temp_dir.path().join("module2.lua")).exec() {
|
||||
Err(err) if err.downcast_ref::<io::Error>().unwrap().kind() == io::ErrorKind::NotFound => {}
|
||||
res => panic!("expected io::Error, got {:?}", res),
|
||||
};
|
||||
|
||||
|
||||
@@ -1,22 +1,28 @@
|
||||
error[E0277]: the type `UnsafeCell<mlua::lua::LuaInner>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
|
||||
--> tests/compile/lua_norefunwindsafe.rs:7:5
|
||||
|
|
||||
7 | catch_unwind(|| lua.create_table().unwrap());
|
||||
| ^^^^^^^^^^^^ `UnsafeCell<mlua::lua::LuaInner>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
|
||||
|
|
||||
= help: within `Lua`, the trait `RefUnwindSafe` is not implemented for `UnsafeCell<mlua::lua::LuaInner>`
|
||||
= note: required because it appears within the type `alloc::sync::ArcInner<UnsafeCell<mlua::lua::LuaInner>>`
|
||||
= note: required because it appears within the type `PhantomData<alloc::sync::ArcInner<UnsafeCell<mlua::lua::LuaInner>>>`
|
||||
= note: required because it appears within the type `Arc<UnsafeCell<mlua::lua::LuaInner>>`
|
||||
= note: required because it appears within the type `Lua`
|
||||
= note: required because of the requirements on the impl of `UnwindSafe` for `&Lua`
|
||||
error[E0277]: the type `UnsafeCell<mlua::lua::ExtraData>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
|
||||
--> tests/compile/lua_norefunwindsafe.rs:7:18
|
||||
|
|
||||
7 | catch_unwind(|| lua.create_table().unwrap());
|
||||
| ------------ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `UnsafeCell<mlua::lua::ExtraData>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
|
||||
| |
|
||||
| required by a bound introduced by this call
|
||||
|
|
||||
= help: within `Lua`, the trait `RefUnwindSafe` is not implemented for `UnsafeCell<mlua::lua::ExtraData>`
|
||||
= note: required because it appears within the type `ArcInner<UnsafeCell<ExtraData>>`
|
||||
= note: required because it appears within the type `PhantomData<ArcInner<UnsafeCell<ExtraData>>>`
|
||||
= note: required because it appears within the type `Arc<UnsafeCell<ExtraData>>`
|
||||
= note: required because it appears within the type `LuaInner`
|
||||
= note: required because it appears within the type `ArcInner<LuaInner>`
|
||||
= note: required because it appears within the type `PhantomData<ArcInner<LuaInner>>`
|
||||
= note: required because it appears within the type `Arc<LuaInner>`
|
||||
= note: required because it appears within the type `Lua`
|
||||
= note: required for `&Lua` to implement `UnwindSafe`
|
||||
note: required because it's used within this closure
|
||||
--> tests/compile/lua_norefunwindsafe.rs:7:18
|
||||
|
|
||||
7 | catch_unwind(|| lua.create_table().unwrap());
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
--> tests/compile/lua_norefunwindsafe.rs:7:18
|
||||
|
|
||||
7 | catch_unwind(|| lua.create_table().unwrap());
|
||||
| ^^
|
||||
note: required by a bound in `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`
|
||||
--> $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`
|
||||
|
||||
@@ -1,26 +1,25 @@
|
||||
error[E0277]: `Rc<Cell<i32>>` cannot be sent between threads safely
|
||||
--> tests/compile/non_send.rs:11:9
|
||||
|
|
||||
11 | lua.create_function(move |_, ()| {
|
||||
| _________^^^^^^^^^^^^^^^_-
|
||||
| | |
|
||||
| | `Rc<Cell<i32>>` cannot be sent between threads safely
|
||||
12 | | Ok(data.get())
|
||||
13 | | })?
|
||||
| |_____- within this `[closure@$DIR/tests/compile/non_send.rs:11:25: 13:6]`
|
||||
|
|
||||
= help: within `[closure@$DIR/tests/compile/non_send.rs:11:25: 13:6]`, the trait `Send` is not implemented for `Rc<Cell<i32>>`
|
||||
--> 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
|
||||
|
|
||||
= help: within `[closure@$DIR/tests/compile/non_send.rs:11:25: 11:37]`, the trait `Send` is not implemented for `Rc<Cell<i32>>`
|
||||
note: required because it's used within this closure
|
||||
--> tests/compile/non_send.rs:11:25
|
||||
|
|
||||
11 | lua.create_function(move |_, ()| {
|
||||
| _________________________^
|
||||
12 | | Ok(data.get())
|
||||
13 | | })?
|
||||
| |_____^
|
||||
= note: required because of the requirements on the impl of `mlua::types::MaybeSend` for `[closure@$DIR/tests/compile/non_send.rs:11:25: 13:6]`
|
||||
--> tests/compile/non_send.rs:11:25
|
||||
|
|
||||
11 | lua.create_function(move |_, ()| {
|
||||
| ^^^^^^^^^^^^
|
||||
= note: required for `[closure@$DIR/tests/compile/non_send.rs:11:25: 11:37]` to implement `mlua::types::MaybeSend`
|
||||
note: required by a bound in `Lua::create_function`
|
||||
--> src/lua.rs
|
||||
|
|
||||
| F: 'static + MaybeSend + Fn(&'lua Lua, A) -> Result<R>,
|
||||
| ^^^^^^^^^ required by this bound in `Lua::create_function`
|
||||
--> src/lua.rs
|
||||
|
|
||||
| F: 'static + MaybeSend + Fn(&'lua Lua, A) -> Result<R>,
|
||||
| ^^^^^^^^^ required by this bound in `Lua::create_function`
|
||||
|
||||
@@ -1,24 +1,30 @@
|
||||
error[E0277]: the type `UnsafeCell<mlua::lua::LuaInner>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
|
||||
--> tests/compile/ref_nounwindsafe.rs:8:5
|
||||
|
|
||||
8 | catch_unwind(move || table.set("a", "b").unwrap());
|
||||
| ^^^^^^^^^^^^ `UnsafeCell<mlua::lua::LuaInner>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
|
||||
|
|
||||
= help: within `Lua`, the trait `RefUnwindSafe` is not implemented for `UnsafeCell<mlua::lua::LuaInner>`
|
||||
= note: required because it appears within the type `alloc::sync::ArcInner<UnsafeCell<mlua::lua::LuaInner>>`
|
||||
= note: required because it appears within the type `PhantomData<alloc::sync::ArcInner<UnsafeCell<mlua::lua::LuaInner>>>`
|
||||
= note: required because it appears within the type `Arc<UnsafeCell<mlua::lua::LuaInner>>`
|
||||
= note: required because it appears within the type `Lua`
|
||||
= note: required because of the requirements on the impl of `UnwindSafe` for `&Lua`
|
||||
= note: required because it appears within the type `mlua::types::LuaRef<'_>`
|
||||
= note: required because it appears within the type `LuaTable<'_>`
|
||||
error[E0277]: the type `UnsafeCell<mlua::lua::ExtraData>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
|
||||
--> tests/compile/ref_nounwindsafe.rs:8:18
|
||||
|
|
||||
8 | catch_unwind(move || table.set("a", "b").unwrap());
|
||||
| ------------ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `UnsafeCell<mlua::lua::ExtraData>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
|
||||
| |
|
||||
| required by a bound introduced by this call
|
||||
|
|
||||
= help: within `Lua`, the trait `RefUnwindSafe` is not implemented for `UnsafeCell<mlua::lua::ExtraData>`
|
||||
= note: required because it appears within the type `ArcInner<UnsafeCell<ExtraData>>`
|
||||
= note: required because it appears within the type `PhantomData<ArcInner<UnsafeCell<ExtraData>>>`
|
||||
= note: required because it appears within the type `Arc<UnsafeCell<ExtraData>>`
|
||||
= note: required because it appears within the type `LuaInner`
|
||||
= note: required because it appears within the type `ArcInner<LuaInner>`
|
||||
= note: required because it appears within the type `PhantomData<ArcInner<LuaInner>>`
|
||||
= note: required because it appears within the type `Arc<LuaInner>`
|
||||
= note: required because it appears within the type `Lua`
|
||||
= note: required for `&Lua` to implement `UnwindSafe`
|
||||
= note: required because it appears within the type `LuaRef<'_>`
|
||||
= note: required because it appears within the type `Table<'_>`
|
||||
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());
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
--> tests/compile/ref_nounwindsafe.rs:8:18
|
||||
|
|
||||
8 | catch_unwind(move || table.set("a", "b").unwrap());
|
||||
| ^^^^^^^
|
||||
note: required by a bound in `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`
|
||||
--> $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`
|
||||
|
||||
@@ -1,15 +1,17 @@
|
||||
error[E0597]: `lua` does not live long enough
|
||||
--> tests/compile/static_callback_args.rs:12:5
|
||||
|
|
||||
12 | / lua.create_function(|_, table: Table| {
|
||||
13 | | BAD_TIME.with(|bt| {
|
||||
| |_________-
|
||||
10 | let lua = Lua::new();
|
||||
| --- binding `lua` declared here
|
||||
11 |
|
||||
12 | / lua.create_function(|_, table: Table| {
|
||||
13 | |/ BAD_TIME.with(|bt| {
|
||||
14 | || *bt.borrow_mut() = Some(table);
|
||||
15 | || });
|
||||
| ||__________- argument requires that `lua` is borrowed for `'static`
|
||||
16 | | Ok(())
|
||||
17 | | })?
|
||||
| |______^ borrowed value does not live long enough
|
||||
16 | | Ok(())
|
||||
17 | | })?
|
||||
| |_______^ borrowed value does not live long enough
|
||||
...
|
||||
32 | }
|
||||
| - `lua` dropped here while still borrowed
|
||||
@@ -17,15 +19,17 @@ error[E0597]: `lua` does not live long enough
|
||||
error[E0505]: cannot move out of `lua` because it is borrowed
|
||||
--> tests/compile/static_callback_args.rs:22:10
|
||||
|
|
||||
12 | / lua.create_function(|_, table: Table| {
|
||||
13 | | BAD_TIME.with(|bt| {
|
||||
| |_________-
|
||||
10 | let lua = Lua::new();
|
||||
| --- binding `lua` declared here
|
||||
11 |
|
||||
12 | / lua.create_function(|_, table: Table| {
|
||||
13 | |/ BAD_TIME.with(|bt| {
|
||||
14 | || *bt.borrow_mut() = Some(table);
|
||||
15 | || });
|
||||
| ||__________- argument requires that `lua` is borrowed for `'static`
|
||||
16 | | Ok(())
|
||||
17 | | })?
|
||||
| |______- borrow of `lua` occurs here
|
||||
16 | | Ok(())
|
||||
17 | | })?
|
||||
| |_______- borrow of `lua` occurs here
|
||||
...
|
||||
22 | drop(lua);
|
||||
| ^^^ move out of `lua` occurs here
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
use mlua::{Lua, Result};
|
||||
|
||||
#[test]
|
||||
fn test_debug_format() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
// Globals
|
||||
let globals = lua.globals();
|
||||
let dump = format!("{globals:#?}");
|
||||
assert!(dump.starts_with("{\n [\"_G\"] = <table"));
|
||||
|
||||
// TODO: Other cases
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
use mlua::{Error, ErrorContext, Lua, Result};
|
||||
|
||||
#[test]
|
||||
fn test_error_context() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let func = lua.create_function(|_, ()| {
|
||||
Err::<(), _>(Error::RuntimeError("runtime error".into())).context("some context")
|
||||
})?;
|
||||
lua.globals().set("func", func)?;
|
||||
|
||||
let msg = lua
|
||||
.load("local _, err = pcall(func); return tostring(err)")
|
||||
.eval::<String>()?;
|
||||
assert!(msg.contains("some context"));
|
||||
assert!(msg.contains("runtime error"));
|
||||
|
||||
let func2 = lua.create_function(|lua, ()| {
|
||||
lua.globals()
|
||||
.get::<_, String>("nonextant")
|
||||
.with_context(|_| "failed to find global")
|
||||
})?;
|
||||
lua.globals().set("func2", func2)?;
|
||||
|
||||
let msg2 = lua
|
||||
.load("local _, err = pcall(func2); return tostring(err)")
|
||||
.eval::<String>()?;
|
||||
assert!(msg2.contains("failed to find global"));
|
||||
println!("{msg2}");
|
||||
assert!(msg2.contains("error converting Lua nil to String"));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
+75
-1
@@ -126,7 +126,7 @@ fn test_function_info() -> Result<()> {
|
||||
end
|
||||
"#,
|
||||
)
|
||||
.set_name("source1")?
|
||||
.set_name("source1")
|
||||
.exec()?;
|
||||
|
||||
let function1 = globals.get::<_, Function>("function1")?;
|
||||
@@ -167,3 +167,77 @@ fn test_function_info() -> Result<()> {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[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 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:?}"),
|
||||
},
|
||||
other => panic!("incorrect result: {other:?}"),
|
||||
};
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "unstable", not(feature = "send")))]
|
||||
#[test]
|
||||
fn test_owned_function() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let f = lua
|
||||
.create_function(|_, ()| Ok("hello, world!"))?
|
||||
.into_owned();
|
||||
drop(lua);
|
||||
|
||||
// We still should be able to call the function despite Lua is dropped
|
||||
let s = f.call::<_, String>(())?;
|
||||
assert_eq!(s.to_string_lossy(), "hello, world!");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "unstable", not(feature = "send")))]
|
||||
#[test]
|
||||
fn test_owned_function_drop() -> Result<()> {
|
||||
let rc = std::sync::Arc::new(());
|
||||
|
||||
{
|
||||
let lua = Lua::new();
|
||||
|
||||
lua.set_app_data(rc.clone());
|
||||
|
||||
let f1 = lua
|
||||
.create_function(|_, ()| Ok("hello, world!"))?
|
||||
.into_owned();
|
||||
let f2 =
|
||||
lua.create_function(move |_, ()| f1.to_ref().call::<_, std::string::String>(()))?;
|
||||
assert_eq!(f2.call::<_, String>(())?.to_string_lossy(), "hello, world!");
|
||||
}
|
||||
|
||||
// Check that Lua is properly destroyed
|
||||
// It works because we collect garbage when Lua goes out of scope
|
||||
assert_eq!(std::sync::Arc::strong_count(&rc), 1);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
+53
-16
@@ -9,11 +9,9 @@ use std::sync::{Arc, Mutex};
|
||||
use mlua::{DebugEvent, Error, HookTriggers, Lua, Result, Value};
|
||||
|
||||
#[test]
|
||||
fn test_hook_triggers_bitor() {
|
||||
let trigger = HookTriggers::on_calls()
|
||||
| HookTriggers::on_returns()
|
||||
| HookTriggers::every_line()
|
||||
| HookTriggers::every_nth_instruction(5);
|
||||
fn test_hook_triggers() {
|
||||
let trigger = HookTriggers::new().on_calls().on_returns()
|
||||
| HookTriggers::new().every_line().every_nth_instruction(5);
|
||||
|
||||
assert!(trigger.on_calls);
|
||||
assert!(trigger.on_returns);
|
||||
@@ -27,7 +25,7 @@ fn test_line_counts() -> Result<()> {
|
||||
let hook_output = output.clone();
|
||||
|
||||
let lua = Lua::new();
|
||||
lua.set_hook(HookTriggers::every_line(), move |_lua, debug| {
|
||||
lua.set_hook(HookTriggers::EVERY_LINE, move |_lua, debug| {
|
||||
assert_eq!(debug.event(), DebugEvent::Line);
|
||||
hook_output.lock().unwrap().push(debug.curr_line());
|
||||
Ok(())
|
||||
@@ -59,7 +57,7 @@ fn test_function_calls() -> Result<()> {
|
||||
let hook_output = output.clone();
|
||||
|
||||
let lua = Lua::new();
|
||||
lua.set_hook(HookTriggers::on_calls(), move |_lua, debug| {
|
||||
lua.set_hook(HookTriggers::ON_CALLS, move |_lua, debug| {
|
||||
assert_eq!(debug.event(), DebugEvent::Call);
|
||||
let names = debug.names();
|
||||
let source = debug.source();
|
||||
@@ -104,7 +102,7 @@ fn test_function_calls() -> Result<()> {
|
||||
fn test_error_within_hook() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
lua.set_hook(HookTriggers::every_line(), |_lua, _debug| {
|
||||
lua.set_hook(HookTriggers::EVERY_LINE, |_lua, _debug| {
|
||||
Err(Error::RuntimeError(
|
||||
"Something happened in there!".to_string(),
|
||||
))
|
||||
@@ -136,7 +134,7 @@ fn test_limit_execution_instructions() -> Result<()> {
|
||||
|
||||
let max_instructions = AtomicI64::new(10000);
|
||||
lua.set_hook(
|
||||
HookTriggers::every_nth_instruction(30),
|
||||
HookTriggers::new().every_nth_instruction(30),
|
||||
move |_lua, debug| {
|
||||
assert_eq!(debug.event(), DebugEvent::Count);
|
||||
if max_instructions.fetch_sub(30, Ordering::Relaxed) <= 30 {
|
||||
@@ -166,11 +164,14 @@ fn test_limit_execution_instructions() -> Result<()> {
|
||||
fn test_hook_removal() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
lua.set_hook(HookTriggers::every_nth_instruction(1), |_lua, _debug| {
|
||||
Err(Error::RuntimeError(
|
||||
"this hook should've been removed by this time".to_string(),
|
||||
))
|
||||
})?;
|
||||
lua.set_hook(
|
||||
HookTriggers::new().every_nth_instruction(1),
|
||||
|_lua, _debug| {
|
||||
Err(Error::RuntimeError(
|
||||
"this hook should've been removed by this time".to_string(),
|
||||
))
|
||||
},
|
||||
)?;
|
||||
|
||||
assert!(lua.load("local x = 1").exec().is_err());
|
||||
lua.remove_hook();
|
||||
@@ -193,11 +194,11 @@ fn test_hook_swap_within_hook() -> Result<()> {
|
||||
tl.borrow()
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.set_hook(HookTriggers::every_line(), move |lua, _debug| {
|
||||
.set_hook(HookTriggers::EVERY_LINE, move |lua, _debug| {
|
||||
lua.globals().set("ok", 1i64)?;
|
||||
TL_LUA.with(|tl| {
|
||||
tl.borrow().as_ref().unwrap().set_hook(
|
||||
HookTriggers::every_line(),
|
||||
HookTriggers::EVERY_LINE,
|
||||
move |lua, _debug| {
|
||||
lua.load(
|
||||
r#"
|
||||
@@ -233,3 +234,39 @@ fn test_hook_swap_within_hook() -> Result<()> {
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_hook_threads() -> 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)?;
|
||||
|
||||
let output = Arc::new(Mutex::new(Vec::new()));
|
||||
let hook_output = output.clone();
|
||||
co.set_hook(HookTriggers::EVERY_LINE, move |_lua, debug| {
|
||||
assert_eq!(debug.event(), DebugEvent::Line);
|
||||
hook_output.lock().unwrap().push(debug.curr_line());
|
||||
Ok(())
|
||||
});
|
||||
|
||||
co.resume(())?;
|
||||
lua.remove_hook();
|
||||
|
||||
let output = output.lock().unwrap();
|
||||
if cfg!(feature = "luajit") && lua.load("jit.version_num").eval::<i64>()? >= 20100 {
|
||||
assert_eq!(*output, vec![2, 3, 4, 0, 4]);
|
||||
} else {
|
||||
assert_eq!(*output, vec![2, 3, 4]);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
+3
-3
@@ -173,7 +173,7 @@ fn test_interrupts() -> Result<()> {
|
||||
let interrupts_count = Arc::new(AtomicU64::new(0));
|
||||
let interrupts_count2 = interrupts_count.clone();
|
||||
|
||||
lua.set_interrupt(move || {
|
||||
lua.set_interrupt(move |_| {
|
||||
interrupts_count2.fetch_add(1, Ordering::Relaxed);
|
||||
Ok(VmState::Continue)
|
||||
});
|
||||
@@ -195,7 +195,7 @@ fn test_interrupts() -> Result<()> {
|
||||
//
|
||||
let yield_count = Arc::new(AtomicU64::new(0));
|
||||
let yield_count2 = yield_count.clone();
|
||||
lua.set_interrupt(move || {
|
||||
lua.set_interrupt(move |_| {
|
||||
if yield_count2.fetch_add(1, Ordering::Relaxed) == 1 {
|
||||
return Ok(VmState::Yield);
|
||||
}
|
||||
@@ -222,7 +222,7 @@ fn test_interrupts() -> Result<()> {
|
||||
//
|
||||
// Test errors in interrupts
|
||||
//
|
||||
lua.set_interrupt(|| Err(Error::RuntimeError("error from interrupt".into())));
|
||||
lua.set_interrupt(|_| Err(Error::RuntimeError("error from interrupt".into())));
|
||||
match f.call::<_, ()>(()) {
|
||||
Err(Error::CallbackError { cause, .. }) => match *cause {
|
||||
Error::RuntimeError(ref m) if m == "error from interrupt" => {}
|
||||
|
||||
+33
-5
@@ -1,11 +1,7 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use mlua::{GCMode, Lua, Result, UserData};
|
||||
use mlua::{Error, GCMode, Lua, Result, UserData};
|
||||
|
||||
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
|
||||
use mlua::Error;
|
||||
|
||||
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
|
||||
#[test]
|
||||
fn test_memory_limit() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
@@ -21,6 +17,15 @@ fn test_memory_limit() -> Result<()> {
|
||||
.into_function()?;
|
||||
f.call::<_, ()>(()).expect("should trigger no memory limit");
|
||||
|
||||
if cfg!(feature = "luajit") && cfg!(not(feature = "vendored")) {
|
||||
// we don't support setting memory limit for non-vendored luajit
|
||||
assert!(matches!(
|
||||
lua.set_memory_limit(0),
|
||||
Err(Error::MemoryLimitNotAvailable)
|
||||
));
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
lua.set_memory_limit(initial_memory + 10000)?;
|
||||
match f.call::<_, ()>(()) {
|
||||
Err(Error::MemoryError(_)) => {}
|
||||
@@ -33,6 +38,29 @@ fn test_memory_limit() -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_memory_limit_thread() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let f = lua
|
||||
.load("local t = {}; for i = 1,10000 do t[i] = i end")
|
||||
.into_function()?;
|
||||
|
||||
if cfg!(feature = "luajit") && cfg!(not(feature = "vendored")) {
|
||||
// we don't support setting memory limit for non-vendored luajit
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
lua.set_memory_limit(lua.used_memory() + 10000)?;
|
||||
let thread = lua.create_thread(f)?;
|
||||
match thread.resume::<_, ()>(()) {
|
||||
Err(Error::MemoryError(_)) => {}
|
||||
something_else => panic!("did not trigger memory error: {:?}", something_else),
|
||||
};
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_gc_control() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
[package]
|
||||
name = "rust_module"
|
||||
name = "test_module"
|
||||
version = "0.0.0"
|
||||
authors = ["Aleksandr Orlenko <zxteam@pm.me>"]
|
||||
edition = "2018"
|
||||
edition = "2021"
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib"]
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
name = "module_loader"
|
||||
version = "0.0.0"
|
||||
authors = ["Aleksandr Orlenko <zxteam@pm.me>"]
|
||||
edition = "2018"
|
||||
edition = "2021"
|
||||
|
||||
[features]
|
||||
lua54 = ["mlua/lua54"]
|
||||
|
||||
@@ -8,7 +8,7 @@ fn test_module() -> Result<()> {
|
||||
let lua = make_lua()?;
|
||||
lua.load(
|
||||
r#"
|
||||
local mod = require("rust_module")
|
||||
local mod = require("test_module")
|
||||
assert(mod.sum(2,2) == 4)
|
||||
"#,
|
||||
)
|
||||
@@ -20,8 +20,8 @@ fn test_module_multi() -> Result<()> {
|
||||
let lua = make_lua()?;
|
||||
lua.load(
|
||||
r#"
|
||||
local mod = require("rust_module")
|
||||
local mod2 = require("rust_module.second")
|
||||
local mod = require("test_module")
|
||||
local mod2 = require("test_module.second")
|
||||
assert(mod.check_userdata(mod2.userdata) == 123)
|
||||
"#,
|
||||
)
|
||||
@@ -33,7 +33,7 @@ fn test_module_error() -> Result<()> {
|
||||
let lua = make_lua()?;
|
||||
lua.load(
|
||||
r#"
|
||||
local ok, err = pcall(require, "rust_module.error")
|
||||
local ok, err = pcall(require, "test_module.error")
|
||||
assert(not ok)
|
||||
assert(string.find(tostring(err), "custom module error"))
|
||||
"#,
|
||||
@@ -55,7 +55,7 @@ fn test_module_from_thread() -> Result<()> {
|
||||
local mod
|
||||
|
||||
local co = coroutine.create(function(a, b)
|
||||
mod = require("rust_module")
|
||||
mod = require("test_module")
|
||||
assert(mod.sum(a, b) == a + b)
|
||||
end)
|
||||
|
||||
|
||||
@@ -8,12 +8,12 @@ fn used_memory(lua: &Lua, _: ()) -> LuaResult<usize> {
|
||||
Ok(lua.used_memory())
|
||||
}
|
||||
|
||||
fn check_userdata(_: &Lua, ud: MyUserData) -> LuaResult<i32> {
|
||||
Ok(ud.0)
|
||||
fn check_userdata(_: &Lua, ud: LuaAnyUserData) -> LuaResult<i32> {
|
||||
Ok(ud.borrow::<MyUserData>()?.0)
|
||||
}
|
||||
|
||||
#[mlua::lua_module]
|
||||
fn rust_module(lua: &Lua) -> LuaResult<LuaTable> {
|
||||
fn test_module(lua: &Lua) -> LuaResult<LuaTable> {
|
||||
let exports = lua.create_table()?;
|
||||
exports.set("sum", lua.create_function(sum)?)?;
|
||||
exports.set("used_memory", lua.create_function(used_memory)?)?;
|
||||
@@ -26,14 +26,14 @@ struct MyUserData(i32);
|
||||
|
||||
impl LuaUserData for MyUserData {}
|
||||
|
||||
#[mlua::lua_module]
|
||||
fn rust_module_second(lua: &Lua) -> LuaResult<LuaTable> {
|
||||
#[mlua::lua_module(name = "test_module_second")]
|
||||
fn test_module2(lua: &Lua) -> LuaResult<LuaTable> {
|
||||
let exports = lua.create_table()?;
|
||||
exports.set("userdata", lua.create_userdata(MyUserData(123))?)?;
|
||||
exports.set("userdata", MyUserData(123))?;
|
||||
Ok(exports)
|
||||
}
|
||||
|
||||
#[mlua::lua_module]
|
||||
fn rust_module_error(_: &Lua) -> LuaResult<LuaTable> {
|
||||
Err("custom module error".to_lua_err())
|
||||
fn test_module_error(_: &Lua) -> LuaResult<LuaTable> {
|
||||
Err("custom module error".into_lua_err())
|
||||
}
|
||||
|
||||
+104
@@ -356,3 +356,107 @@ fn test_scope_nonstatic_userdata_drop() -> Result<()> {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_scope_userdata_ref() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
struct MyUserData(Cell<i64>);
|
||||
|
||||
impl UserData for MyUserData {
|
||||
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
|
||||
methods.add_method("inc", |_, data, ()| {
|
||||
data.0.set(data.0.get() + 1);
|
||||
Ok(())
|
||||
});
|
||||
|
||||
methods.add_method("dec", |_, data, ()| {
|
||||
data.0.set(data.0.get() - 1);
|
||||
Ok(())
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
let data = MyUserData(Cell::new(1));
|
||||
lua.scope(|scope| {
|
||||
let ud = scope.create_userdata_ref(&data)?;
|
||||
modify_userdata(&lua, ud)
|
||||
})?;
|
||||
assert_eq!(data.0.get(), 2);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_scope_userdata_ref_mut() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
struct MyUserData(i64);
|
||||
|
||||
impl UserData for MyUserData {
|
||||
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
|
||||
methods.add_method_mut("inc", |_, data, ()| {
|
||||
data.0 += 1;
|
||||
Ok(())
|
||||
});
|
||||
|
||||
methods.add_method_mut("dec", |_, data, ()| {
|
||||
data.0 -= 1;
|
||||
Ok(())
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
let mut data = MyUserData(1);
|
||||
lua.scope(|scope| {
|
||||
let ud = scope.create_userdata_ref_mut(&mut data)?;
|
||||
modify_userdata(&lua, ud)
|
||||
})?;
|
||||
assert_eq!(data.0, 2);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_scope_any_userdata_ref() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
lua.register_userdata_type::<Cell<i64>>(|reg| {
|
||||
reg.add_method("inc", |_, data, ()| {
|
||||
data.set(data.get() + 1);
|
||||
Ok(())
|
||||
});
|
||||
|
||||
reg.add_method("dec", |_, data, ()| {
|
||||
data.set(data.get() - 1);
|
||||
Ok(())
|
||||
});
|
||||
})?;
|
||||
|
||||
let data = Cell::new(1i64);
|
||||
lua.scope(|scope| {
|
||||
let ud = scope.create_any_userdata_ref(&data)?;
|
||||
modify_userdata(&lua, ud)
|
||||
})?;
|
||||
assert_eq!(data.get(), 2);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn modify_userdata(lua: &Lua, ud: AnyUserData) -> Result<()> {
|
||||
let f: Function = lua
|
||||
.load(
|
||||
r#"
|
||||
function(u)
|
||||
u:inc()
|
||||
u:dec()
|
||||
u:inc()
|
||||
end
|
||||
"#,
|
||||
)
|
||||
.eval()?;
|
||||
|
||||
f.call(ud)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
+88
-15
@@ -1,6 +1,7 @@
|
||||
#![cfg(feature = "serialize")]
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::error::Error as StdError;
|
||||
|
||||
use mlua::{
|
||||
DeserializeOptions, Error, Lua, LuaSerdeExt, Result as LuaResult, SerializeOptions, UserData,
|
||||
@@ -9,7 +10,7 @@ use mlua::{
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[test]
|
||||
fn test_serialize() -> Result<(), Box<dyn std::error::Error>> {
|
||||
fn test_serialize() -> Result<(), Box<dyn StdError>> {
|
||||
#[derive(Serialize)]
|
||||
struct MyUserData(i64, String);
|
||||
|
||||
@@ -115,7 +116,7 @@ fn test_serialize_in_scope() -> LuaResult<()> {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_serialize_failure() -> Result<(), Box<dyn std::error::Error>> {
|
||||
fn test_serialize_failure() -> Result<(), Box<dyn StdError>> {
|
||||
#[derive(Serialize)]
|
||||
struct MyUserData(i64);
|
||||
|
||||
@@ -146,7 +147,7 @@ fn test_serialize_failure() -> Result<(), Box<dyn std::error::Error>> {
|
||||
|
||||
#[cfg(feature = "luau")]
|
||||
#[test]
|
||||
fn test_serialize_vector() -> Result<(), Box<dyn std::error::Error>> {
|
||||
fn test_serialize_vector() -> Result<(), Box<dyn StdError>> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let globals = lua.globals();
|
||||
@@ -235,7 +236,7 @@ fn test_to_value_enum() -> LuaResult<()> {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_to_value_with_options() -> Result<(), Box<dyn std::error::Error>> {
|
||||
fn test_to_value_with_options() -> Result<(), Box<dyn StdError>> {
|
||||
let lua = Lua::new();
|
||||
let globals = lua.globals();
|
||||
globals.set("null", lua.null())?;
|
||||
@@ -305,7 +306,7 @@ fn test_to_value_with_options() -> Result<(), Box<dyn std::error::Error>> {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_from_value_nested_tables() -> Result<(), Box<dyn std::error::Error>> {
|
||||
fn test_from_value_nested_tables() -> Result<(), Box<dyn StdError>> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let value = lua
|
||||
@@ -335,7 +336,7 @@ fn test_from_value_nested_tables() -> Result<(), Box<dyn std::error::Error>> {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_from_value_struct() -> Result<(), Box<dyn std::error::Error>> {
|
||||
fn test_from_value_struct() -> Result<(), Box<dyn StdError>> {
|
||||
let lua = Lua::new();
|
||||
|
||||
#[derive(Deserialize, PartialEq, Debug)]
|
||||
@@ -376,7 +377,7 @@ fn test_from_value_struct() -> Result<(), Box<dyn std::error::Error>> {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_from_value_newtype_struct() -> Result<(), Box<dyn std::error::Error>> {
|
||||
fn test_from_value_newtype_struct() -> Result<(), Box<dyn StdError>> {
|
||||
let lua = Lua::new();
|
||||
|
||||
#[derive(Deserialize, PartialEq, Debug)]
|
||||
@@ -389,38 +390,51 @@ fn test_from_value_newtype_struct() -> Result<(), Box<dyn std::error::Error>> {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_from_value_enum() -> Result<(), Box<dyn std::error::Error>> {
|
||||
fn test_from_value_enum() -> Result<(), Box<dyn StdError>> {
|
||||
let lua = Lua::new();
|
||||
lua.globals().set("null", lua.null())?;
|
||||
|
||||
#[derive(Deserialize, PartialEq, Debug)]
|
||||
enum E {
|
||||
struct UnitStruct;
|
||||
|
||||
#[derive(Deserialize, PartialEq, Debug)]
|
||||
enum E<T = ()> {
|
||||
Unit,
|
||||
Integer(u32),
|
||||
Tuple(u32, u32),
|
||||
Struct { a: u32 },
|
||||
Wrap(T),
|
||||
}
|
||||
|
||||
let value = lua.load(r#""Unit""#).eval()?;
|
||||
let got = lua.from_value(value)?;
|
||||
let got: E = lua.from_value(value)?;
|
||||
assert_eq!(E::Unit, got);
|
||||
|
||||
let value = lua.load(r#"{Integer = 1}"#).eval()?;
|
||||
let got = lua.from_value(value)?;
|
||||
let got: E = lua.from_value(value)?;
|
||||
assert_eq!(E::Integer(1), got);
|
||||
|
||||
let value = lua.load(r#"{Tuple = {1, 2}}"#).eval()?;
|
||||
let got = lua.from_value(value)?;
|
||||
let got: E = lua.from_value(value)?;
|
||||
assert_eq!(E::Tuple(1, 2), got);
|
||||
|
||||
let value = lua.load(r#"{Struct = {a = 3}}"#).eval()?;
|
||||
let got = lua.from_value(value)?;
|
||||
let got: E = lua.from_value(value)?;
|
||||
assert_eq!(E::Struct { a: 3 }, got);
|
||||
|
||||
let value = lua.load(r#"{Wrap = null}"#).eval()?;
|
||||
let got = lua.from_value(value)?;
|
||||
assert_eq!(E::Wrap(UnitStruct), got);
|
||||
|
||||
let value = lua.load(r#"{Wrap = null}"#).eval()?;
|
||||
let got = lua.from_value(value)?;
|
||||
assert_eq!(E::Wrap(()), got);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_from_value_enum_untagged() -> Result<(), Box<dyn std::error::Error>> {
|
||||
fn test_from_value_enum_untagged() -> Result<(), Box<dyn StdError>> {
|
||||
let lua = Lua::new();
|
||||
lua.globals().set("null", lua.null())?;
|
||||
|
||||
@@ -460,7 +474,7 @@ fn test_from_value_enum_untagged() -> Result<(), Box<dyn std::error::Error>> {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_from_value_with_options() -> Result<(), Box<dyn std::error::Error>> {
|
||||
fn test_from_value_with_options() -> Result<(), Box<dyn StdError>> {
|
||||
let lua = Lua::new();
|
||||
|
||||
// Deny unsupported types by default
|
||||
@@ -515,3 +529,62 @@ fn test_from_value_with_options() -> Result<(), Box<dyn std::error::Error>> {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_from_value_userdata() -> Result<(), Box<dyn StdError>> {
|
||||
let lua = Lua::new();
|
||||
|
||||
// Tuple struct
|
||||
#[derive(Serialize, Deserialize)]
|
||||
struct MyUserData(i64, String);
|
||||
|
||||
impl UserData for MyUserData {}
|
||||
|
||||
let ud = lua.create_ser_userdata(MyUserData(123, "test userdata".into()))?;
|
||||
|
||||
match lua.from_value::<MyUserData>(Value::UserData(ud)) {
|
||||
Ok(_) => {}
|
||||
Err(err) => panic!("expected no errors, got {err:?}"),
|
||||
};
|
||||
|
||||
// Newtype struct
|
||||
#[derive(Serialize, Deserialize)]
|
||||
struct NewtypeUserdata(String);
|
||||
|
||||
impl UserData for NewtypeUserdata {}
|
||||
|
||||
let ud = lua.create_ser_userdata(NewtypeUserdata("newtype userdata".into()))?;
|
||||
|
||||
match lua.from_value::<NewtypeUserdata>(Value::UserData(ud)) {
|
||||
Ok(_) => {}
|
||||
Err(err) => panic!("expected no errors, got {err:?}"),
|
||||
};
|
||||
|
||||
// Option
|
||||
#[derive(Serialize, Deserialize)]
|
||||
struct UnitUserdata;
|
||||
|
||||
impl UserData for UnitUserdata {}
|
||||
|
||||
let ud = lua.create_ser_userdata(UnitUserdata)?;
|
||||
|
||||
match lua.from_value::<Option<()>>(Value::UserData(ud)) {
|
||||
Ok(Some(_)) => {}
|
||||
Ok(_) => panic!("expected `Some`, got `None`"),
|
||||
Err(err) => panic!("expected no errors, got {err:?}"),
|
||||
};
|
||||
|
||||
// Destructed userdata with skip option
|
||||
let ud = lua.create_ser_userdata(NewtypeUserdata("newtype userdata".into()))?;
|
||||
let _ = ud.take::<NewtypeUserdata>()?;
|
||||
|
||||
match lua.from_value_with::<()>(
|
||||
Value::UserData(ud),
|
||||
DeserializeOptions::new().deny_unsupported_types(false),
|
||||
) {
|
||||
Ok(_) => {}
|
||||
Err(err) => panic!("expected no errors, got {err:?}"),
|
||||
};
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -83,3 +83,37 @@ fn test_string_hash() -> Result<()> {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_string_debug() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
// Valid utf8
|
||||
let s = lua.create_string("hello")?;
|
||||
assert_eq!(format!("{s:?}"), r#""hello""#);
|
||||
|
||||
// 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""#);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "unstable", not(feature = "send")))]
|
||||
#[test]
|
||||
fn test_owned_string() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let s = lua.create_string("hello, world!")?.into_owned();
|
||||
drop(lua);
|
||||
|
||||
// Shortcuts
|
||||
assert_eq!(s.as_bytes(), b"hello, world!");
|
||||
assert_eq!(s.to_str()?, "hello, world!");
|
||||
assert_eq!(format!("{s:?}"), "\"hello, world!\"");
|
||||
|
||||
// Access via reference
|
||||
assert_eq!(s.to_ref().to_string_lossy(), "hello, world!");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -152,6 +152,53 @@ fn test_table_push_pop() -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_table_clear() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
// Check readonly error
|
||||
#[cfg(feature = "luau")]
|
||||
{
|
||||
let t = lua.create_table()?;
|
||||
t.set_readonly(true);
|
||||
assert!(matches!(
|
||||
t.clear(),
|
||||
Err(Error::RuntimeError(err)) if err.contains("attempt to modify a readonly table")
|
||||
));
|
||||
}
|
||||
|
||||
let t = lua.create_table()?;
|
||||
// Set array and hash parts
|
||||
t.push("abc")?;
|
||||
t.push("bcd")?;
|
||||
t.set("a", "1")?;
|
||||
t.set("b", "2")?;
|
||||
t.clear()?;
|
||||
assert_eq!(t.len()?, 0);
|
||||
assert_eq!(t.pairs::<Value, Value>().count(), 0);
|
||||
|
||||
// Test table with metamethods
|
||||
let t2 = lua
|
||||
.load(
|
||||
r#"
|
||||
setmetatable({1, 2, 3, a = "1"}, {
|
||||
__index = function() error("index error") end,
|
||||
__newindex = function() error("newindex error") end,
|
||||
__len = function() error("len error") end,
|
||||
__pairs = function() error("pairs error") end,
|
||||
})
|
||||
"#,
|
||||
)
|
||||
.eval::<Table>()?;
|
||||
assert_eq!(t2.raw_len(), 3);
|
||||
t2.clear()?;
|
||||
assert_eq!(t2.raw_len(), 0);
|
||||
assert_eq!(t2.raw_get::<_, Value>("a")?, Value::Nil);
|
||||
assert_ne!(t2.get_metatable(), None);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_table_sequence_from() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
@@ -345,3 +392,17 @@ fn test_table_call() -> Result<()> {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "unstable", not(feature = "send")))]
|
||||
#[test]
|
||||
fn test_owned_table() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let table = lua.create_table()?.into_owned();
|
||||
drop(lua);
|
||||
|
||||
table.to_ref().set("abc", 123)?;
|
||||
assert_eq!(table.to_ref().get::<_, i64>("abc")?, 123);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
+9
-16
@@ -308,7 +308,7 @@ fn test_error() -> Result<()> {
|
||||
.exec()?;
|
||||
|
||||
let rust_error_function =
|
||||
lua.create_function(|_, ()| -> Result<()> { Err(TestError.to_lua_err()) })?;
|
||||
lua.create_function(|_, ()| -> Result<()> { Err(TestError.into_lua_err()) })?;
|
||||
globals.set("rust_error_function", rust_error_function)?;
|
||||
|
||||
let no_error = globals.get::<_, Function>("no_error")?;
|
||||
@@ -506,7 +506,7 @@ fn test_result_conversions() -> Result<()> {
|
||||
|
||||
let err = lua.create_function(|_, ()| {
|
||||
Ok(Err::<String, _>(
|
||||
"only through failure can we succeed".to_lua_err(),
|
||||
"only through failure can we succeed".into_lua_err(),
|
||||
))
|
||||
})?;
|
||||
let ok = lua.create_function(|_, ()| Ok(Ok::<_, Error>("!".to_owned())))?;
|
||||
@@ -730,9 +730,9 @@ fn test_set_metatable_nil() -> Result<()> {
|
||||
fn test_named_registry_value() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
lua.set_named_registry_value::<_, i32>("test", 42)?;
|
||||
lua.set_named_registry_value::<i32>("test", 42)?;
|
||||
let f = lua.create_function(move |lua, ()| {
|
||||
assert_eq!(lua.named_registry_value::<_, i32>("test")?, 42);
|
||||
assert_eq!(lua.named_registry_value::<i32>("test")?, 42);
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
@@ -1072,7 +1072,7 @@ fn test_chunk_env() -> Result<()> {
|
||||
test_var = 1
|
||||
"#,
|
||||
)
|
||||
.set_environment(env1.clone())?
|
||||
.set_environment(env1.clone())
|
||||
.exec()?;
|
||||
|
||||
lua.load(
|
||||
@@ -1081,18 +1081,11 @@ fn test_chunk_env() -> Result<()> {
|
||||
test_var = 2
|
||||
"#,
|
||||
)
|
||||
.set_environment(env2.clone())?
|
||||
.set_environment(env2.clone())
|
||||
.exec()?;
|
||||
|
||||
assert_eq!(
|
||||
lua.load("test_var").set_environment(env1)?.eval::<i32>()?,
|
||||
1
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
lua.load("test_var").set_environment(env2)?.eval::<i32>()?,
|
||||
2
|
||||
);
|
||||
assert_eq!(lua.load("test_var").set_environment(env1).eval::<i32>()?, 1);
|
||||
assert_eq!(lua.load("test_var").set_environment(env2).eval::<i32>()?, 2);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -1227,7 +1220,7 @@ fn test_inspect_stack() -> Result<()> {
|
||||
assert(logline("world") == '[string "chunk"]:12 world')
|
||||
"#,
|
||||
)
|
||||
.set_name("chunk")?
|
||||
.set_name("chunk")
|
||||
.exec()?;
|
||||
|
||||
Ok(())
|
||||
|
||||
+173
-31
@@ -1,3 +1,4 @@
|
||||
use std::string::String as StdString;
|
||||
use std::sync::Arc;
|
||||
#[cfg(not(feature = "parking_lot"))]
|
||||
use std::sync::{Mutex, RwLock};
|
||||
@@ -12,12 +13,12 @@ use std::{cell::RefCell, rc::Rc};
|
||||
use std::sync::atomic::{AtomicI64, Ordering};
|
||||
|
||||
use mlua::{
|
||||
AnyUserData, Error, ExternalError, Function, Lua, MetaMethod, Nil, Result, String, UserData,
|
||||
UserDataFields, UserDataMethods, Value,
|
||||
AnyUserData, AnyUserDataExt, Error, ExternalError, Function, Lua, MetaMethod, Nil, Result,
|
||||
String, UserData, UserDataFields, UserDataMethods, UserDataRef, Value,
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn test_user_data() -> Result<()> {
|
||||
fn test_userdata() -> Result<()> {
|
||||
struct UserData1(i64);
|
||||
struct UserData2(Box<i64>);
|
||||
|
||||
@@ -100,20 +101,25 @@ fn test_metamethods() -> Result<()> {
|
||||
methods.add_method("get", |_, data, ()| Ok(data.0));
|
||||
methods.add_meta_function(
|
||||
MetaMethod::Add,
|
||||
|_, (lhs, rhs): (MyUserData, MyUserData)| Ok(MyUserData(lhs.0 + rhs.0)),
|
||||
|_, (lhs, rhs): (UserDataRef<Self>, UserDataRef<Self>)| {
|
||||
Ok(MyUserData(lhs.0 + rhs.0))
|
||||
},
|
||||
);
|
||||
methods.add_meta_function(
|
||||
MetaMethod::Sub,
|
||||
|_, (lhs, rhs): (MyUserData, MyUserData)| Ok(MyUserData(lhs.0 - rhs.0)),
|
||||
|_, (lhs, rhs): (UserDataRef<Self>, UserDataRef<Self>)| {
|
||||
Ok(MyUserData(lhs.0 - rhs.0))
|
||||
},
|
||||
);
|
||||
methods.add_meta_function(
|
||||
MetaMethod::Eq,
|
||||
|_, (lhs, rhs): (UserDataRef<Self>, UserDataRef<Self>)| Ok(lhs.0 == rhs.0),
|
||||
);
|
||||
methods.add_meta_function(MetaMethod::Eq, |_, (lhs, rhs): (MyUserData, MyUserData)| {
|
||||
Ok(lhs.0 == rhs.0)
|
||||
});
|
||||
methods.add_meta_method(MetaMethod::Index, |_, data, index: String| {
|
||||
if index.to_str()? == "inner" {
|
||||
Ok(data.0)
|
||||
} else {
|
||||
Err("no such custom index".to_lua_err())
|
||||
Err("no such custom index".into_lua_err())
|
||||
}
|
||||
});
|
||||
#[cfg(any(
|
||||
@@ -124,13 +130,14 @@ fn test_metamethods() -> Result<()> {
|
||||
))]
|
||||
methods.add_meta_method(MetaMethod::Pairs, |lua, data, ()| {
|
||||
use std::iter::FromIterator;
|
||||
let stateless_iter = lua.create_function(|_, (data, i): (MyUserData, i64)| {
|
||||
let i = i + 1;
|
||||
if i <= data.0 {
|
||||
return Ok(mlua::Variadic::from_iter(vec![i, i]));
|
||||
}
|
||||
return Ok(mlua::Variadic::new());
|
||||
})?;
|
||||
let stateless_iter =
|
||||
lua.create_function(|_, (data, i): (UserDataRef<Self>, i64)| {
|
||||
let i = i + 1;
|
||||
if i <= data.0 {
|
||||
return Ok(mlua::Variadic::from_iter(vec![i, i]));
|
||||
}
|
||||
return Ok(mlua::Variadic::new());
|
||||
})?;
|
||||
Ok((stateless_iter, data.clone(), 0))
|
||||
});
|
||||
}
|
||||
@@ -142,7 +149,9 @@ fn test_metamethods() -> Result<()> {
|
||||
globals.set("userdata2", MyUserData(3))?;
|
||||
globals.set("userdata3", MyUserData(3))?;
|
||||
assert_eq!(
|
||||
lua.load("userdata1 + userdata2").eval::<MyUserData>()?.0,
|
||||
lua.load("userdata1 + userdata2")
|
||||
.eval::<UserDataRef<MyUserData>>()?
|
||||
.0,
|
||||
10
|
||||
);
|
||||
|
||||
@@ -166,7 +175,12 @@ fn test_metamethods() -> Result<()> {
|
||||
)
|
||||
.eval::<Function>()?;
|
||||
|
||||
assert_eq!(lua.load("userdata1 - userdata2").eval::<MyUserData>()?.0, 4);
|
||||
assert_eq!(
|
||||
lua.load("userdata1 - userdata2")
|
||||
.eval::<UserDataRef<MyUserData>>()?
|
||||
.0,
|
||||
4
|
||||
);
|
||||
assert_eq!(lua.load("userdata1:get()").eval::<i64>()?, 7);
|
||||
assert_eq!(lua.load("userdata2.inner").eval::<i64>()?, 3);
|
||||
assert!(lua.load("userdata2.nonexist_field").eval::<()>().is_err());
|
||||
@@ -305,21 +319,19 @@ fn test_userdata_take() -> Result<()> {
|
||||
fn check_userdata_take(lua: &Lua, userdata: AnyUserData, rc: Arc<i64>) -> Result<()> {
|
||||
lua.globals().set("userdata", userdata.clone())?;
|
||||
assert_eq!(Arc::strong_count(&rc), 3);
|
||||
let userdata_copy = userdata.clone();
|
||||
{
|
||||
let _value = userdata.borrow::<MyUserdata>()?;
|
||||
// We should not be able to take userdata if it's borrowed
|
||||
match userdata_copy.take::<MyUserdata>() {
|
||||
match userdata.take::<MyUserdata>() {
|
||||
Err(Error::UserDataBorrowMutError) => {}
|
||||
r => panic!("expected `UserDataBorrowMutError` error, got {:?}", r),
|
||||
}
|
||||
}
|
||||
|
||||
let value = userdata_copy.take::<MyUserdata>()?;
|
||||
let value = userdata.take::<MyUserdata>()?;
|
||||
assert_eq!(*value.0, 18);
|
||||
drop(value);
|
||||
lua.gc_collect()?;
|
||||
assert_eq!(Arc::strong_count(&rc), 1);
|
||||
assert_eq!(Arc::strong_count(&rc), 2);
|
||||
|
||||
match userdata.borrow::<MyUserdata>() {
|
||||
Err(Error::UserDataDestructed) => {}
|
||||
@@ -332,6 +344,13 @@ fn test_userdata_take() -> Result<()> {
|
||||
},
|
||||
r => panic!("improper return for destructed userdata: {:?}", r),
|
||||
}
|
||||
|
||||
drop(userdata);
|
||||
lua.globals().raw_remove("userdata")?;
|
||||
lua.gc_collect()?;
|
||||
lua.gc_collect()?;
|
||||
assert_eq!(Arc::strong_count(&rc), 1);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -403,9 +422,9 @@ fn test_user_values() -> Result<()> {
|
||||
ud.set_named_user_value("name", "alex")?;
|
||||
ud.set_named_user_value("age", 10)?;
|
||||
|
||||
assert_eq!(ud.get_named_user_value::<_, String>("name")?, "alex");
|
||||
assert_eq!(ud.get_named_user_value::<_, i32>("age")?, 10);
|
||||
assert_eq!(ud.get_named_user_value::<_, Value>("nonexist")?, Value::Nil);
|
||||
assert_eq!(ud.get_named_user_value::<String>("name")?, "alex");
|
||||
assert_eq!(ud.get_named_user_value::<i32>("age")?, 10);
|
||||
assert_eq!(ud.get_named_user_value::<Value>("nonexist")?, Value::Nil);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -529,7 +548,7 @@ fn test_metatable() -> Result<()> {
|
||||
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
|
||||
methods.add_function("my_type_name", |_, data: AnyUserData| {
|
||||
let metatable = data.get_metatable()?;
|
||||
metatable.get::<_, String>("__type_name")
|
||||
metatable.get::<String>("__type_name")
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -547,7 +566,7 @@ fn test_metatable() -> Result<()> {
|
||||
let ud: AnyUserData = globals.get("ud")?;
|
||||
let metatable = ud.get_metatable()?;
|
||||
|
||||
match metatable.get::<_, Value>("__gc") {
|
||||
match metatable.get::<Value>("__gc") {
|
||||
Ok(_) => panic!("expected MetaMethodRestricted, got no error"),
|
||||
Err(Error::MetaMethodRestricted(_)) => {}
|
||||
Err(e) => panic!("expected MetaMethodRestricted, got {:?}", e),
|
||||
@@ -561,11 +580,10 @@ fn test_metatable() -> Result<()> {
|
||||
|
||||
let mut methods = metatable
|
||||
.pairs()
|
||||
.into_iter()
|
||||
.map(|kv: Result<(_, Value)>| Ok(kv?.0))
|
||||
.collect::<Result<Vec<_>>>()?;
|
||||
methods.sort_by_cached_key(|k| k.name().to_owned());
|
||||
assert_eq!(methods, vec![MetaMethod::Index, "__type_name".into()]);
|
||||
methods.sort();
|
||||
assert_eq!(methods, vec!["__index", "__type_name"]);
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
struct MyUserData2(i64);
|
||||
@@ -697,3 +715,127 @@ fn test_userdata_proxy() -> Result<()> {
|
||||
)
|
||||
.exec()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_any_userdata() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
lua.register_userdata_type::<StdString>(|reg| {
|
||||
reg.add_method("get", |_, this, ()| Ok(this.clone()));
|
||||
reg.add_method_mut("concat", |_, this, s: String| {
|
||||
this.push_str(&s.to_string_lossy());
|
||||
Ok(())
|
||||
});
|
||||
})?;
|
||||
|
||||
let ud = lua.create_any_userdata("hello".to_string())?;
|
||||
assert_eq!(&*ud.borrow::<StdString>()?, "hello");
|
||||
|
||||
lua.globals().set("ud", ud)?;
|
||||
lua.load(
|
||||
r#"
|
||||
assert(ud:get() == "hello")
|
||||
ud:concat(", world")
|
||||
assert(ud:get() == "hello, world")
|
||||
"#,
|
||||
)
|
||||
.exec()
|
||||
.unwrap();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_userdata_ext() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
struct MyUserData(u32);
|
||||
|
||||
impl UserData for MyUserData {
|
||||
fn add_fields<'lua, F: UserDataFields<'lua, Self>>(fields: &mut F) {
|
||||
fields.add_field_method_get("n", |_, this| Ok(this.0));
|
||||
fields.add_field_method_set("n", |_, this, val| {
|
||||
this.0 = val;
|
||||
Ok(())
|
||||
});
|
||||
}
|
||||
|
||||
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
|
||||
methods.add_meta_method(MetaMethod::Call, |_, _this, ()| Ok("called"));
|
||||
methods.add_method_mut("add", |_, this, x: u32| {
|
||||
this.0 += x;
|
||||
Ok(())
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
let ud = lua.create_userdata(MyUserData(123))?;
|
||||
|
||||
assert_eq!(ud.get::<_, u32>("n")?, 123);
|
||||
ud.set("n", 321)?;
|
||||
assert_eq!(ud.get::<_, u32>("n")?, 321);
|
||||
match ud.get::<_, u32>("non-existent") {
|
||||
Err(Error::RuntimeError(_)) => {}
|
||||
r => panic!("expected RuntimeError, got {r:?}"),
|
||||
}
|
||||
match ud.set::<_, u32>("non-existent", 123) {
|
||||
Err(Error::RuntimeError(_)) => {}
|
||||
r => panic!("expected RuntimeError, got {r:?}"),
|
||||
}
|
||||
|
||||
assert_eq!(ud.call::<_, String>(())?, "called");
|
||||
|
||||
ud.call_method("add", 2)?;
|
||||
assert_eq!(ud.get::<_, u32>("n")?, 323);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_userdata_method_errors() -> Result<()> {
|
||||
struct MyUserData(i64);
|
||||
|
||||
impl UserData for MyUserData {
|
||||
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
|
||||
methods.add_method("get_value", |_, data, ()| Ok(data.0));
|
||||
}
|
||||
}
|
||||
|
||||
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")
|
||||
));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "unstable", not(feature = "send")))]
|
||||
#[test]
|
||||
fn test_owned_userdata() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let ud = lua.create_any_userdata("abc")?.into_owned();
|
||||
drop(lua);
|
||||
|
||||
assert_eq!(*ud.borrow::<&str>()?, "abc");
|
||||
*ud.borrow_mut()? = "cba";
|
||||
assert_eq!(*ud.to_ref().borrow::<&str>()?, "cba");
|
||||
assert!(matches!(
|
||||
ud.borrow::<i64>(),
|
||||
Err(Error::UserDataTypeMismatch)
|
||||
));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user