mirror of
https://github.com/mlua-rs/mlua
synced 2026-06-08 16:05:43 +00:00
Compare commits
59 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 03b6dfb2c3 | |||
| cf1cb31150 | |||
| 7d586f52f2 | |||
| e85818e199 | |||
| 16bec29274 | |||
| 5a135a331a | |||
| e7b712e29f | |||
| d5483988d2 | |||
| 0f5c68dcf8 | |||
| 8ab0ccf11c | |||
| 9596f2e9ee | |||
| 1dc32452e6 | |||
| 9785722d61 | |||
| c905a34b1d | |||
| a1089dbf95 | |||
| baf25e263f | |||
| 3abf73dee5 | |||
| 4adebd31f9 | |||
| cea2d7fd15 | |||
| e0224ab159 | |||
| 6dee339783 | |||
| b674d7906d | |||
| 3d7796de55 | |||
| 4306e6e978 | |||
| 68e65a8ffe | |||
| 2efc637ab9 | |||
| 22e748557c | |||
| 77effb5055 | |||
| 1c66a02878 | |||
| 1ac98e7d16 | |||
| bbd2fe06e1 | |||
| d951cb503f | |||
| bbd2488f79 | |||
| 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 |
@@ -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
|
||||
|
||||
@@ -9,7 +9,7 @@ jobs:
|
||||
matrix:
|
||||
os: [ubuntu-22.04, macos-latest, windows-latest]
|
||||
rust: [stable]
|
||||
lua: [lua54, lua53, lua52, lua51, luajit, luau]
|
||||
lua: [lua54, lua53, lua52, lua51, luajit, luau, luau-jit]
|
||||
include:
|
||||
- os: ubuntu-22.04
|
||||
target: x86_64-unknown-linux-gnu
|
||||
@@ -104,7 +104,7 @@ jobs:
|
||||
matrix:
|
||||
os: [ubuntu-22.04, macos-latest, windows-latest]
|
||||
rust: [stable, nightly]
|
||||
lua: [lua54, lua53, lua52, lua51, luajit, luajit52, luau]
|
||||
lua: [lua54, lua53, lua52, lua51, luajit, luajit52, luau, luau-jit]
|
||||
include:
|
||||
- os: ubuntu-22.04
|
||||
target: x86_64-unknown-linux-gnu
|
||||
@@ -118,11 +118,12 @@ 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,unstable"
|
||||
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' }}
|
||||
@@ -139,7 +140,7 @@ jobs:
|
||||
matrix:
|
||||
os: [ubuntu-22.04]
|
||||
rust: [nightly]
|
||||
lua: [lua54, lua53, lua52, lua51, luajit, luau]
|
||||
lua: [lua54, lua53, lua52, lua51, luajit, luau, luau-jit]
|
||||
include:
|
||||
- os: ubuntu-22.04
|
||||
target: x86_64-unknown-linux-gnu
|
||||
@@ -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,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 }}")
|
||||
@@ -220,7 +222,7 @@ jobs:
|
||||
runs-on: ubuntu-22.04
|
||||
strategy:
|
||||
matrix:
|
||||
lua: [lua54, lua53, lua52, lua51, luajit, luau]
|
||||
lua: [lua54, lua53, lua52, lua51, luajit, luau, luau-jit]
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
|
||||
@@ -4,3 +4,4 @@ Cargo.lock
|
||||
|
||||
.vscode/
|
||||
.DS_Store
|
||||
.stignore
|
||||
|
||||
@@ -1,3 +1,32 @@
|
||||
## v0.9.0-beta.3
|
||||
|
||||
- Added `OwnedAnyUserData::take()`
|
||||
- Switch to `DeserializeOwned`
|
||||
- Overwrite error context when called multiple times
|
||||
- New feature flag `luau-jit` to enable (experimental) Luau codegen backend
|
||||
- Set `__name` field in userdata metatable
|
||||
- Added `Value::to_string()` method similar to `luaL_tolstring`
|
||||
- Lua 5.4.6
|
||||
- Application data container now allows to mutably and immutably borrow different types at the same time
|
||||
- Performance optimizations
|
||||
- Support getting and setting environment for Lua functions.
|
||||
- Added `UserDataFields::add_field()` method to add static fields to UserData
|
||||
|
||||
Breaking changes:
|
||||
- Require environment to be a `Table` instead of `Value` in Chunks.
|
||||
- `AsChunk::env()` renamed to `AsChunk::environment()`
|
||||
|
||||
## 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:
|
||||
|
||||
+17
-24
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "mlua"
|
||||
version = "0.9.0-beta.1" # remember to update html_root_url and mlua_derive
|
||||
version = "0.9.0-beta.3" # remember to update mlua_derive
|
||||
authors = ["Aleksandr Orlenko <zxteam@pm.me>", "kyren <catherine@chucklefish.org>"]
|
||||
edition = "2021"
|
||||
repository = "https://github.com/khvzak/mlua"
|
||||
@@ -9,8 +9,6 @@ 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.
|
||||
@@ -23,48 +21,43 @@ 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"]
|
||||
async = ["futures-core", "futures-task", "futures-util"]
|
||||
lua54 = ["ffi/lua54"]
|
||||
lua53 = ["ffi/lua53"]
|
||||
lua52 = ["ffi/lua52"]
|
||||
lua51 = ["ffi/lua51"]
|
||||
luajit = ["ffi/luajit"]
|
||||
luajit52 = ["luajit", "ffi/luajit52"]
|
||||
luau = ["ffi/luau"]
|
||||
luau-jit = ["luau", "ffi/luau-codegen"]
|
||||
vendored = ["ffi/vendored"]
|
||||
module = ["mlua_derive", "ffi/module"]
|
||||
async = ["futures-util"]
|
||||
send = []
|
||||
serialize = ["serde", "erased-serde", "serde-value"]
|
||||
macros = ["mlua_derive/macros"]
|
||||
unstable = []
|
||||
|
||||
[dependencies]
|
||||
mlua_derive = { version = "=0.9.0-beta.1", optional = true, path = "mlua_derive" }
|
||||
mlua_derive = { version = "=0.9.0-beta.2", 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"
|
||||
futures-core = { version = "0.3.5", optional = true }
|
||||
futures-task = { version = "0.3.5", optional = true }
|
||||
futures-util = { version = "0.3.5", optional = true }
|
||||
futures-util = { version = "0.3", optional = true, default-features = false, features = ["std"] }
|
||||
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.2.0", path = "mlua-sys" }
|
||||
|
||||
[dev-dependencies]
|
||||
rustyline = "11.0"
|
||||
criterion = { version = "0.4", features = ["html_reports", "async_tokio"] }
|
||||
criterion = { version = "0.5", features = ["async_tokio"] }
|
||||
trybuild = "1.0"
|
||||
futures = "0.3.5"
|
||||
hyper = { version = "0.14", features = ["client", "server"] }
|
||||
|
||||
@@ -45,6 +45,7 @@ Below is a list of the available feature flags. By default `mlua` does not enabl
|
||||
* `luajit`: activate [LuaJIT] support
|
||||
* `luajit52`: activate [LuaJIT] support with partial compatibility with Lua 5.2
|
||||
* `luau`: activate [Luau] support (auto vendored mode)
|
||||
* `luau-jit`: activate [Luau] support with experimental jit backend. This is unstable feature and not recommended to use.
|
||||
* `vendored`: build static Lua(JIT) library from sources during `mlua` compilation using [lua-src] or [luajit-src] crates
|
||||
* `module`: enable module mode (building loadable `cdylib` library for Lua)
|
||||
* `async`: enable async/await support (any executor can be used, eg. [tokio] or [async-std])
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
[package]
|
||||
name = "mlua-sys"
|
||||
version = "0.2.0"
|
||||
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"]
|
||||
luau-codegen = ["luau"]
|
||||
vendored = ["lua-src", "luajit-src"]
|
||||
module = []
|
||||
|
||||
[dependencies]
|
||||
|
||||
[build-dependencies]
|
||||
cc = "1.0"
|
||||
cfg-if = "1.0"
|
||||
pkg-config = "0.3.17"
|
||||
lua-src = { version = ">= 546.0.0, < 550.0.0", optional = true }
|
||||
luajit-src = { version = ">= 210.4.0, < 220.0.0", optional = true }
|
||||
luau0-src = { version = "0.5.8", 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
|
||||
@@ -5,22 +5,25 @@ use std::path::PathBuf;
|
||||
pub fn probe_lua() -> Option<PathBuf> {
|
||||
#[cfg(feature = "lua54")]
|
||||
let artifacts = lua_src::Build::new().build(lua_src::Lua54);
|
||||
|
||||
#[cfg(feature = "lua53")]
|
||||
let artifacts = lua_src::Build::new().build(lua_src::Lua53);
|
||||
|
||||
#[cfg(feature = "lua52")]
|
||||
let artifacts = lua_src::Build::new().build(lua_src::Lua52);
|
||||
|
||||
#[cfg(feature = "lua51")]
|
||||
let artifacts = lua_src::Build::new().build(lua_src::Lua51);
|
||||
|
||||
#[cfg(feature = "luajit")]
|
||||
let artifacts = {
|
||||
let mut builder = luajit_src::Build::new();
|
||||
if cfg!(feature = "luajit52") {
|
||||
builder.lua52compat(true);
|
||||
}
|
||||
builder.build()
|
||||
};
|
||||
let artifacts = luajit_src::Build::new()
|
||||
.lua52compat(cfg!(feature = "luajit52"))
|
||||
.build();
|
||||
|
||||
#[cfg(feature = "luau")]
|
||||
let artifacts = luau0_src::Build::new().build();
|
||||
let artifacts = luau0_src::Build::new()
|
||||
.enable_codegen(cfg!(feature = "luau-codegen"))
|
||||
.build();
|
||||
|
||||
artifacts.print_cargo_metadata();
|
||||
|
||||
@@ -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,44 +1,52 @@
|
||||
//! 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
|
||||
// add fast paths for low alignment values.
|
||||
// Copied from https://github.com/rust-lang/rust/blob/master/library/std/src/sys/common/alloc.rs
|
||||
#[cfg(all(any(
|
||||
#[cfg(any(
|
||||
target_arch = "x86",
|
||||
target_arch = "arm",
|
||||
target_arch = "mips",
|
||||
@@ -50,9 +58,10 @@ pub const LUA_TRACEBACK_STACK: c_int = 11;
|
||||
target_arch = "hexagon",
|
||||
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(
|
||||
#[cfg(any(
|
||||
target_arch = "x86_64",
|
||||
target_arch = "aarch64",
|
||||
target_arch = "mips64",
|
||||
@@ -60,45 +69,36 @@ pub const SYS_MIN_ALIGN: usize = 8;
|
||||
target_arch = "sparc64",
|
||||
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(
|
||||
#[cfg(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![
|
||||
lua_atpanic as _,
|
||||
lua_isuserdata as _,
|
||||
lua_tocfunction as _,
|
||||
luaL_loadstring as _,
|
||||
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;
|
||||
@@ -486,10 +486,10 @@ pub unsafe fn luaL_traceback(
|
||||
lua_concat(L, lua_gettop(L) - top);
|
||||
}
|
||||
|
||||
pub unsafe fn luaL_tolstring(L: *mut lua_State, idx: c_int, len: *mut usize) -> *const c_char {
|
||||
pub unsafe fn luaL_tolstring(L: *mut lua_State, mut idx: c_int, len: *mut usize) -> *const c_char {
|
||||
idx = lua_absindex(L, idx);
|
||||
if luaL_callmeta(L, idx, cstr!("__tostring")) == 0 {
|
||||
let t = lua_type(L, idx);
|
||||
match t {
|
||||
match lua_type(L, idx) {
|
||||
LUA_TNIL => {
|
||||
lua_pushliteral(L, "nil");
|
||||
}
|
||||
@@ -503,7 +503,7 @@ pub unsafe fn luaL_tolstring(L: *mut lua_State, idx: c_int, len: *mut usize) ->
|
||||
lua_pushliteral(L, "true");
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
t => {
|
||||
let tt = luaL_getmetafield(L, idx, cstr!("__name"));
|
||||
let name = if tt == LUA_TSTRING {
|
||||
lua_tostring(L, -1)
|
||||
@@ -512,7 +512,7 @@ pub unsafe fn luaL_tolstring(L: *mut lua_State, idx: c_int, len: *mut usize) ->
|
||||
};
|
||||
lua_pushfstring(L, cstr!("%s: %p"), name, lua_topointer(L, idx));
|
||||
if tt != LUA_TNIL {
|
||||
lua_replace(L, -2);
|
||||
lua_replace(L, -2); // remove '__name'
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -205,10 +205,10 @@ pub unsafe fn luaL_newmetatable(L: *mut lua_State, tname: *const c_char) -> c_in
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe fn luaL_tolstring(L: *mut lua_State, idx: c_int, len: *mut usize) -> *const c_char {
|
||||
pub unsafe fn luaL_tolstring(L: *mut lua_State, mut idx: c_int, len: *mut usize) -> *const c_char {
|
||||
idx = lua_absindex(L, idx);
|
||||
if luaL_callmeta(L, idx, cstr!("__tostring")) == 0 {
|
||||
let t = lua_type(L, idx);
|
||||
match t {
|
||||
match lua_type(L, idx) {
|
||||
LUA_TNIL => {
|
||||
lua_pushliteral(L, "nil");
|
||||
}
|
||||
@@ -222,7 +222,7 @@ pub unsafe fn luaL_tolstring(L: *mut lua_State, idx: c_int, len: *mut usize) ->
|
||||
lua_pushliteral(L, "true");
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
t => {
|
||||
let tt = luaL_getmetafield(L, idx, cstr!("__name"));
|
||||
let name = if tt == LUA_TSTRING {
|
||||
lua_tostring(L, -1)
|
||||
@@ -231,7 +231,7 @@ pub unsafe fn luaL_tolstring(L: *mut lua_State, idx: c_int, len: *mut usize) ->
|
||||
};
|
||||
lua_pushfstring(L, cstr!("%s: %p"), name, lua_topointer(L, idx));
|
||||
if tt != LUA_TNIL {
|
||||
lua_replace(L, -2);
|
||||
lua_replace(L, -2); // remove '__name'
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -1,4 +1,4 @@
|
||||
//! MLua compatibility layer for Lua 5.2
|
||||
//! MLua compatibility layer for Lua 5.3
|
||||
|
||||
use std::os::raw::c_int;
|
||||
|
||||
@@ -25,7 +25,8 @@ extern "C" {
|
||||
|
||||
pub fn luaL_getmetafield(L: *mut lua_State, obj: c_int, e: *const c_char) -> c_int;
|
||||
pub fn luaL_callmeta(L: *mut lua_State, obj: c_int, e: *const c_char) -> c_int;
|
||||
pub fn luaL_tolstring(L: *mut lua_State, idx: c_int, len: *mut usize) -> *const c_char;
|
||||
#[link_name = "luaL_tolstring"]
|
||||
pub fn luaL_tolstring_(L: *mut lua_State, idx: c_int, len: *mut usize) -> *const c_char;
|
||||
pub fn luaL_argerror(L: *mut lua_State, arg: c_int, extramsg: *const c_char) -> c_int;
|
||||
pub fn luaL_checklstring(L: *mut lua_State, arg: c_int, l: *mut usize) -> *const c_char;
|
||||
pub fn luaL_optlstring(
|
||||
@@ -167,6 +168,11 @@ pub unsafe fn luaL_getmetatable(L: *mut lua_State, n: *const c_char) {
|
||||
lua::lua_getfield(L, lua::LUA_REGISTRYINDEX, n);
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn luaL_tolstring(L: *mut lua_State, idx: c_int, len: *mut usize) -> *const c_char {
|
||||
luaL_tolstring_(L, lua::lua_absindex(L, idx), len)
|
||||
}
|
||||
|
||||
// luaL_opt would be implemented here but it is undocumented, so it's omitted
|
||||
|
||||
#[inline(always)]
|
||||
@@ -112,7 +112,10 @@ extern "C" {
|
||||
pub fn lua_newstate(f: lua_Alloc, ud: *mut c_void) -> *mut lua_State;
|
||||
pub fn lua_close(L: *mut lua_State);
|
||||
pub fn lua_newthread(L: *mut lua_State) -> *mut lua_State;
|
||||
// Deprecated in Lua 5.4.6
|
||||
pub fn lua_resetthread(L: *mut lua_State) -> c_int;
|
||||
#[cfg(feature = "vendored")]
|
||||
pub fn lua_closethread(L: *mut lua_State, from: *mut lua_State) -> c_int;
|
||||
|
||||
pub fn lua_atpanic(L: *mut lua_State, panicf: lua_CFunction) -> lua_CFunction;
|
||||
|
||||
@@ -436,10 +436,10 @@ pub unsafe fn luaL_traceback(
|
||||
lua_concat(L, lua_gettop(L) - top);
|
||||
}
|
||||
|
||||
pub unsafe fn luaL_tolstring(L: *mut lua_State, idx: c_int, len: *mut usize) -> *const c_char {
|
||||
pub unsafe fn luaL_tolstring(L: *mut lua_State, mut idx: c_int, len: *mut usize) -> *const c_char {
|
||||
idx = lua_absindex(L, idx);
|
||||
if luaL_callmeta(L, idx, cstr!("__tostring")) == 0 {
|
||||
let t = lua_type(L, idx);
|
||||
match t {
|
||||
match lua_type(L, idx) {
|
||||
LUA_TNIL => {
|
||||
lua_pushliteral(L, "nil");
|
||||
}
|
||||
@@ -453,7 +453,7 @@ pub unsafe fn luaL_tolstring(L: *mut lua_State, idx: c_int, len: *mut usize) ->
|
||||
lua_pushliteral(L, "true");
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
t => {
|
||||
let tt = luaL_getmetafield(L, idx, cstr!("__name"));
|
||||
let name = if tt == LUA_TSTRING {
|
||||
lua_tostring(L, -1)
|
||||
@@ -462,7 +462,7 @@ pub unsafe fn luaL_tolstring(L: *mut lua_State, idx: c_int, len: *mut usize) ->
|
||||
};
|
||||
lua_pushfstring(L, cstr!("%s: %p"), name, lua_topointer(L, idx));
|
||||
if tt != LUA_TNIL {
|
||||
lua_replace(L, -2);
|
||||
lua_replace(L, -2); // remove '__name'
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -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(
|
||||
@@ -83,6 +84,11 @@ pub type lua_Alloc = unsafe extern "C" fn(
|
||||
nsize: usize,
|
||||
) -> *mut c_void;
|
||||
|
||||
/// Returns Luau release version (eg. `0.xxx`).
|
||||
pub const fn luau_version() -> Option<&'static str> {
|
||||
option_env!("LUAU_VERSION")
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
//
|
||||
// State manipulation
|
||||
@@ -265,11 +271,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,11 @@
|
||||
//! Contains definitions from `luacodegen.h`.
|
||||
|
||||
use std::os::raw::c_int;
|
||||
|
||||
use super::lua::lua_State;
|
||||
|
||||
extern "C" {
|
||||
pub fn luau_codegen_supported() -> c_int;
|
||||
pub fn luau_codegen_create(state: *mut lua_State);
|
||||
pub fn luau_codegen_compile(state: *mut lua_State, idx: c_int);
|
||||
}
|
||||
@@ -4,10 +4,12 @@ pub use compat::*;
|
||||
pub use lauxlib::*;
|
||||
pub use lua::*;
|
||||
pub use luacode::*;
|
||||
pub use luacodegen::*;
|
||||
pub use lualib::*;
|
||||
|
||||
pub mod compat;
|
||||
pub mod lauxlib;
|
||||
pub mod lua;
|
||||
pub mod luacode;
|
||||
pub mod luacodegen;
|
||||
pub mod lualib;
|
||||
@@ -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,6 +1,6 @@
|
||||
[package]
|
||||
name = "mlua_derive"
|
||||
version = "0.9.0-beta.1"
|
||||
version = "0.9.0-beta.2"
|
||||
authors = ["Aleksandr Orlenko <zxteam@pm.me>"]
|
||||
edition = "2021"
|
||||
description = "Procedural macros for the mlua crate."
|
||||
|
||||
+10
-10
@@ -42,7 +42,7 @@ pub fn lua_module(attr: TokenStream, item: TokenStream) -> TokenStream {
|
||||
}
|
||||
|
||||
let func = parse_macro_input!(item as ItemFn);
|
||||
let func_name = func.sig.ident.clone();
|
||||
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());
|
||||
|
||||
@@ -84,26 +84,26 @@ pub fn chunk(input: TokenStream) -> TokenStream {
|
||||
});
|
||||
|
||||
let wrapped_code = quote! {{
|
||||
use ::mlua::{AsChunk, ChunkMode, Lua, Result, Value};
|
||||
use ::mlua::{AsChunk, ChunkMode, Lua, Result, Table};
|
||||
use ::std::borrow::Cow;
|
||||
use ::std::io::Result as IoResult;
|
||||
use ::std::sync::Mutex;
|
||||
|
||||
struct InnerChunk<F: for <'a> FnOnce(&'a Lua) -> Result<Value<'a>>>(Mutex<Option<F>>);
|
||||
struct InnerChunk<F: for <'a> FnOnce(&'a Lua) -> Result<Table<'a>>>(Mutex<Option<F>>);
|
||||
|
||||
impl<F> AsChunk<'static> for InnerChunk<F>
|
||||
where
|
||||
F: for <'a> FnOnce(&'a Lua) -> Result<Value<'a>>,
|
||||
F: for <'a> FnOnce(&'a Lua) -> Result<Table<'a>>,
|
||||
{
|
||||
fn env<'lua>(&self, lua: &'lua Lua) -> Result<Value<'lua>> {
|
||||
fn environment<'lua>(&self, lua: &'lua Lua) -> Result<Option<Table<'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);
|
||||
return make_env(lua).map(Some);
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(Value::Nil)
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
fn mode(&self) -> Option<ChunkMode> {
|
||||
@@ -115,9 +115,9 @@ pub fn chunk(input: TokenStream) -> TokenStream {
|
||||
}
|
||||
}
|
||||
|
||||
fn annotate<F: for<'a> FnOnce(&'a Lua) -> Result<Value<'a>>>(f: F) -> F { f }
|
||||
fn annotate<F: for<'a> FnOnce(&'a Lua) -> Result<Table<'a>>>(f: F) -> F { f }
|
||||
|
||||
let make_env = annotate(move |lua: &Lua| -> Result<Value> {
|
||||
let make_env = annotate(move |lua: &Lua| -> Result<Table> {
|
||||
let globals = lua.globals();
|
||||
let env = lua.create_table()?;
|
||||
let meta = lua.create_table()?;
|
||||
@@ -128,7 +128,7 @@ pub fn chunk(input: TokenStream) -> TokenStream {
|
||||
#(#caps)*
|
||||
|
||||
env.set_metatable(Some(meta));
|
||||
Ok(Value::Table(env))
|
||||
Ok(env)
|
||||
});
|
||||
|
||||
InnerChunk(Mutex::new(Some(make_env)))
|
||||
|
||||
+15
-12
@@ -5,14 +5,14 @@ use std::io::Result as IoResult;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::string::String as StdString;
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
use crate::ffi;
|
||||
use crate::error::{Error, ErrorContext, Result};
|
||||
use crate::function::Function;
|
||||
use crate::lua::Lua;
|
||||
use crate::value::{FromLuaMulti, IntoLua, IntoLuaMulti, Value};
|
||||
use crate::table::Table;
|
||||
use crate::value::{FromLuaMulti, IntoLua, IntoLuaMulti};
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
use {futures_core::future::LocalBoxFuture, futures_util::future};
|
||||
use futures_util::future::{self, LocalBoxFuture};
|
||||
|
||||
/// Trait for types [loadable by Lua] and convertible to a [`Chunk`]
|
||||
///
|
||||
@@ -27,9 +27,9 @@ pub trait AsChunk<'a> {
|
||||
/// Returns optional chunk [environment]
|
||||
///
|
||||
/// [environment]: https://www.lua.org/manual/5.4/manual.html#2.2
|
||||
fn env<'lua>(&self, lua: &'lua Lua) -> Result<Value<'lua>> {
|
||||
fn environment<'lua>(&self, lua: &'lua Lua) -> Result<Option<Table<'lua>>> {
|
||||
let _lua = lua; // suppress warning
|
||||
Ok(Value::Nil)
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
/// Returns optional chunk mode (text or binary)
|
||||
@@ -104,7 +104,7 @@ impl AsChunk<'static> for PathBuf {
|
||||
pub struct Chunk<'lua, 'a> {
|
||||
pub(crate) lua: &'lua Lua,
|
||||
pub(crate) name: StdString,
|
||||
pub(crate) env: Result<Value<'lua>>,
|
||||
pub(crate) env: Result<Option<Table<'lua>>>,
|
||||
pub(crate) mode: Option<ChunkMode>,
|
||||
pub(crate) source: IoResult<Cow<'a, [u8]>>,
|
||||
#[cfg(feature = "luau")]
|
||||
@@ -255,9 +255,9 @@ impl<'lua, 'a> Chunk<'lua, 'a> {
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the first upvalue (`_ENV`) of the loaded chunk to the given value.
|
||||
/// Sets the environment of the loaded chunk to the given value.
|
||||
///
|
||||
/// Lua main chunks always have exactly one upvalue, and this upvalue is used as the `_ENV`
|
||||
/// In Lua >=5.2 main chunks always have exactly one upvalue, and this upvalue is used as the `_ENV`
|
||||
/// variable inside the chunk. By default this value is set to the global environment.
|
||||
///
|
||||
/// Calling this method changes the `_ENV` upvalue to the value provided, and variables inside
|
||||
@@ -267,7 +267,10 @@ impl<'lua, 'a> Chunk<'lua, 'a> {
|
||||
/// necessary to populate the environment in order for scripts using custom environments to be
|
||||
/// useful.
|
||||
pub fn set_environment<V: IntoLua<'lua>>(mut self, env: V) -> Self {
|
||||
self.env = env.into_lua(self.lua);
|
||||
self.env = env
|
||||
.into_lua(self.lua)
|
||||
.and_then(|val| self.lua.unpack(val))
|
||||
.context("bad environment value");
|
||||
self
|
||||
}
|
||||
|
||||
@@ -415,7 +418,7 @@ impl<'lua, 'a> Chunk<'lua, 'a> {
|
||||
self.mode = Some(ChunkMode::Binary);
|
||||
}
|
||||
#[cfg(not(feature = "luau"))]
|
||||
if let Ok(func) = self.lua.load_chunk(None, Value::Nil, None, source.as_ref()) {
|
||||
if let Ok(func) = self.lua.load_chunk(None, None, None, source.as_ref()) {
|
||||
let data = func.dump(false);
|
||||
self.source = Ok(Cow::Owned(data));
|
||||
self.mode = Some(ChunkMode::Binary);
|
||||
@@ -455,7 +458,7 @@ impl<'lua, 'a> Chunk<'lua, 'a> {
|
||||
} else {
|
||||
let mut cache = ChunksCache(HashMap::new());
|
||||
cache.0.insert(text_source, binary_source.as_ref().to_vec());
|
||||
self.lua.set_app_data(cache);
|
||||
let _ = self.lua.try_set_app_data(cache);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+17
-16
@@ -9,7 +9,7 @@ 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;
|
||||
@@ -18,14 +18,10 @@ use crate::types::{LightUserData, MaybeSend};
|
||||
use crate::userdata::{AnyUserData, UserData, UserDataRef, UserDataRefMut};
|
||||
use crate::value::{FromLua, IntoLua, Nil, Value};
|
||||
|
||||
#[cfg(feature = "unstable")]
|
||||
use crate::{
|
||||
function::{OwnedFunction, WrappedFunction},
|
||||
table::OwnedTable,
|
||||
userdata::OwnedAnyUserData,
|
||||
};
|
||||
#[cfg(all(feature = "unstable", any(not(feature = "send"), doc)))]
|
||||
use crate::{function::OwnedFunction, table::OwnedTable, userdata::OwnedAnyUserData};
|
||||
|
||||
#[cfg(all(feature = "async", feature = "unstable"))]
|
||||
#[cfg(feature = "async")]
|
||||
use crate::function::WrappedAsyncFunction;
|
||||
|
||||
impl<'lua> IntoLua<'lua> for Value<'lua> {
|
||||
@@ -83,7 +79,8 @@ impl<'lua> FromLua<'lua> for Table<'lua> {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "unstable")]
|
||||
#[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 into_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
|
||||
@@ -91,7 +88,8 @@ impl<'lua> IntoLua<'lua> for OwnedTable {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "unstable")]
|
||||
#[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> {
|
||||
@@ -120,7 +118,8 @@ impl<'lua> FromLua<'lua> for Function<'lua> {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "unstable")]
|
||||
#[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 into_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
|
||||
@@ -128,7 +127,8 @@ impl<'lua> IntoLua<'lua> for OwnedFunction {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "unstable")]
|
||||
#[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> {
|
||||
@@ -136,7 +136,6 @@ impl<'lua> FromLua<'lua> for OwnedFunction {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "unstable")]
|
||||
impl<'lua> IntoLua<'lua> for WrappedFunction<'lua> {
|
||||
#[inline]
|
||||
fn into_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
|
||||
@@ -144,7 +143,7 @@ impl<'lua> IntoLua<'lua> for WrappedFunction<'lua> {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "async", feature = "unstable"))]
|
||||
#[cfg(feature = "async")]
|
||||
impl<'lua> IntoLua<'lua> for WrappedAsyncFunction<'lua> {
|
||||
#[inline]
|
||||
fn into_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
|
||||
@@ -194,7 +193,8 @@ impl<'lua> FromLua<'lua> for AnyUserData<'lua> {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "unstable")]
|
||||
#[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 into_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
|
||||
@@ -202,7 +202,8 @@ impl<'lua> IntoLua<'lua> for OwnedAnyUserData {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "unstable")]
|
||||
#[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> {
|
||||
|
||||
+24
-14
@@ -335,6 +335,10 @@ impl StdError for Error {
|
||||
// Given that we include source to fmt::Display implementation for `CallbackError`, this call returns nothing.
|
||||
Error::CallbackError { .. } => None,
|
||||
Error::ExternalError(ref err) => err.source(),
|
||||
Error::WithContext { ref cause, .. } => match cause.as_ref() {
|
||||
Error::ExternalError(err) => err.source(),
|
||||
_ => None,
|
||||
},
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
@@ -353,6 +357,10 @@ impl Error {
|
||||
{
|
||||
match self {
|
||||
Error::ExternalError(err) => err.downcast_ref(),
|
||||
Error::WithContext { cause, .. } => match cause.as_ref() {
|
||||
Error::ExternalError(err) => err.downcast_ref(),
|
||||
_ => None,
|
||||
},
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
@@ -414,33 +422,35 @@ pub trait ErrorContext: Sealed {
|
||||
|
||||
impl ErrorContext for Error {
|
||||
fn context<C: fmt::Display>(self, context: C) -> Self {
|
||||
Error::WithContext {
|
||||
context: context.to_string(),
|
||||
cause: Arc::new(self),
|
||||
let context = context.to_string();
|
||||
match self {
|
||||
Error::WithContext { cause, .. } => Error::WithContext { context, cause },
|
||||
_ => Error::WithContext {
|
||||
context,
|
||||
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),
|
||||
let context = f(&self).to_string();
|
||||
match self {
|
||||
Error::WithContext { cause, .. } => Error::WithContext { context, cause },
|
||||
_ => Error::WithContext {
|
||||
context,
|
||||
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),
|
||||
})
|
||||
self.map_err(|err| err.context(context))
|
||||
}
|
||||
|
||||
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),
|
||||
})
|
||||
self.map_err(|err| err.with_context(f))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+187
-36
@@ -1,36 +1,36 @@
|
||||
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::lua::Lua;
|
||||
use crate::memory::MemoryState;
|
||||
use crate::types::LuaRef;
|
||||
use crate::table::Table;
|
||||
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, IntoLuaMulti};
|
||||
|
||||
#[cfg(feature = "unstable")]
|
||||
use {
|
||||
crate::lua::Lua,
|
||||
crate::types::{Callback, MaybeSend},
|
||||
crate::value::IntoLua,
|
||||
std::cell::RefCell,
|
||||
};
|
||||
use crate::value::{FromLuaMulti, IntoLua, IntoLuaMulti};
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
use {futures_core::future::LocalBoxFuture, futures_util::future};
|
||||
|
||||
#[cfg(all(feature = "async", feature = "unstable"))]
|
||||
use {crate::types::AsyncCallback, futures_core::Future, futures_util::TryFutureExt};
|
||||
use {
|
||||
crate::types::AsyncCallback,
|
||||
futures_util::future::{self, Future, LocalBoxFuture, 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)]
|
||||
@@ -39,20 +39,36 @@ 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())
|
||||
}
|
||||
}
|
||||
|
||||
/// Contains information about a function.
|
||||
///
|
||||
/// Please refer to the [`Lua Debug Interface`] for more information.
|
||||
///
|
||||
/// [`Lua Debug Interface`]: https://www.lua.org/manual/5.4/manual.html#4.7
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct FunctionInfo {
|
||||
pub name: Option<Vec<u8>>,
|
||||
pub name_what: Option<Vec<u8>>,
|
||||
pub what: Option<Vec<u8>>,
|
||||
/// A (reasonable) name of the function.
|
||||
pub name: Option<String>,
|
||||
/// Explains the `name` field ("global", "local", "method", "field", "upvalue", or "").
|
||||
///
|
||||
/// Always `None` for Luau.
|
||||
pub name_what: Option<String>,
|
||||
/// A string "Lua" if the function is a Lua function, "C" if it is a C function, "main" if it is the main part of a chunk.
|
||||
pub what: Option<String>,
|
||||
/// The source of the chunk that created the function.
|
||||
pub source: Option<Vec<u8>>,
|
||||
/// A "printable" version of source, to be used in error messages.
|
||||
pub short_src: Option<Vec<u8>>,
|
||||
/// The line number where the definition of the function starts.
|
||||
pub line_defined: i32,
|
||||
#[cfg(not(feature = "luau"))]
|
||||
/// The line number where the definition of the function ends.
|
||||
///
|
||||
/// Always `-1` for Luau.
|
||||
pub last_line_defined: i32,
|
||||
}
|
||||
|
||||
@@ -270,10 +286,113 @@ impl<'lua> Function<'lua> {
|
||||
"#,
|
||||
)
|
||||
.try_cache()
|
||||
.set_name("_mlua_bind")
|
||||
.set_name("__mlua_bind")
|
||||
.call((self.clone(), args_wrapper))
|
||||
}
|
||||
|
||||
/// Returns the environment of the Lua function.
|
||||
///
|
||||
/// By default Lua functions shares a global environment.
|
||||
///
|
||||
/// This function always returns `None` for Rust/C functions.
|
||||
pub fn environment(&self) -> Option<Table> {
|
||||
let lua = self.0.lua;
|
||||
let state = lua.state();
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(state);
|
||||
assert_stack(state, 1);
|
||||
|
||||
lua.push_ref(&self.0);
|
||||
|
||||
let mut ar: ffi::lua_Debug = mem::zeroed();
|
||||
#[cfg(not(feature = "luau"))]
|
||||
{
|
||||
ffi::lua_pushvalue(state, -1);
|
||||
ffi::lua_getinfo(state, cstr!(">S"), &mut ar);
|
||||
}
|
||||
#[cfg(feature = "luau")]
|
||||
ffi::lua_getinfo(state, -1, cstr!("s"), &mut ar);
|
||||
|
||||
if ptr_to_cstr_bytes(ar.what) == Some(b"C") {
|
||||
return None;
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "lua51", feature = "luajit", feature = "luau"))]
|
||||
ffi::lua_getfenv(state, -1);
|
||||
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
|
||||
for i in 1..=255 {
|
||||
// Traverse upvalues until we find the _ENV one
|
||||
match ffi::lua_getupvalue(state, -1, i) {
|
||||
s if s.is_null() => break,
|
||||
s if std::ffi::CStr::from_ptr(s as _).to_bytes() == b"_ENV" => break,
|
||||
_ => ffi::lua_pop(state, 1),
|
||||
}
|
||||
}
|
||||
|
||||
if ffi::lua_type(state, -1) != ffi::LUA_TTABLE {
|
||||
return None;
|
||||
}
|
||||
Some(Table(lua.pop_ref()))
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the environment of the Lua function.
|
||||
///
|
||||
/// The environment is a table that is used as the global environment for the function.
|
||||
/// Returns `true` if environment successfully changed, `false` otherwise.
|
||||
///
|
||||
/// This function does nothing for Rust/C functions.
|
||||
pub fn set_environment(&self, env: Table) -> Result<bool> {
|
||||
let lua = self.0.lua;
|
||||
let state = lua.state();
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(state);
|
||||
check_stack(state, 2)?;
|
||||
|
||||
lua.push_ref(&self.0);
|
||||
|
||||
let mut ar: ffi::lua_Debug = mem::zeroed();
|
||||
#[cfg(not(feature = "luau"))]
|
||||
{
|
||||
ffi::lua_pushvalue(state, -1);
|
||||
ffi::lua_getinfo(state, cstr!(">S"), &mut ar);
|
||||
}
|
||||
#[cfg(feature = "luau")]
|
||||
ffi::lua_getinfo(state, -1, cstr!("s"), &mut ar);
|
||||
|
||||
if ptr_to_cstr_bytes(ar.what) == Some(b"C") {
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "lua51", feature = "luajit", feature = "luau"))]
|
||||
{
|
||||
lua.push_ref(&env.0);
|
||||
ffi::lua_setfenv(state, -2);
|
||||
}
|
||||
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
|
||||
for i in 1..=255 {
|
||||
match ffi::lua_getupvalue(state, -1, i) {
|
||||
s if s.is_null() => return Ok(false),
|
||||
s if std::ffi::CStr::from_ptr(s as _).to_bytes() == b"_ENV" => {
|
||||
ffi::lua_pop(state, 1);
|
||||
// Create an anonymous function with the new environment
|
||||
let f_with_env = lua
|
||||
.load("return _ENV")
|
||||
.set_environment(env)
|
||||
.try_cache()
|
||||
.into_function()?;
|
||||
lua.push_ref(&f_with_env.0);
|
||||
ffi::lua_upvaluejoin(state, -2, i, -1, 1);
|
||||
break;
|
||||
}
|
||||
_ => ffi::lua_pop(state, 1),
|
||||
}
|
||||
}
|
||||
|
||||
Ok(true)
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns information about the function.
|
||||
///
|
||||
/// Corresponds to the `>Sn` what mask for [`lua_getinfo`] when applied to the function.
|
||||
@@ -295,12 +414,13 @@ impl<'lua> Function<'lua> {
|
||||
mlua_assert!(res != 0, "lua_getinfo failed with `>Sn`");
|
||||
|
||||
FunctionInfo {
|
||||
name: ptr_to_cstr_bytes(ar.name).map(|s| s.to_vec()),
|
||||
name: ptr_to_cstr_bytes(ar.name).map(|s| String::from_utf8_lossy(s).into_owned()),
|
||||
#[cfg(not(feature = "luau"))]
|
||||
name_what: ptr_to_cstr_bytes(ar.namewhat).map(|s| s.to_vec()),
|
||||
name_what: ptr_to_cstr_bytes(ar.namewhat)
|
||||
.map(|s| String::from_utf8_lossy(s).into_owned()),
|
||||
#[cfg(feature = "luau")]
|
||||
name_what: None,
|
||||
what: ptr_to_cstr_bytes(ar.what).map(|s| s.to_vec()),
|
||||
what: ptr_to_cstr_bytes(ar.what).map(|s| String::from_utf8_lossy(s).into_owned()),
|
||||
source: ptr_to_cstr_bytes(ar.source).map(|s| s.to_vec()),
|
||||
#[cfg(not(feature = "luau"))]
|
||||
short_src: ptr_to_cstr_bytes(ar.short_src.as_ptr()).map(|s| s.to_vec()),
|
||||
@@ -309,6 +429,8 @@ impl<'lua> Function<'lua> {
|
||||
line_defined: ar.linedefined,
|
||||
#[cfg(not(feature = "luau"))]
|
||||
last_line_defined: ar.lastlinedefined,
|
||||
#[cfg(feature = "luau")]
|
||||
last_line_defined: -1,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -406,8 +528,8 @@ impl<'lua> Function<'lua> {
|
||||
}
|
||||
|
||||
/// Convert this handle to owned version.
|
||||
#[cfg(feature = "unstable")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "unstable")))]
|
||||
#[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())
|
||||
@@ -420,22 +542,50 @@ impl<'lua> PartialEq for Function<'lua> {
|
||||
}
|
||||
}
|
||||
|
||||
// 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(all(feature = "async", feature = "unstable"))]
|
||||
#[cfg(feature = "async")]
|
||||
pub(crate) struct WrappedAsyncFunction<'lua>(pub(crate) AsyncCallback<'lua, 'static>);
|
||||
|
||||
#[cfg(feature = "unstable")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "unstable")))]
|
||||
impl<'lua> Function<'lua> {
|
||||
/// Wraps a Rust function or closure, returning an opaque type that implements [`IntoLua`] trait.
|
||||
#[inline]
|
||||
pub fn wrap<F, A, R>(func: F) -> impl IntoLua<'lua>
|
||||
pub fn wrap<A, R, F>(func: F) -> impl IntoLua<'lua>
|
||||
where
|
||||
F: Fn(&'lua Lua, A) -> Result<R> + MaybeSend + 'static,
|
||||
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)
|
||||
@@ -444,11 +594,11 @@ impl<'lua> Function<'lua> {
|
||||
|
||||
/// Wraps a Rust mutable closure, returning an opaque type that implements [`IntoLua`] trait.
|
||||
#[inline]
|
||||
pub fn wrap_mut<F, A, R>(func: F) -> impl IntoLua<'lua>
|
||||
pub fn wrap_mut<A, R, F>(func: F) -> impl IntoLua<'lua>
|
||||
where
|
||||
F: FnMut(&'lua Lua, A) -> Result<R> + MaybeSend + 'static,
|
||||
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| {
|
||||
@@ -459,14 +609,15 @@ impl<'lua> Function<'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<F, A, FR, R>(func: F) -> impl IntoLua<'lua>
|
||||
pub fn wrap_async<A, R, F, FR>(func: F) -> impl IntoLua<'lua>
|
||||
where
|
||||
F: Fn(&'lua Lua, A) -> FR + MaybeSend + 'static,
|
||||
A: FromLuaMulti<'lua>,
|
||||
FR: Future<Output = Result<R>> + '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) {
|
||||
@@ -484,6 +635,6 @@ mod assertions {
|
||||
|
||||
static_assertions::assert_not_impl_any!(Function: Send);
|
||||
|
||||
#[cfg(feature = "unstable")]
|
||||
#[cfg(all(feature = "unstable", not(feature = "send")))]
|
||||
static_assertions::assert_not_impl_any!(OwnedFunction: Send);
|
||||
}
|
||||
|
||||
+41
-28
@@ -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;
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+8
-9
@@ -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.9.0-beta.1")]
|
||||
// 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,7 +82,6 @@ mod macros;
|
||||
mod chunk;
|
||||
mod conversion;
|
||||
mod error;
|
||||
mod ffi;
|
||||
mod function;
|
||||
mod hook;
|
||||
mod lua;
|
||||
@@ -106,7 +103,7 @@ 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, ErrorContext, ExternalError, ExternalResult, Result};
|
||||
@@ -119,7 +116,7 @@ pub use crate::stdlib::StdLib;
|
||||
pub use crate::string::String;
|
||||
pub use crate::table::{Table, TableExt, TablePairs, TableSequence};
|
||||
pub use crate::thread::{Thread, ThreadStatus};
|
||||
pub use crate::types::{Integer, LightUserData, Number, RegistryKey};
|
||||
pub use crate::types::{AppDataRef, AppDataRefMut, Integer, LightUserData, Number, RegistryKey};
|
||||
pub use crate::userdata::{
|
||||
AnyUserData, MetaMethod, UserData, UserDataFields, UserDataMetatable, UserDataMethods,
|
||||
UserDataRef, UserDataRefMut,
|
||||
@@ -148,14 +145,16 @@ pub use crate::serde::{
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "serialize")))]
|
||||
pub mod serde;
|
||||
|
||||
#[cfg(any(feature = "mlua_derive"))]
|
||||
#[cfg(feature = "mlua_derive")]
|
||||
#[allow(unused_imports)]
|
||||
#[macro_use]
|
||||
extern crate mlua_derive;
|
||||
|
||||
// Unstable features
|
||||
#[cfg(all(feature = "unstable", not(feature = "send")))]
|
||||
pub use crate::{function::OwnedFunction, table::OwnedTable, userdata::OwnedAnyUserData};
|
||||
#[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.
|
||||
///
|
||||
@@ -209,7 +208,7 @@ pub use crate::{function::OwnedFunction, table::OwnedTable, userdata::OwnedAnyUs
|
||||
/// [`AsChunk`]: crate::AsChunk
|
||||
/// [`UserData`]: crate::UserData
|
||||
/// [`IntoLua`]: crate::IntoLua
|
||||
#[cfg(any(feature = "macros"))]
|
||||
#[cfg(feature = "macros")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "macros")))]
|
||||
pub use mlua_derive::chunk;
|
||||
|
||||
|
||||
+346
-240
File diff suppressed because it is too large
Load Diff
+1
-2
@@ -4,7 +4,6 @@ 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};
|
||||
@@ -25,7 +24,7 @@ impl Lua {
|
||||
|
||||
// Set `_VERSION` global to include version number
|
||||
// The environment variable `LUAU_VERSION` set by the build script
|
||||
if let Some(version) = option_env!("LUAU_VERSION") {
|
||||
if let Some(version) = ffi::luau_version() {
|
||||
globals.raw_set("_VERSION", format!("Luau {version}"))?;
|
||||
}
|
||||
|
||||
|
||||
+6
-1
@@ -103,7 +103,12 @@ macro_rules! protect_lua {
|
||||
($state:expr, $nargs:expr, $nresults:expr, fn($state_inner:ident) $code:expr) => {{
|
||||
unsafe extern "C" fn do_call($state_inner: *mut ffi::lua_State) -> ::std::os::raw::c_int {
|
||||
$code;
|
||||
$nresults
|
||||
let nresults = $nresults;
|
||||
if nresults == ::ffi::LUA_MULTRET {
|
||||
ffi::lua_gettop($state_inner)
|
||||
} else {
|
||||
nresults
|
||||
}
|
||||
}
|
||||
|
||||
crate::util::protect_lua_call($state, $nargs, do_call)
|
||||
|
||||
@@ -2,7 +2,6 @@ use std::alloc::{self, Layout};
|
||||
use std::os::raw::c_void;
|
||||
use std::ptr;
|
||||
|
||||
use crate::ffi;
|
||||
#[cfg(feature = "luau")]
|
||||
use crate::lua::ExtraData;
|
||||
|
||||
|
||||
+2
-2
@@ -36,9 +36,9 @@ pub use crate::{
|
||||
SerializeOptions as LuaSerializeOptions,
|
||||
};
|
||||
|
||||
#[cfg(all(feature = "unstable", not(feature = "send")))]
|
||||
#[cfg(feature = "unstable")]
|
||||
#[doc(no_inline)]
|
||||
pub use crate::{
|
||||
OwnedAnyUserData as LuaOwnedAnyUserData, OwnedFunction as LuaOwnedFunction,
|
||||
OwnedTable as LuaOwnedTable,
|
||||
OwnedString as LuaOwnedString, OwnedTable as LuaOwnedTable,
|
||||
};
|
||||
|
||||
+38
-23
@@ -8,13 +8,13 @@ use std::os::raw::c_int;
|
||||
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};
|
||||
use crate::userdata::{
|
||||
AnyUserData, MetaMethod, UserData, UserDataCell, UserDataFields, UserDataMethods,
|
||||
};
|
||||
use crate::userdata_impl::UserDataRegistrar;
|
||||
use crate::util::{
|
||||
assert_stack, check_stack, get_userdata, init_userdata_metatable, push_table, rawset_field,
|
||||
take_userdata, StackGuard,
|
||||
@@ -25,7 +25,7 @@ use crate::value::{FromLua, FromLuaMulti, IntoLua, IntoLuaMulti, MultiValue, Val
|
||||
use crate::userdata::USER_VALUE_MAXSLOT;
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
use futures_core::future::Future;
|
||||
use std::future::Future;
|
||||
|
||||
/// Constructed by the [`Lua::scope`] method, allows temporarily creating Lua userdata and
|
||||
/// callbacks that are not required to be Send or 'static.
|
||||
@@ -33,7 +33,10 @@ use futures_core::future::Future;
|
||||
/// See [`Lua::scope`] for more details.
|
||||
///
|
||||
/// [`Lua::scope`]: crate::Lua.html::scope
|
||||
pub struct Scope<'lua, 'scope> {
|
||||
pub struct Scope<'lua, 'scope>
|
||||
where
|
||||
'lua: 'scope,
|
||||
{
|
||||
lua: &'lua Lua,
|
||||
destructors: RefCell<Vec<(LuaRef<'lua>, DestructorCallback<'lua>)>>,
|
||||
_scope_invariant: PhantomData<Cell<&'scope ()>>,
|
||||
@@ -394,7 +397,7 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
|
||||
rawset_field(state, -2, MetaMethod::validate(&k)?)?;
|
||||
}
|
||||
for (k, f) in ud_fields.meta_fields {
|
||||
lua.push_value(f(mem::transmute(lua))?)?;
|
||||
lua.push_value(f(lua, MultiValue::new())?.pop_front().unwrap())?;
|
||||
rawset_field(state, -2, MetaMethod::validate(&k)?)?;
|
||||
}
|
||||
let metatable_index = ffi::lua_absindex(state, -1);
|
||||
@@ -735,15 +738,16 @@ impl<'lua, T: UserData> UserDataMethods<'lua, T> for NonStaticUserDataMethods<'l
|
||||
}
|
||||
|
||||
struct NonStaticUserDataFields<'lua, T: UserData> {
|
||||
fields: Vec<(String, Callback<'lua, 'static>)>,
|
||||
field_getters: Vec<(String, NonStaticMethod<'lua, T>)>,
|
||||
field_setters: Vec<(String, NonStaticMethod<'lua, T>)>,
|
||||
#[allow(clippy::type_complexity)]
|
||||
meta_fields: Vec<(String, Box<dyn Fn(&'lua Lua) -> Result<Value<'lua>>>)>,
|
||||
meta_fields: Vec<(String, Callback<'lua, 'static>)>,
|
||||
}
|
||||
|
||||
impl<'lua, T: UserData> Default for NonStaticUserDataFields<'lua, T> {
|
||||
fn default() -> NonStaticUserDataFields<'lua, T> {
|
||||
NonStaticUserDataFields {
|
||||
fields: Vec::new(),
|
||||
field_getters: Vec::new(),
|
||||
field_setters: Vec::new(),
|
||||
meta_fields: Vec::new(),
|
||||
@@ -752,6 +756,17 @@ impl<'lua, T: UserData> Default for NonStaticUserDataFields<'lua, T> {
|
||||
}
|
||||
|
||||
impl<'lua, T: UserData> UserDataFields<'lua, T> for NonStaticUserDataFields<'lua, T> {
|
||||
fn add_field<V>(&mut self, name: impl AsRef<str>, value: V)
|
||||
where
|
||||
V: IntoLua<'lua> + Clone + 'static,
|
||||
{
|
||||
let name = name.as_ref().to_string();
|
||||
self.fields.push((
|
||||
name,
|
||||
Box::new(move |lua, _| value.clone().into_lua_multi(lua)),
|
||||
));
|
||||
}
|
||||
|
||||
fn add_field_method_get<M, R>(&mut self, name: impl AsRef<str>, method: M)
|
||||
where
|
||||
M: Fn(&'lua Lua, &T) -> Result<R> + MaybeSend + 'static,
|
||||
@@ -797,30 +812,30 @@ impl<'lua, T: UserData> UserDataFields<'lua, T> for NonStaticUserDataFields<'lua
|
||||
self.field_setters.push((name.as_ref().into(), func));
|
||||
}
|
||||
|
||||
fn add_meta_field<V>(&mut self, name: impl AsRef<str>, value: V)
|
||||
where
|
||||
V: IntoLua<'lua> + Clone + 'static,
|
||||
{
|
||||
let name = name.as_ref().to_string();
|
||||
let name2 = name.clone();
|
||||
self.meta_fields.push((
|
||||
name,
|
||||
Box::new(move |lua, _| {
|
||||
UserDataRegistrar::<()>::check_meta_field(lua, &name2, value.clone())
|
||||
}),
|
||||
));
|
||||
}
|
||||
|
||||
fn add_meta_field_with<F, R>(&mut self, name: impl AsRef<str>, f: F)
|
||||
where
|
||||
F: Fn(&'lua Lua) -> Result<R> + MaybeSend + 'static,
|
||||
R: IntoLua<'lua>,
|
||||
{
|
||||
let name = name.as_ref().to_string();
|
||||
let name2 = name.clone();
|
||||
self.meta_fields.push((
|
||||
name.clone(),
|
||||
Box::new(move |lua| {
|
||||
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: name.clone(),
|
||||
type_name: value.type_name(),
|
||||
message: Some("expected nil, table or function".to_string()),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(value)
|
||||
}),
|
||||
name,
|
||||
Box::new(move |lua, _| UserDataRegistrar::<()>::check_meta_field(lua, &name2, f(lua)?)),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
+23
-1
@@ -345,9 +345,31 @@ impl<'lua, 'de> serde::Deserializer<'de> for Deserializer<'lua> {
|
||||
}
|
||||
}
|
||||
|
||||
#[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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+19
-25
@@ -1,22 +1,19 @@
|
||||
//! (De)Serialization support using serde.
|
||||
|
||||
use std::os::raw::c_void;
|
||||
use std::ptr;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde::{de::DeserializeOwned, ser::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;
|
||||
use crate::value::Value;
|
||||
|
||||
/// Trait for serializing/deserializing Lua values using Serde.
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "serialize")))]
|
||||
pub trait LuaSerdeExt<'lua>: Sealed {
|
||||
pub trait LuaSerdeExt: Sealed {
|
||||
/// A special value (lightuserdata) to encode/decode optional (none) values.
|
||||
///
|
||||
/// Requires `feature = "serialize"`
|
||||
@@ -38,7 +35,7 @@ pub trait LuaSerdeExt<'lua>: Sealed {
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
fn null(&'lua self) -> Value<'lua>;
|
||||
fn null(&self) -> Value;
|
||||
|
||||
/// A metatable attachable to a Lua table to systematically encode it as Array (instead of Map).
|
||||
/// As result, encoded Array will contain only sequence part of the table, with the same length
|
||||
@@ -69,7 +66,7 @@ pub trait LuaSerdeExt<'lua>: Sealed {
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
fn array_metatable(&'lua self) -> Table<'lua>;
|
||||
fn array_metatable(&self) -> Table;
|
||||
|
||||
/// Converts `T` into a [`Value`] instance.
|
||||
///
|
||||
@@ -102,7 +99,7 @@ pub trait LuaSerdeExt<'lua>: Sealed {
|
||||
/// "#).exec()
|
||||
/// }
|
||||
/// ```
|
||||
fn to_value<T: Serialize + ?Sized>(&'lua self, t: &T) -> Result<Value<'lua>>;
|
||||
fn to_value<'lua, T: Serialize + ?Sized>(&'lua self, t: &T) -> Result<Value<'lua>>;
|
||||
|
||||
/// Converts `T` into a [`Value`] instance with options.
|
||||
///
|
||||
@@ -127,7 +124,7 @@ pub trait LuaSerdeExt<'lua>: Sealed {
|
||||
/// "#).exec()
|
||||
/// }
|
||||
/// ```
|
||||
fn to_value_with<T>(&'lua self, t: &T, options: ser::Options) -> Result<Value<'lua>>
|
||||
fn to_value_with<'lua, T>(&'lua self, t: &T, options: ser::Options) -> Result<Value<'lua>>
|
||||
where
|
||||
T: Serialize + ?Sized;
|
||||
|
||||
@@ -160,7 +157,7 @@ pub trait LuaSerdeExt<'lua>: Sealed {
|
||||
/// }
|
||||
/// ```
|
||||
#[allow(clippy::wrong_self_convention)]
|
||||
fn from_value<T: Deserialize<'lua>>(&'lua self, value: Value<'lua>) -> Result<T>;
|
||||
fn from_value<T: DeserializeOwned>(&self, value: Value) -> Result<T>;
|
||||
|
||||
/// Deserializes a [`Value`] into any serde deserializable object with options.
|
||||
///
|
||||
@@ -192,49 +189,46 @@ pub trait LuaSerdeExt<'lua>: Sealed {
|
||||
/// }
|
||||
/// ```
|
||||
#[allow(clippy::wrong_self_convention)]
|
||||
fn from_value_with<T: Deserialize<'lua>>(
|
||||
&'lua self,
|
||||
value: Value<'lua>,
|
||||
options: de::Options,
|
||||
) -> Result<T>;
|
||||
fn from_value_with<T: DeserializeOwned>(&self, value: Value, options: de::Options)
|
||||
-> Result<T>;
|
||||
}
|
||||
|
||||
impl<'lua> LuaSerdeExt<'lua> for Lua {
|
||||
fn null(&'lua self) -> Value<'lua> {
|
||||
Value::LightUserData(LightUserData(ptr::null_mut()))
|
||||
impl LuaSerdeExt for Lua {
|
||||
fn null(&self) -> Value {
|
||||
Value::NULL
|
||||
}
|
||||
|
||||
fn array_metatable(&'lua self) -> Table<'lua> {
|
||||
fn array_metatable(&self) -> Table {
|
||||
unsafe {
|
||||
push_array_metatable(self.ref_thread());
|
||||
Table(self.pop_ref_thread())
|
||||
}
|
||||
}
|
||||
|
||||
fn to_value<T>(&'lua self, t: &T) -> Result<Value<'lua>>
|
||||
fn to_value<'lua, T>(&'lua self, t: &T) -> Result<Value<'lua>>
|
||||
where
|
||||
T: Serialize + ?Sized,
|
||||
{
|
||||
t.serialize(ser::Serializer::new(self))
|
||||
}
|
||||
|
||||
fn to_value_with<T>(&'lua self, t: &T, options: ser::Options) -> Result<Value<'lua>>
|
||||
fn to_value_with<'lua, T>(&'lua self, t: &T, options: ser::Options) -> Result<Value<'lua>>
|
||||
where
|
||||
T: Serialize + ?Sized,
|
||||
{
|
||||
t.serialize(ser::Serializer::new_with_options(self, options))
|
||||
}
|
||||
|
||||
fn from_value<T>(&'lua self, value: Value<'lua>) -> Result<T>
|
||||
fn from_value<T>(&self, value: Value) -> Result<T>
|
||||
where
|
||||
T: Deserialize<'lua>,
|
||||
T: DeserializeOwned,
|
||||
{
|
||||
T::deserialize(de::Deserializer::new(value))
|
||||
}
|
||||
|
||||
fn from_value_with<T>(&'lua self, value: Value<'lua>, options: de::Options) -> Result<T>
|
||||
fn from_value_with<T>(&self, value: Value, options: de::Options) -> Result<T>
|
||||
where
|
||||
T: Deserialize<'lua>,
|
||||
T: DeserializeOwned,
|
||||
{
|
||||
T::deserialize(de::Deserializer::new_with_options(value, options))
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@ 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;
|
||||
|
||||
+60
-1
@@ -11,7 +11,6 @@ use {
|
||||
};
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
use crate::ffi;
|
||||
use crate::types::LuaRef;
|
||||
|
||||
/// Handle to an internal Lua string.
|
||||
@@ -20,6 +19,27 @@ use crate::types::LuaRef;
|
||||
#[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,14 @@ 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> {
|
||||
@@ -203,6 +231,37 @@ 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::*;
|
||||
|
||||
+50
-6
@@ -1,3 +1,5 @@
|
||||
use std::collections::HashSet;
|
||||
use std::fmt;
|
||||
use std::marker::PhantomData;
|
||||
use std::os::raw::c_void;
|
||||
|
||||
@@ -9,7 +11,6 @@ use {
|
||||
};
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
use crate::ffi;
|
||||
use crate::function::Function;
|
||||
use crate::private::Sealed;
|
||||
use crate::types::{Integer, LuaRef};
|
||||
@@ -17,13 +18,19 @@ use crate::util::{assert_stack, check_stack, StackGuard};
|
||||
use crate::value::{FromLua, FromLuaMulti, IntoLua, IntoLuaMulti, Nil, Value};
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
use {futures_core::future::LocalBoxFuture, futures_util::future};
|
||||
use futures_util::future::{self, LocalBoxFuture};
|
||||
|
||||
/// 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)]
|
||||
@@ -32,6 +39,7 @@ 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())
|
||||
}
|
||||
@@ -583,8 +591,8 @@ impl<'lua> Table<'lua> {
|
||||
}
|
||||
|
||||
/// Convert this handle to owned version.
|
||||
#[cfg(feature = "unstable")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "unstable")))]
|
||||
#[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())
|
||||
@@ -697,7 +705,7 @@ impl<'lua> Table<'lua> {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "serialize"))]
|
||||
#[cfg(feature = "serialize")]
|
||||
pub(crate) fn raw_sequence_values_by_len<V: FromLua<'lua>>(
|
||||
self,
|
||||
len: Option<Integer>,
|
||||
@@ -738,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> {
|
||||
|
||||
+38
-10
@@ -2,7 +2,8 @@ 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, IntoLuaMulti};
|
||||
@@ -14,14 +15,21 @@ use crate::value::{FromLuaMulti, IntoLuaMulti};
|
||||
))]
|
||||
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},
|
||||
futures_util::stream::Stream,
|
||||
std::{
|
||||
future::Future,
|
||||
marker::PhantomData,
|
||||
pin::Pin,
|
||||
ptr::NonNull,
|
||||
@@ -56,6 +64,7 @@ pub struct Thread<'lua>(pub(crate) LuaRef<'lua>);
|
||||
/// [`Stream`]: futures_core::stream::Stream
|
||||
#[cfg(feature = "async")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
|
||||
#[must_use = "futures do nothing unless you `.await` or poll them"]
|
||||
pub struct AsyncThread<'lua, R> {
|
||||
thread: Thread<'lua>,
|
||||
args0: Option<Result<MultiValue<'lua>>>,
|
||||
@@ -177,6 +186,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.
|
||||
@@ -207,8 +233,10 @@ impl<'lua> Thread<'lua> {
|
||||
lua.push_ref(&self.0);
|
||||
let thread_state = ffi::lua_tothread(state, -1);
|
||||
|
||||
#[cfg(feature = "lua54")]
|
||||
#[cfg(all(feature = "lua54", not(feature = "vendored")))]
|
||||
let status = ffi::lua_resetthread(thread_state);
|
||||
#[cfg(all(feature = "lua54", feature = "vendored"))]
|
||||
let status = ffi::lua_closethread(thread_state, state);
|
||||
#[cfg(feature = "lua54")]
|
||||
if status != ffi::LUA_OK {
|
||||
return Err(pop_error(thread_state, status));
|
||||
@@ -223,8 +251,8 @@ impl<'lua> Thread<'lua> {
|
||||
|
||||
#[cfg(feature = "luau")]
|
||||
{
|
||||
// Inherit `LUA_GLOBALSINDEX` from the caller
|
||||
ffi::lua_xpush(state, thread_state, ffi::LUA_GLOBALSINDEX);
|
||||
// Inherit `LUA_GLOBALSINDEX` from the main thread
|
||||
ffi::lua_xpush(lua.main_state(), thread_state, ffi::LUA_GLOBALSINDEX);
|
||||
ffi::lua_replace(thread_state, ffi::LUA_GLOBALSINDEX);
|
||||
}
|
||||
|
||||
@@ -332,11 +360,8 @@ impl<'lua> Thread<'lua> {
|
||||
let state = lua.state();
|
||||
unsafe {
|
||||
let thread = ffi::lua_tothread(lua.ref_thread(), self.0.index);
|
||||
check_stack(thread, 1)?;
|
||||
check_stack(thread, 3)?;
|
||||
check_stack(state, 3)?;
|
||||
// Inherit `LUA_GLOBALSINDEX` from the caller
|
||||
ffi::lua_xpush(state, thread, ffi::LUA_GLOBALSINDEX);
|
||||
ffi::lua_replace(thread, ffi::LUA_GLOBALSINDEX);
|
||||
protect_lua!(state, 0, 0, |_| ffi::luaL_sandboxthread(thread))
|
||||
}
|
||||
}
|
||||
@@ -372,7 +397,10 @@ impl<'lua, R> Drop for AsyncThread<'lua, R> {
|
||||
#[cfg(feature = "lua54")]
|
||||
if self.thread.status() == ThreadStatus::Error {
|
||||
let thread_state = ffi::lua_tothread(lua.ref_thread(), self.thread.0.index);
|
||||
#[cfg(not(feature = "vendored"))]
|
||||
ffi::lua_resetthread(thread_state);
|
||||
#[cfg(feature = "vendored")]
|
||||
ffi::lua_closethread(thread_state, lua.state());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+162
-18
@@ -1,6 +1,9 @@
|
||||
use std::cell::UnsafeCell;
|
||||
use std::any::{Any, TypeId};
|
||||
use std::cell::{Cell, Ref, RefCell, RefMut, UnsafeCell};
|
||||
use std::hash::{Hash, Hasher};
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use std::os::raw::{c_int, c_void};
|
||||
use std::result::Result as StdResult;
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::{fmt, mem, ptr};
|
||||
@@ -8,17 +11,21 @@ use std::{fmt, mem, ptr};
|
||||
#[cfg(feature = "lua54")]
|
||||
use std::ffi::CStr;
|
||||
|
||||
use rustc_hash::FxHashMap;
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
use futures_core::future::LocalBoxFuture;
|
||||
use futures_util::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.
|
||||
@@ -238,9 +245,9 @@ impl<'lua> PartialEq for LuaRef<'lua> {
|
||||
|
||||
#[cfg(feature = "unstable")]
|
||||
pub(crate) struct LuaOwnedRef {
|
||||
pub(crate) lua: Lua,
|
||||
pub(crate) inner: Arc<LuaInner>,
|
||||
pub(crate) index: c_int,
|
||||
_non_send: std::marker::PhantomData<*const ()>,
|
||||
_non_send: PhantomData<*const ()>,
|
||||
}
|
||||
|
||||
#[cfg(feature = "unstable")]
|
||||
@@ -260,37 +267,174 @@ impl Clone for LuaOwnedRef {
|
||||
#[cfg(feature = "unstable")]
|
||||
impl Drop for LuaOwnedRef {
|
||||
fn drop(&mut self) {
|
||||
self.lua.drop_ref_index(self.index);
|
||||
let lua: &Lua = unsafe { mem::transmute(&self.inner) };
|
||||
lua.drop_ref_index(self.index);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "unstable")]
|
||||
impl LuaOwnedRef {
|
||||
pub(crate) const fn new(lua: Lua, index: c_int) -> Self {
|
||||
#[cfg(feature = "send")]
|
||||
{
|
||||
let _lua = lua;
|
||||
let _index = index;
|
||||
panic!("mlua must be compiled without \"send\" feature to use Owned types");
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "send"))]
|
||||
pub(crate) const fn new(inner: Arc<LuaInner>, index: c_int) -> Self {
|
||||
LuaOwnedRef {
|
||||
lua,
|
||||
inner,
|
||||
index,
|
||||
_non_send: std::marker::PhantomData,
|
||||
_non_send: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) const fn to_ref(&self) -> LuaRef {
|
||||
LuaRef {
|
||||
lua: &self.lua,
|
||||
lua: unsafe { mem::transmute(&self.inner) },
|
||||
index: self.index,
|
||||
drop: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub(crate) struct AppData {
|
||||
#[cfg(not(feature = "send"))]
|
||||
container: UnsafeCell<FxHashMap<TypeId, RefCell<Box<dyn Any>>>>,
|
||||
#[cfg(feature = "send")]
|
||||
container: UnsafeCell<FxHashMap<TypeId, RefCell<Box<dyn Any + Send>>>>,
|
||||
borrow: Cell<usize>,
|
||||
}
|
||||
|
||||
impl AppData {
|
||||
#[track_caller]
|
||||
pub(crate) fn insert<T: MaybeSend + 'static>(&self, data: T) -> Option<T> {
|
||||
match self.try_insert(data) {
|
||||
Ok(data) => data,
|
||||
Err(_) => panic!("cannot mutably borrow app data container"),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn try_insert<T: MaybeSend + 'static>(&self, data: T) -> StdResult<Option<T>, T> {
|
||||
if self.borrow.get() != 0 {
|
||||
return Err(data);
|
||||
}
|
||||
// SAFETY: we checked that there are no other references to the container
|
||||
Ok(unsafe { &mut *self.container.get() }
|
||||
.insert(TypeId::of::<T>(), RefCell::new(Box::new(data)))
|
||||
.and_then(|data| data.into_inner().downcast::<T>().ok().map(|data| *data)))
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
pub(crate) fn borrow<T: 'static>(&self) -> Option<AppDataRef<T>> {
|
||||
let data = unsafe { &*self.container.get() }
|
||||
.get(&TypeId::of::<T>())?
|
||||
.borrow();
|
||||
self.borrow.set(self.borrow.get() + 1);
|
||||
Some(AppDataRef {
|
||||
data: Ref::filter_map(data, |data| data.downcast_ref()).ok()?,
|
||||
borrow: &self.borrow,
|
||||
})
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
pub(crate) fn borrow_mut<T: 'static>(&self) -> Option<AppDataRefMut<T>> {
|
||||
let data = unsafe { &*self.container.get() }
|
||||
.get(&TypeId::of::<T>())?
|
||||
.borrow_mut();
|
||||
self.borrow.set(self.borrow.get() + 1);
|
||||
Some(AppDataRefMut {
|
||||
data: RefMut::filter_map(data, |data| data.downcast_mut()).ok()?,
|
||||
borrow: &self.borrow,
|
||||
})
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
pub(crate) fn remove<T: 'static>(&self) -> Option<T> {
|
||||
if self.borrow.get() != 0 {
|
||||
panic!("cannot mutably borrow app data container");
|
||||
}
|
||||
// SAFETY: we checked that there are no other references to the container
|
||||
unsafe { &mut *self.container.get() }
|
||||
.remove(&TypeId::of::<T>())?
|
||||
.into_inner()
|
||||
.downcast::<T>()
|
||||
.ok()
|
||||
.map(|data| *data)
|
||||
}
|
||||
}
|
||||
|
||||
/// A wrapper type for an immutably borrowed value from an app data container.
|
||||
///
|
||||
/// This type is similar to [`Ref`].
|
||||
pub struct AppDataRef<'a, T: ?Sized + 'a> {
|
||||
data: Ref<'a, T>,
|
||||
borrow: &'a Cell<usize>,
|
||||
}
|
||||
|
||||
impl<T: ?Sized> Drop for AppDataRef<'_, T> {
|
||||
fn drop(&mut self) {
|
||||
self.borrow.set(self.borrow.get() - 1);
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized> Deref for AppDataRef<'_, T> {
|
||||
type Target = T;
|
||||
|
||||
#[inline]
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.data
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized + fmt::Display> fmt::Display for AppDataRef<'_, T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
(**self).fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized + fmt::Debug> fmt::Debug for AppDataRef<'_, T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
(**self).fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
/// A wrapper type for a mutably borrowed value from an app data container.
|
||||
///
|
||||
/// This type is similar to [`RefMut`].
|
||||
pub struct AppDataRefMut<'a, T: ?Sized + 'a> {
|
||||
data: RefMut<'a, T>,
|
||||
borrow: &'a Cell<usize>,
|
||||
}
|
||||
|
||||
impl<T: ?Sized> Drop for AppDataRefMut<'_, T> {
|
||||
fn drop(&mut self) {
|
||||
self.borrow.set(self.borrow.get() - 1);
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized> Deref for AppDataRefMut<'_, T> {
|
||||
type Target = T;
|
||||
|
||||
#[inline]
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.data
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized> DerefMut for AppDataRefMut<'_, T> {
|
||||
#[inline]
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.data
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized + fmt::Display> fmt::Display for AppDataRefMut<'_, T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
(**self).fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized + fmt::Debug> fmt::Debug for AppDataRefMut<'_, T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
(**self).fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod assertions {
|
||||
use super::*;
|
||||
|
||||
+105
-29
@@ -17,16 +17,14 @@ use {
|
||||
};
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
use crate::ffi;
|
||||
use crate::function::Function;
|
||||
use crate::lua::Lua;
|
||||
use crate::string::String;
|
||||
use crate::table::{Table, TablePairs};
|
||||
use crate::types::{Callback, LuaRef, MaybeSend};
|
||||
use crate::types::{LuaRef, MaybeSend};
|
||||
use crate::util::{check_stack, get_userdata, take_userdata, StackGuard};
|
||||
use crate::value::{FromLua, FromLuaMulti, IntoLua, IntoLuaMulti, Value};
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
use crate::types::AsyncCallback;
|
||||
use crate::UserDataRegistrar;
|
||||
|
||||
#[cfg(feature = "lua54")]
|
||||
pub(crate) const USER_VALUE_MAXSLOT: usize = 8;
|
||||
@@ -140,7 +138,8 @@ pub enum MetaMethod {
|
||||
/// Requires `feature = "lua54"`
|
||||
///
|
||||
/// [lua_doc]: https://www.lua.org/manual/5.4/manual.html#3.3.8
|
||||
#[cfg(any(feature = "lua54"))]
|
||||
#[cfg(feature = "lua54")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "lua54")))]
|
||||
Close,
|
||||
}
|
||||
|
||||
@@ -150,7 +149,7 @@ impl PartialEq<MetaMethod> for &str {
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq<MetaMethod> for String {
|
||||
impl PartialEq<MetaMethod> for StdString {
|
||||
fn eq(&self, other: &MetaMethod) -> bool {
|
||||
self == other.name()
|
||||
}
|
||||
@@ -411,24 +410,26 @@ pub trait UserDataMethods<'lua, T> {
|
||||
//
|
||||
|
||||
#[doc(hidden)]
|
||||
fn add_callback(&mut self, _name: String, _callback: Callback<'lua, 'static>) {}
|
||||
|
||||
#[doc(hidden)]
|
||||
#[cfg(feature = "async")]
|
||||
fn add_async_callback(&mut self, _name: String, _callback: AsyncCallback<'lua, 'static>) {}
|
||||
|
||||
#[doc(hidden)]
|
||||
fn add_meta_callback(&mut self, _name: String, _callback: Callback<'lua, 'static>) {}
|
||||
|
||||
#[doc(hidden)]
|
||||
#[cfg(feature = "async")]
|
||||
fn add_async_meta_callback(&mut self, _name: String, _callback: AsyncCallback<'lua, 'static>) {}
|
||||
fn append_methods_from<S>(&mut self, _other: UserDataRegistrar<'lua, S>) {}
|
||||
}
|
||||
|
||||
/// Field registry for [`UserData`] implementors.
|
||||
///
|
||||
/// [`UserData`]: crate::UserData
|
||||
pub trait UserDataFields<'lua, T> {
|
||||
/// Add a static field to the `UserData`.
|
||||
///
|
||||
/// Static fields are implemented by updating the `__index` metamethod and returning the
|
||||
/// accessed field. This allows them to be used with the expected `userdata.field` syntax.
|
||||
///
|
||||
/// Static fields are usually shared between all instances of the `UserData` of the same type.
|
||||
///
|
||||
/// If `add_meta_method` is used to set the `__index` metamethod, it will
|
||||
/// be used as a fall-back if no regular field or method are found.
|
||||
fn add_field<V>(&mut self, name: impl AsRef<str>, value: V)
|
||||
where
|
||||
V: IntoLua<'lua> + Clone + 'static;
|
||||
|
||||
/// Add a regular field getter as a method which accepts a `&T` as the parameter.
|
||||
///
|
||||
/// Regular field getters are implemented by overriding the `__index` metamethod and returning the
|
||||
@@ -477,9 +478,21 @@ pub trait UserDataFields<'lua, T> {
|
||||
F: FnMut(&'lua Lua, AnyUserData<'lua>, A) -> Result<()> + MaybeSend + 'static,
|
||||
A: FromLua<'lua>;
|
||||
|
||||
/// Add a metamethod value computed from `f`.
|
||||
/// Add a metatable field.
|
||||
///
|
||||
/// This will initialize the metamethod value from `f` on `UserData` creation.
|
||||
/// This will initialize the metatable field with `value` on `UserData` creation.
|
||||
///
|
||||
/// # Note
|
||||
///
|
||||
/// `mlua` will trigger an error on an attempt to define a protected metamethod,
|
||||
/// like `__gc` or `__metatable`.
|
||||
fn add_meta_field<V>(&mut self, name: impl AsRef<str>, value: V)
|
||||
where
|
||||
V: IntoLua<'lua> + Clone + 'static;
|
||||
|
||||
/// Add a metatable field computed from `f`.
|
||||
///
|
||||
/// This will initialize the metatable field from `f` on `UserData` creation.
|
||||
///
|
||||
/// # Note
|
||||
///
|
||||
@@ -495,10 +508,7 @@ pub trait UserDataFields<'lua, T> {
|
||||
//
|
||||
|
||||
#[doc(hidden)]
|
||||
fn add_field_getter(&mut self, _name: String, _callback: Callback<'lua, 'static>) {}
|
||||
|
||||
#[doc(hidden)]
|
||||
fn add_field_setter(&mut self, _name: String, _callback: Callback<'lua, 'static>) {}
|
||||
fn append_fields_from<S>(&mut self, _other: UserDataRegistrar<'lua, S>) {}
|
||||
}
|
||||
|
||||
/// Trait for custom userdata types.
|
||||
@@ -740,6 +750,11 @@ impl Serialize for UserDataSerializeError {
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct AnyUserData<'lua>(pub(crate) LuaRef<'lua>);
|
||||
|
||||
/// Owned handle to an internal Lua userdata.
|
||||
///
|
||||
/// 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.
|
||||
#[cfg(feature = "unstable")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "unstable")))]
|
||||
#[derive(Clone, Debug)]
|
||||
@@ -747,6 +762,8 @@ pub struct OwnedAnyUserData(pub(crate) crate::types::LuaOwnedRef);
|
||||
|
||||
#[cfg(feature = "unstable")]
|
||||
impl OwnedAnyUserData {
|
||||
/// Get borrowed handle to the underlying Lua userdata.
|
||||
#[cfg_attr(feature = "send", allow(unused))]
|
||||
pub const fn to_ref(&self) -> AnyUserData {
|
||||
AnyUserData(self.0.to_ref())
|
||||
}
|
||||
@@ -1030,13 +1047,38 @@ impl<'lua> AnyUserData<'lua> {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "unstable")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "unstable")))]
|
||||
/// 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) -> OwnedAnyUserData {
|
||||
OwnedAnyUserData(self.0.into_owned())
|
||||
}
|
||||
|
||||
/// Returns a type name of this `UserData` (from `__name` metatable field).
|
||||
pub(crate) fn type_name(&self) -> Result<Option<StdString>> {
|
||||
let lua = self.0.lua;
|
||||
let state = lua.state();
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(state);
|
||||
check_stack(state, 3)?;
|
||||
|
||||
lua.push_userdata_ref(&self.0)?;
|
||||
let protect = !lua.unlikely_memory_error();
|
||||
let name_type = if protect {
|
||||
protect_lua!(state, 1, 1, |state| {
|
||||
ffi::luaL_getmetafield(state, -1, cstr!("__name"))
|
||||
})?
|
||||
} else {
|
||||
ffi::luaL_getmetafield(state, -1, cstr!("__name"))
|
||||
};
|
||||
match name_type {
|
||||
ffi::LUA_TSTRING => Ok(Some(String(lua.pop_ref()).to_str()?.to_owned())),
|
||||
_ => Ok(None),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn equals<T: AsRef<Self>>(&self, other: T) -> Result<bool> {
|
||||
let other = other.as_ref();
|
||||
// Uses lua_rawequal() under the hood
|
||||
@@ -1121,6 +1163,40 @@ unsafe fn getuservalue_table(state: *mut ffi::lua_State, idx: c_int) -> c_int {
|
||||
return ffi::lua_getuservalue(state, idx);
|
||||
}
|
||||
|
||||
// Additional shortcuts
|
||||
#[cfg(feature = "unstable")]
|
||||
impl OwnedAnyUserData {
|
||||
/// Borrow this userdata immutably if it is of type `T`.
|
||||
///
|
||||
/// This is a shortcut for [`AnyUserData::borrow()`]
|
||||
#[inline]
|
||||
pub fn borrow<T: 'static>(&self) -> Result<Ref<T>> {
|
||||
let ud = self.to_ref();
|
||||
let t = ud.borrow::<T>()?;
|
||||
// Reattach lifetime to &self
|
||||
Ok(unsafe { mem::transmute::<Ref<T>, Ref<T>>(t) })
|
||||
}
|
||||
|
||||
/// Borrow this userdata mutably if it is of type `T`.
|
||||
///
|
||||
/// This is a shortcut for [`AnyUserData::borrow_mut()`]
|
||||
#[inline]
|
||||
pub fn borrow_mut<T: 'static>(&self) -> Result<RefMut<T>> {
|
||||
let ud = self.to_ref();
|
||||
let t = ud.borrow_mut::<T>()?;
|
||||
// Reattach lifetime to &self
|
||||
Ok(unsafe { mem::transmute::<RefMut<T>, RefMut<T>>(t) })
|
||||
}
|
||||
|
||||
/// Takes the value out of this userdata.
|
||||
///
|
||||
/// This is a shortcut for [`AnyUserData::take()`]
|
||||
#[inline]
|
||||
pub fn take<T: 'static>(&self) -> Result<T> {
|
||||
self.to_ref().take()
|
||||
}
|
||||
}
|
||||
|
||||
/// Handle to a `UserData` metatable.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct UserDataMetatable<'lua>(pub(crate) Table<'lua>);
|
||||
@@ -1178,7 +1254,7 @@ impl<'lua, V> Iterator for UserDataMetatablePairs<'lua, V>
|
||||
where
|
||||
V: FromLua<'lua>,
|
||||
{
|
||||
type Item = Result<(String, V)>;
|
||||
type Item = Result<(StdString, V)>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
loop {
|
||||
@@ -1286,6 +1362,6 @@ mod assertions {
|
||||
|
||||
static_assertions::assert_not_impl_any!(AnyUserData: Send);
|
||||
|
||||
#[cfg(feature = "unstable")]
|
||||
#[cfg(all(feature = "unstable", not(feature = "send")))]
|
||||
static_assertions::assert_not_impl_any!(OwnedAnyUserData: Send);
|
||||
}
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@ use crate::userdata::{AnyUserData, MetaMethod};
|
||||
use crate::value::{FromLua, FromLuaMulti, IntoLua, IntoLuaMulti, Value};
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
use {futures_core::future::LocalBoxFuture, futures_util::future};
|
||||
use futures_util::future::{self, LocalBoxFuture};
|
||||
|
||||
/// An extension trait for [`AnyUserData`] that provides a variety of convenient functionality.
|
||||
pub trait AnyUserDataExt<'lua>: Sealed {
|
||||
|
||||
+72
-71
@@ -1,18 +1,17 @@
|
||||
use std::any::{self, TypeId};
|
||||
use std::any::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, IntoLua, IntoLuaMulti, Value};
|
||||
use crate::util::{check_stack, get_userdata, short_type_name, StackGuard};
|
||||
use crate::value::{FromLua, FromLuaMulti, IntoLua, IntoLuaMulti, MultiValue, Value};
|
||||
|
||||
#[cfg(not(feature = "send"))]
|
||||
use std::rc::Rc;
|
||||
@@ -26,13 +25,10 @@ use {
|
||||
|
||||
pub struct UserDataRegistrar<'lua, T: 'static> {
|
||||
// Fields
|
||||
pub(crate) fields: Vec<(String, Callback<'lua, 'static>)>,
|
||||
pub(crate) field_getters: Vec<(String, Callback<'lua, 'static>)>,
|
||||
pub(crate) field_setters: Vec<(String, Callback<'lua, 'static>)>,
|
||||
#[allow(clippy::type_complexity)]
|
||||
pub(crate) meta_fields: Vec<(
|
||||
String,
|
||||
Box<dyn Fn(&'lua Lua) -> Result<Value<'lua>> + 'static>,
|
||||
)>,
|
||||
pub(crate) meta_fields: Vec<(String, Callback<'lua, 'static>)>,
|
||||
|
||||
// Methods
|
||||
pub(crate) methods: Vec<(String, Callback<'lua, 'static>)>,
|
||||
@@ -48,6 +44,7 @@ pub struct UserDataRegistrar<'lua, T: 'static> {
|
||||
impl<'lua, T: 'static> UserDataRegistrar<'lua, T> {
|
||||
pub(crate) const fn new() -> Self {
|
||||
UserDataRegistrar {
|
||||
fields: Vec::new(),
|
||||
field_getters: Vec::new(),
|
||||
field_setters: Vec::new(),
|
||||
meta_fields: Vec::new(),
|
||||
@@ -361,15 +358,49 @@ impl<'lua, T: 'static> UserDataRegistrar<'lua, T> {
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn check_meta_field<V>(
|
||||
lua: &'lua Lua,
|
||||
name: &str,
|
||||
value: V,
|
||||
) -> Result<MultiValue<'lua>>
|
||||
where
|
||||
V: IntoLua<'lua>,
|
||||
{
|
||||
let value = value.into_lua(lua)?;
|
||||
if name == MetaMethod::Index || name == MetaMethod::NewIndex {
|
||||
match value {
|
||||
Value::Nil | Value::Table(_) | Value::Function(_) => {}
|
||||
_ => {
|
||||
return Err(Error::MetaMethodTypeError {
|
||||
method: name.to_string(),
|
||||
type_name: value.type_name(),
|
||||
message: Some("expected nil, table or function".to_string()),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
value.into_lua_multi(lua)
|
||||
}
|
||||
}
|
||||
|
||||
// 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}",)
|
||||
fn get_function_name<T>(name: &str) -> StdString {
|
||||
format!("{}.{name}", short_type_name::<T>())
|
||||
}
|
||||
|
||||
impl<'lua, T: 'static> UserDataFields<'lua, T> for UserDataRegistrar<'lua, T> {
|
||||
fn add_field<V>(&mut self, name: impl AsRef<str>, value: V)
|
||||
where
|
||||
V: IntoLua<'lua> + Clone + 'static,
|
||||
{
|
||||
let name = name.as_ref().to_string();
|
||||
self.fields.push((
|
||||
name,
|
||||
Box::new(move |lua, _| value.clone().into_lua_multi(lua)),
|
||||
));
|
||||
}
|
||||
|
||||
fn add_field_method_get<M, R>(&mut self, name: impl AsRef<str>, method: M)
|
||||
where
|
||||
M: Fn(&'lua Lua, &T) -> Result<R> + MaybeSend + 'static,
|
||||
@@ -410,41 +441,38 @@ impl<'lua, T: 'static> UserDataFields<'lua, T> for UserDataRegistrar<'lua, T> {
|
||||
self.field_setters.push((name.into(), func));
|
||||
}
|
||||
|
||||
fn add_meta_field<V>(&mut self, name: impl AsRef<str>, value: V)
|
||||
where
|
||||
V: IntoLua<'lua> + Clone + 'static,
|
||||
{
|
||||
let name = name.as_ref().to_string();
|
||||
let name2 = name.clone();
|
||||
self.meta_fields.push((
|
||||
name,
|
||||
Box::new(move |lua, _| Self::check_meta_field(lua, &name2, value.clone())),
|
||||
));
|
||||
}
|
||||
|
||||
fn add_meta_field_with<F, R>(&mut self, name: impl AsRef<str>, f: F)
|
||||
where
|
||||
F: Fn(&'lua Lua) -> Result<R> + MaybeSend + 'static,
|
||||
R: IntoLua<'lua>,
|
||||
{
|
||||
let name = name.as_ref().to_string();
|
||||
let name2 = name.clone();
|
||||
self.meta_fields.push((
|
||||
name.clone(),
|
||||
Box::new(move |lua| {
|
||||
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: name.clone(),
|
||||
type_name: value.type_name(),
|
||||
message: Some("expected nil, table or function".to_string()),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(value)
|
||||
}),
|
||||
name,
|
||||
Box::new(move |lua, _| Self::check_meta_field(lua, &name2, f(lua)?)),
|
||||
));
|
||||
}
|
||||
|
||||
// Below are internal methods
|
||||
|
||||
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: String, callback: Callback<'lua, 'static>) {
|
||||
self.field_setters.push((name, callback));
|
||||
fn append_fields_from<S>(&mut self, other: UserDataRegistrar<'lua, S>) {
|
||||
self.fields.extend(other.fields);
|
||||
self.field_getters.extend(other.field_getters);
|
||||
self.field_setters.extend(other.field_setters);
|
||||
self.meta_fields.extend(other.meta_fields);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -593,22 +621,13 @@ impl<'lua, T: 'static> UserDataMethods<'lua, T> for UserDataRegistrar<'lua, T> {
|
||||
|
||||
// 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))
|
||||
fn append_methods_from<S>(&mut self, other: UserDataRegistrar<'lua, S>) {
|
||||
self.methods.extend(other.methods);
|
||||
#[cfg(feature = "async")]
|
||||
self.async_methods.extend(other.async_methods);
|
||||
self.meta_methods.extend(other.meta_methods);
|
||||
#[cfg(feature = "async")]
|
||||
self.async_meta_methods.extend(other.async_meta_methods);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -628,31 +647,13 @@ macro_rules! lua_userdata_impl {
|
||||
fn add_fields<'lua, F: UserDataFields<'lua, Self>>(fields: &mut F) {
|
||||
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);
|
||||
}
|
||||
for (name, callback) in orig_fields.field_setters {
|
||||
fields.add_field_setter(name, callback);
|
||||
}
|
||||
fields.append_fields_from(orig_fields);
|
||||
}
|
||||
|
||||
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
|
||||
let mut orig_methods = UserDataRegistrar::new();
|
||||
T::add_methods(&mut orig_methods);
|
||||
for (name, callback) in orig_methods.methods {
|
||||
methods.add_callback(name, callback);
|
||||
}
|
||||
#[cfg(feature = "async")]
|
||||
for (name, callback) in orig_methods.async_methods {
|
||||
methods.add_async_callback(name, callback);
|
||||
}
|
||||
for (meta, callback) in orig_methods.meta_methods {
|
||||
methods.add_meta_callback(meta, callback);
|
||||
}
|
||||
#[cfg(feature = "async")]
|
||||
for (meta, callback) in orig_methods.async_meta_methods {
|
||||
methods.add_async_meta_callback(meta, callback);
|
||||
}
|
||||
methods.append_methods_from(orig_methods);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -11,9 +11,10 @@ use once_cell::sync::Lazy;
|
||||
use rustc_hash::FxHashMap;
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
use crate::ffi;
|
||||
use crate::memory::MemoryState;
|
||||
|
||||
pub(crate) use short_names::short_type_name;
|
||||
|
||||
static METATABLE_CACHE: Lazy<FxHashMap<TypeId, u8>> = Lazy::new(|| {
|
||||
let mut map = FxHashMap::with_capacity_and_hasher(32, Default::default());
|
||||
crate::lua::init_metatable_cache(&mut map);
|
||||
@@ -401,9 +402,12 @@ unsafe extern "C" fn lua_error_impl(state: *mut ffi::lua_State) -> c_int {
|
||||
}
|
||||
|
||||
unsafe extern "C" fn lua_isfunction_impl(state: *mut ffi::lua_State) -> c_int {
|
||||
let t = ffi::lua_type(state, -1);
|
||||
ffi::lua_pop(state, 1);
|
||||
ffi::lua_pushboolean(state, (t == ffi::LUA_TFUNCTION) as c_int);
|
||||
ffi::lua_pushboolean(state, ffi::lua_isfunction(state, -1));
|
||||
1
|
||||
}
|
||||
|
||||
unsafe extern "C" fn lua_istable_impl(state: *mut ffi::lua_State) -> c_int {
|
||||
ffi::lua_pushboolean(state, ffi::lua_istable(state, -1));
|
||||
1
|
||||
}
|
||||
|
||||
@@ -417,14 +421,19 @@ unsafe fn init_userdata_metatable_index(state: *mut ffi::lua_State) -> Result<()
|
||||
// Create and cache `__index` generator
|
||||
let code = cstr!(
|
||||
r#"
|
||||
local error, isfunction = ...
|
||||
local error, isfunction, istable = ...
|
||||
return function (__index, field_getters, methods)
|
||||
-- Fastpath to return methods table for index access
|
||||
if __index == nil and field_getters == nil then
|
||||
return methods
|
||||
-- Common case: has field getters and index is a table
|
||||
if field_getters ~= nil and methods == nil and istable(__index) then
|
||||
return function (self, key)
|
||||
local field_getter = field_getters[key]
|
||||
if field_getter ~= nil then
|
||||
return field_getter(self)
|
||||
end
|
||||
return __index[key]
|
||||
end
|
||||
end
|
||||
|
||||
-- Alternatively return a function for index access
|
||||
return function (self, key)
|
||||
if field_getters ~= nil then
|
||||
local field_getter = field_getters[key]
|
||||
@@ -459,7 +468,13 @@ unsafe fn init_userdata_metatable_index(state: *mut ffi::lua_State) -> Result<()
|
||||
}
|
||||
ffi::lua_pushcfunction(state, lua_error_impl);
|
||||
ffi::lua_pushcfunction(state, lua_isfunction_impl);
|
||||
ffi::lua_call(state, 2, 1);
|
||||
ffi::lua_pushcfunction(state, lua_istable_impl);
|
||||
ffi::lua_call(state, 3, 1);
|
||||
|
||||
#[cfg(feature = "luau-jit")]
|
||||
if ffi::luau_codegen_supported() != 0 {
|
||||
ffi::luau_codegen_compile(state, -1);
|
||||
}
|
||||
|
||||
// Store in the registry
|
||||
ffi::lua_pushvalue(state, -1);
|
||||
@@ -509,6 +524,11 @@ pub unsafe fn init_userdata_metatable_newindex(state: *mut ffi::lua_State) -> Re
|
||||
ffi::lua_pushcfunction(state, lua_isfunction_impl);
|
||||
ffi::lua_call(state, 2, 1);
|
||||
|
||||
#[cfg(feature = "luau-jit")]
|
||||
if ffi::luau_codegen_supported() != 0 {
|
||||
ffi::luau_codegen_compile(state, -1);
|
||||
}
|
||||
|
||||
// Store in the registry
|
||||
ffi::lua_pushvalue(state, -1);
|
||||
ffi::lua_rawsetp(state, ffi::LUA_REGISTRYINDEX, newindex_key);
|
||||
@@ -1057,3 +1077,5 @@ static DESTRUCTED_USERDATA_METATABLE: u8 = 0;
|
||||
static ERROR_PRINT_BUFFER_KEY: u8 = 0;
|
||||
static USERDATA_METATABLE_INDEX: u8 = 0;
|
||||
static USERDATA_METATABLE_NEWINDEX: u8 = 0;
|
||||
|
||||
mod short_names;
|
||||
@@ -0,0 +1,84 @@
|
||||
//! Mostly copied from bevy_utils
|
||||
//! https://github.com/bevyengine/bevy/blob/main/crates/bevy_utils/src/short_names.rs
|
||||
|
||||
use std::any::type_name;
|
||||
|
||||
/// Returns a short version of a type name `T` without all module paths.
|
||||
///
|
||||
/// The short name of a type is its full name as returned by
|
||||
/// [`std::any::type_name`], but with the prefix of all paths removed. For
|
||||
/// example, the short name of `alloc::vec::Vec<core::option::Option<u32>>`
|
||||
/// would be `Vec<Option<u32>>`.
|
||||
pub(crate) fn short_type_name<T: ?Sized>() -> String {
|
||||
let full_name = type_name::<T>();
|
||||
|
||||
// Generics result in nested paths within <..> blocks.
|
||||
// Consider "core::option::Option<alloc::string::String>".
|
||||
// To tackle this, we parse the string from left to right, collapsing as we go.
|
||||
let mut index: usize = 0;
|
||||
let end_of_string = full_name.len();
|
||||
let mut parsed_name = String::new();
|
||||
|
||||
while index < end_of_string {
|
||||
let rest_of_string = full_name.get(index..end_of_string).unwrap_or_default();
|
||||
|
||||
// Collapse everything up to the next special character,
|
||||
// then skip over it
|
||||
if let Some(special_character_index) = rest_of_string
|
||||
.find(|c: char| [' ', '<', '>', '(', ')', '[', ']', ',', ';'].contains(&c))
|
||||
{
|
||||
let segment_to_collapse = rest_of_string
|
||||
.get(0..special_character_index)
|
||||
.unwrap_or_default();
|
||||
parsed_name += collapse_type_name(segment_to_collapse);
|
||||
// Insert the special character
|
||||
let special_character =
|
||||
&rest_of_string[special_character_index..=special_character_index];
|
||||
parsed_name.push_str(special_character);
|
||||
|
||||
match special_character {
|
||||
">" | ")" | "]"
|
||||
if rest_of_string[special_character_index + 1..].starts_with("::") =>
|
||||
{
|
||||
parsed_name.push_str("::");
|
||||
// Move the index past the "::"
|
||||
index += special_character_index + 3;
|
||||
}
|
||||
// Move the index just past the special character
|
||||
_ => index += special_character_index + 1,
|
||||
}
|
||||
} else {
|
||||
// If there are no special characters left, we're done!
|
||||
parsed_name += collapse_type_name(rest_of_string);
|
||||
index = end_of_string;
|
||||
}
|
||||
}
|
||||
parsed_name
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn collapse_type_name(string: &str) -> &str {
|
||||
string.rsplit("::").next().unwrap()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::short_type_name;
|
||||
use std::collections::HashMap;
|
||||
|
||||
#[test]
|
||||
fn tests() {
|
||||
assert_eq!(short_type_name::<String>(), "String");
|
||||
assert_eq!(short_type_name::<Option<String>>(), "Option<String>");
|
||||
assert_eq!(short_type_name::<(String, &str)>(), "(String, &str)");
|
||||
assert_eq!(short_type_name::<[i32; 3]>(), "[i32; 3]");
|
||||
assert_eq!(
|
||||
short_type_name::<HashMap<String, Option<[i32; 3]>>>(),
|
||||
"HashMap<String, Option<[i32; 3]>>"
|
||||
);
|
||||
assert_eq!(
|
||||
short_type_name::<dyn Fn(i32) -> i32>(),
|
||||
"dyn Fn(i32) -> i32"
|
||||
);
|
||||
}
|
||||
}
|
||||
+140
-3
@@ -1,8 +1,11 @@
|
||||
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::string::String as StdString;
|
||||
use std::sync::Arc;
|
||||
use std::{ptr, slice, str, vec};
|
||||
use std::{fmt, ptr, slice, str, vec};
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
use {
|
||||
@@ -12,7 +15,6 @@ use {
|
||||
};
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
use crate::ffi;
|
||||
use crate::function::Function;
|
||||
use crate::lua::Lua;
|
||||
use crate::string::String;
|
||||
@@ -20,11 +22,12 @@ use crate::table::Table;
|
||||
use crate::thread::Thread;
|
||||
use crate::types::{Integer, LightUserData, Number};
|
||||
use crate::userdata::AnyUserData;
|
||||
use crate::util::{check_stack, StackGuard};
|
||||
|
||||
/// 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,
|
||||
@@ -62,6 +65,12 @@ pub enum Value<'lua> {
|
||||
pub use self::Value::Nil;
|
||||
|
||||
impl<'lua> Value<'lua> {
|
||||
/// A special value (lightuserdata) to represent null value.
|
||||
///
|
||||
/// It can be used in Lua tables without downsides of `nil`.
|
||||
pub const NULL: Value<'static> = Value::LightUserData(LightUserData(ptr::null_mut()));
|
||||
|
||||
/// Returns type name of this value.
|
||||
pub const fn type_name(&self) -> &'static str {
|
||||
match *self {
|
||||
Value::Nil => "nil",
|
||||
@@ -121,6 +130,134 @@ impl<'lua> Value<'lua> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Converts the value to a string.
|
||||
///
|
||||
/// If the value has a metatable with a `__tostring` method, then it will be called to get the result.
|
||||
pub fn to_string(&self) -> Result<StdString> {
|
||||
match self {
|
||||
Value::Nil => Ok("nil".to_string()),
|
||||
Value::Boolean(b) => Ok(b.to_string()),
|
||||
Value::LightUserData(ud) if ud.0.is_null() => Ok("null".to_string()),
|
||||
Value::LightUserData(ud) => Ok(format!("lightuserdata: {:p}", ud.0)),
|
||||
Value::Integer(i) => Ok(i.to_string()),
|
||||
Value::Number(n) => Ok(n.to_string()),
|
||||
#[cfg(feature = "luau")]
|
||||
Value::Vector(x, y, z) => Ok(format!("vector({x}, {y}, {z})")),
|
||||
Value::String(s) => Ok(s.to_str()?.to_string()),
|
||||
Value::Table(Table(r))
|
||||
| Value::Function(Function(r))
|
||||
| Value::Thread(Thread(r))
|
||||
| Value::UserData(AnyUserData(r)) => unsafe {
|
||||
let state = r.lua.state();
|
||||
let _guard = StackGuard::new(state);
|
||||
check_stack(state, 3)?;
|
||||
|
||||
r.lua.push_ref(r);
|
||||
protect_lua!(state, 1, 1, fn(state) {
|
||||
ffi::luaL_tolstring(state, -1, ptr::null_mut());
|
||||
})?;
|
||||
Ok(String(r.lua.pop_ref()).to_str()?.to_string())
|
||||
},
|
||||
Value::Error(err) => Ok(err.to_string()),
|
||||
}
|
||||
}
|
||||
|
||||
// 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()),
|
||||
u @ Value::UserData(ud) => {
|
||||
let name = ud.type_name().ok().flatten();
|
||||
let name = name.unwrap_or_else(|| "userdata".to_string());
|
||||
write!(fmt, "{name}: {:?}", 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> {
|
||||
|
||||
@@ -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"
|
||||
+60
-3
@@ -1,7 +1,7 @@
|
||||
#![cfg(feature = "async")]
|
||||
|
||||
use std::sync::atomic::{AtomicU64, Ordering};
|
||||
use std::sync::Arc;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::time::Duration;
|
||||
|
||||
use futures_timer::Delay;
|
||||
@@ -9,7 +9,7 @@ use futures_util::stream::TryStreamExt;
|
||||
|
||||
use mlua::{
|
||||
AnyUserDataExt, Error, Function, Lua, LuaOptions, Result, StdLib, Table, TableExt, UserData,
|
||||
UserDataMethods,
|
||||
UserDataMethods, Value,
|
||||
};
|
||||
|
||||
#[tokio::test]
|
||||
@@ -26,7 +26,6 @@ async fn test_async_function() -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(feature = "unstable")]
|
||||
#[tokio::test]
|
||||
async fn test_async_function_wrap() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
@@ -271,6 +270,24 @@ 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_pool_size(4);
|
||||
@@ -467,3 +484,43 @@ async fn test_async_thread_error() -> Result<()> {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "unstable", not(feature = "send")))]
|
||||
#[tokio::test]
|
||||
async fn test_owned_async_call() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let hello = lua
|
||||
.create_async_function(|_, name: String| async move {
|
||||
Delay::new(Duration::from_millis(10)).await;
|
||||
Ok(format!("hello, {}!", name))
|
||||
})?
|
||||
.into_owned();
|
||||
drop(lua);
|
||||
|
||||
assert_eq!(hello.call_async::<_, String>("alex").await?, "hello, alex!");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_async_terminate() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let mutex = Arc::new(Mutex::new(0u32));
|
||||
let mutex2 = mutex.clone();
|
||||
let func = lua.create_async_function(move |_, ()| {
|
||||
let mutex = mutex2.clone();
|
||||
async move {
|
||||
let _guard = mutex.lock();
|
||||
Delay::new(Duration::from_millis(100)).await;
|
||||
Ok(())
|
||||
}
|
||||
})?;
|
||||
|
||||
let _ = tokio::time::timeout(Duration::from_millis(30), func.call_async::<_, ()>(())).await;
|
||||
lua.gc_collect()?;
|
||||
assert!(mutex.try_lock().is_ok());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -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(())
|
||||
}
|
||||
@@ -1,3 +1,5 @@
|
||||
use std::io;
|
||||
|
||||
use mlua::{Error, ErrorContext, Lua, Result};
|
||||
|
||||
#[test]
|
||||
@@ -29,5 +31,20 @@ fn test_error_context() -> Result<()> {
|
||||
println!("{msg2}");
|
||||
assert!(msg2.contains("error converting Lua nil to String"));
|
||||
|
||||
// Rewrite context message and test `downcast_ref`
|
||||
let func3 = lua.create_function(|_, ()| {
|
||||
Err::<(), _>(Error::external(io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
"other",
|
||||
)))
|
||||
.context("some context")
|
||||
.context("some new context")
|
||||
})?;
|
||||
let res = func3.call::<_, ()>(()).err().unwrap();
|
||||
let Error::CallbackError { cause, .. } = &res else { unreachable!() };
|
||||
assert!(!res.to_string().contains("some context"));
|
||||
assert!(res.to_string().contains("some new context"));
|
||||
assert!(cause.downcast_ref::<io::Error>().is_some());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
+117
-13
@@ -1,4 +1,4 @@
|
||||
use mlua::{Function, Lua, Result, String};
|
||||
use mlua::{Function, Lua, Result, String, Table};
|
||||
|
||||
#[test]
|
||||
fn test_function() -> Result<()> {
|
||||
@@ -114,6 +114,66 @@ fn test_dump() -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_function_environment() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
// We must not get or set environment for C functions
|
||||
let rust_func = lua.create_function(|_, ()| Ok("hello"))?;
|
||||
assert_eq!(rust_func.environment(), None);
|
||||
assert_eq!(rust_func.set_environment(lua.globals()).ok(), Some(false));
|
||||
|
||||
// Test getting Lua function environment
|
||||
lua.globals().set("hello", "global")?;
|
||||
let lua_func = lua
|
||||
.load(
|
||||
r#"
|
||||
local t = ""
|
||||
return function()
|
||||
-- two upvalues
|
||||
return t .. hello
|
||||
end
|
||||
"#,
|
||||
)
|
||||
.eval::<Function>()?;
|
||||
let lua_func2 = lua.load("return hello").into_function()?;
|
||||
assert_eq!(lua_func.call::<_, String>(())?, "global");
|
||||
assert_eq!(lua_func.environment(), Some(lua.globals()));
|
||||
|
||||
// Test changing the environment
|
||||
let env = lua.create_table_from([("hello", "local")])?;
|
||||
assert!(lua_func.set_environment(env.clone())?);
|
||||
assert_eq!(lua_func.call::<_, String>(())?, "local");
|
||||
assert_eq!(lua_func2.call::<_, String>(())?, "global");
|
||||
|
||||
// More complex case
|
||||
lua.load(
|
||||
r#"
|
||||
local number = 15
|
||||
function lucky() return tostring("number is "..number) end
|
||||
new_env = {
|
||||
tostring = function() return tostring(number) end,
|
||||
}
|
||||
"#,
|
||||
)
|
||||
.exec()?;
|
||||
let lucky = lua.globals().get::<_, Function>("lucky")?;
|
||||
assert_eq!(lucky.call::<_, String>(())?, "number is 15");
|
||||
let new_env = lua.globals().get::<_, Table>("new_env")?;
|
||||
lucky.set_environment(new_env)?;
|
||||
assert_eq!(lucky.call::<_, String>(())?, "15");
|
||||
|
||||
// Test inheritance
|
||||
let lua_func2 = lua
|
||||
.load(r#"return function() return (function() return hello end)() end"#)
|
||||
.eval::<Function>()?;
|
||||
assert!(lua_func2.set_environment(env.clone())?);
|
||||
lua.gc_collect()?;
|
||||
assert_eq!(lua_func2.call::<_, String>(())?, "local");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_function_info() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
@@ -135,40 +195,42 @@ fn test_function_info() -> Result<()> {
|
||||
|
||||
let function1_info = function1.info();
|
||||
#[cfg(feature = "luau")]
|
||||
assert_eq!(function1_info.name, Some(b"function1".to_vec()));
|
||||
assert_eq!(function1_info.source, Some(b"source1".to_vec()));
|
||||
assert_eq!(function1_info.name.as_deref(), Some("function1"));
|
||||
assert_eq!(function1_info.source.as_deref(), Some(b"source1".as_ref()));
|
||||
assert_eq!(function1_info.line_defined, 2);
|
||||
#[cfg(not(feature = "luau"))]
|
||||
assert_eq!(function1_info.last_line_defined, 4);
|
||||
assert_eq!(function1_info.what, Some(b"Lua".to_vec()));
|
||||
#[cfg(feature = "luau")]
|
||||
assert_eq!(function1_info.last_line_defined, -1);
|
||||
assert_eq!(function1_info.what.as_deref(), Some("Lua"));
|
||||
|
||||
let function2_info = function2.info();
|
||||
assert_eq!(function2_info.name, None);
|
||||
assert_eq!(function2_info.source, Some(b"source1".to_vec()));
|
||||
assert_eq!(function2_info.source.as_deref(), Some(b"source1".as_ref()));
|
||||
assert_eq!(function2_info.line_defined, 3);
|
||||
#[cfg(not(feature = "luau"))]
|
||||
assert_eq!(function2_info.last_line_defined, 3);
|
||||
assert_eq!(function2_info.what, Some(b"Lua".to_vec()));
|
||||
#[cfg(feature = "luau")]
|
||||
assert_eq!(function2_info.last_line_defined, -1);
|
||||
assert_eq!(function2_info.what.as_deref(), Some("Lua"));
|
||||
|
||||
let function3_info = function3.info();
|
||||
assert_eq!(function3_info.name, None);
|
||||
assert_eq!(function3_info.source, Some(b"=[C]".to_vec()));
|
||||
assert_eq!(function3_info.source.as_deref(), Some(b"=[C]".as_ref()));
|
||||
assert_eq!(function3_info.line_defined, -1);
|
||||
#[cfg(not(feature = "luau"))]
|
||||
assert_eq!(function3_info.last_line_defined, -1);
|
||||
assert_eq!(function3_info.what, Some(b"C".to_vec()));
|
||||
assert_eq!(function3_info.what.as_deref(), Some("C"));
|
||||
|
||||
let print_info = globals.get::<_, Function>("print")?.info();
|
||||
#[cfg(feature = "luau")]
|
||||
assert_eq!(print_info.name, Some(b"print".to_vec()));
|
||||
assert_eq!(print_info.source, Some(b"=[C]".to_vec()));
|
||||
assert_eq!(print_info.what, Some(b"C".to_vec()));
|
||||
assert_eq!(print_info.name.as_deref(), Some("print"));
|
||||
assert_eq!(print_info.source.as_deref(), Some(b"=[C]".as_ref()));
|
||||
assert_eq!(print_info.what.as_deref(), Some("C"));
|
||||
assert_eq!(print_info.line_defined, -1);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(feature = "unstable")]
|
||||
#[test]
|
||||
fn test_function_wrap() -> Result<()> {
|
||||
use mlua::Error;
|
||||
@@ -200,3 +262,45 @@ fn test_function_wrap() -> Result<()> {
|
||||
|
||||
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(())
|
||||
}
|
||||
|
||||
@@ -9,6 +9,16 @@ use std::sync::Arc;
|
||||
|
||||
use mlua::{Compiler, CoverageInfo, Error, Lua, Result, Table, ThreadStatus, Value, VmState};
|
||||
|
||||
#[test]
|
||||
fn test_version() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
assert!(lua
|
||||
.globals()
|
||||
.get::<_, String>("_VERSION")?
|
||||
.starts_with("Luau 0."));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_require() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
+18
-5
@@ -392,31 +392,44 @@ fn test_from_value_newtype_struct() -> Result<(), Box<dyn StdError>> {
|
||||
#[test]
|
||||
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(())
|
||||
}
|
||||
|
||||
|
||||
@@ -98,3 +98,22 @@ fn test_string_debug() -> Result<()> {
|
||||
|
||||
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(())
|
||||
}
|
||||
|
||||
@@ -392,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(())
|
||||
}
|
||||
|
||||
+37
-9
@@ -887,19 +887,47 @@ fn test_application_data() -> Result<()> {
|
||||
lua.set_app_data("test1");
|
||||
lua.set_app_data(vec!["test2"]);
|
||||
|
||||
// Borrow &str immutably and Vec<&str> mutably
|
||||
let s = lua.app_data_ref::<&str>().unwrap();
|
||||
let mut v = lua.app_data_mut::<Vec<&str>>().unwrap();
|
||||
v.push("test3");
|
||||
|
||||
// Insert of new data or removal should fail now
|
||||
assert!(lua.try_set_app_data::<i32>(123).is_err());
|
||||
match catch_unwind(AssertUnwindSafe(|| lua.set_app_data::<i32>(123))) {
|
||||
Ok(_) => panic!("expected panic"),
|
||||
Err(_) => {}
|
||||
}
|
||||
match catch_unwind(AssertUnwindSafe(|| lua.remove_app_data::<i32>())) {
|
||||
Ok(_) => panic!("expected panic"),
|
||||
Err(_) => {}
|
||||
}
|
||||
|
||||
// Check display and debug impls
|
||||
assert_eq!(format!("{s}"), "test1");
|
||||
assert_eq!(format!("{s:?}"), "\"test1\"");
|
||||
|
||||
// Borrowing immutably and mutably of the same type is not allowed
|
||||
match catch_unwind(AssertUnwindSafe(|| lua.app_data_mut::<&str>().unwrap())) {
|
||||
Ok(_) => panic!("expected panic"),
|
||||
Err(_) => {}
|
||||
}
|
||||
drop((s, v));
|
||||
|
||||
// Test that application data is accessible from anywhere
|
||||
let f = lua.create_function(|lua, ()| {
|
||||
{
|
||||
let data1 = lua.app_data_ref::<&str>().unwrap();
|
||||
assert_eq!(*data1, "test1");
|
||||
}
|
||||
let mut data2 = lua.app_data_mut::<Vec<&str>>().unwrap();
|
||||
assert_eq!(*data2, vec!["test2"]);
|
||||
data2.push("test3");
|
||||
let mut data1 = lua.app_data_mut::<&str>().unwrap();
|
||||
assert_eq!(*data1, "test1");
|
||||
*data1 = "test4";
|
||||
|
||||
let data2 = lua.app_data_ref::<Vec<&str>>().unwrap();
|
||||
assert_eq!(*data2, vec!["test2", "test3"]);
|
||||
|
||||
Ok(())
|
||||
})?;
|
||||
f.call(())?;
|
||||
|
||||
assert_eq!(*lua.app_data_ref::<&str>().unwrap(), "test1");
|
||||
assert_eq!(*lua.app_data_ref::<&str>().unwrap(), "test4");
|
||||
assert_eq!(
|
||||
*lua.app_data_ref::<Vec<&str>>().unwrap(),
|
||||
vec!["test2", "test3"]
|
||||
@@ -991,7 +1019,7 @@ fn test_ref_stack_exhaustion() {
|
||||
match catch_unwind(AssertUnwindSafe(|| -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
let mut vals = Vec::new();
|
||||
for _ in 0..1000000 {
|
||||
for _ in 0..10000000 {
|
||||
vals.push(lua.create_table()?);
|
||||
}
|
||||
Ok(())
|
||||
|
||||
+50
-26
@@ -1,3 +1,4 @@
|
||||
use std::collections::HashMap;
|
||||
use std::string::String as StdString;
|
||||
use std::sync::Arc;
|
||||
#[cfg(not(feature = "parking_lot"))]
|
||||
@@ -486,6 +487,7 @@ fn test_fields() -> Result<()> {
|
||||
|
||||
impl UserData for MyUserData {
|
||||
fn add_fields<'lua, F: UserDataFields<'lua, Self>>(fields: &mut F) {
|
||||
fields.add_field("static", "constant");
|
||||
fields.add_field_method_get("val", |_, data| Ok(data.0));
|
||||
fields.add_field_method_set("val", |_, data, val| {
|
||||
data.0 = val;
|
||||
@@ -497,11 +499,7 @@ fn test_fields() -> Result<()> {
|
||||
fields
|
||||
.add_field_function_set("uval", |_, ud, s| ud.set_user_value::<Option<String>>(s));
|
||||
|
||||
fields.add_meta_field_with(MetaMethod::Index, |lua| {
|
||||
let index = lua.create_table()?;
|
||||
index.set("f", 321)?;
|
||||
Ok(index)
|
||||
});
|
||||
fields.add_meta_field(MetaMethod::Index, HashMap::from([("f", 321)]));
|
||||
fields.add_meta_field_with(MetaMethod::NewIndex, |lua| {
|
||||
lua.create_function(|lua, (_, field, val): (AnyUserData, String, Value)| {
|
||||
lua.globals().set(field, val)?;
|
||||
@@ -516,6 +514,7 @@ fn test_fields() -> Result<()> {
|
||||
globals.set("ud", MyUserData(7))?;
|
||||
lua.load(
|
||||
r#"
|
||||
assert(ud.static == "constant")
|
||||
assert(ud.val == 7)
|
||||
ud.val = 10
|
||||
assert(ud.val == 10)
|
||||
@@ -538,30 +537,22 @@ fn test_fields() -> Result<()> {
|
||||
#[test]
|
||||
fn test_metatable() -> Result<()> {
|
||||
#[derive(Copy, Clone)]
|
||||
struct MyUserData(i64);
|
||||
struct MyUserData;
|
||||
|
||||
impl UserData for MyUserData {
|
||||
fn add_fields<'lua, F: UserDataFields<'lua, Self>>(fields: &mut F) {
|
||||
fields.add_meta_field_with("__type_name", |_| Ok("MyUserData"));
|
||||
}
|
||||
|
||||
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>("__name")
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
let lua = Lua::new();
|
||||
let globals = lua.globals();
|
||||
globals.set("ud", MyUserData(7))?;
|
||||
lua.load(
|
||||
r#"
|
||||
assert(ud:my_type_name() == "MyUserData")
|
||||
"#,
|
||||
)
|
||||
.exec()?;
|
||||
globals.set("ud", MyUserData)?;
|
||||
lua.load(r#"assert(ud:my_type_name() == "MyUserData")"#)
|
||||
.exec()?;
|
||||
|
||||
let ud: AnyUserData = globals.get("ud")?;
|
||||
let metatable = ud.get_metatable()?;
|
||||
@@ -583,10 +574,10 @@ fn test_metatable() -> Result<()> {
|
||||
.map(|kv: Result<(_, Value)>| Ok(kv?.0))
|
||||
.collect::<Result<Vec<_>>>()?;
|
||||
methods.sort();
|
||||
assert_eq!(methods, vec!["__index", "__type_name"]);
|
||||
assert_eq!(methods, vec!["__index", "__name"]);
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
struct MyUserData2(i64);
|
||||
struct MyUserData2;
|
||||
|
||||
impl UserData for MyUserData2 {
|
||||
fn add_fields<'lua, F: UserDataFields<'lua, Self>>(fields: &mut F) {
|
||||
@@ -594,12 +585,25 @@ fn test_metatable() -> Result<()> {
|
||||
}
|
||||
}
|
||||
|
||||
match lua.create_userdata(MyUserData2(1)) {
|
||||
match lua.create_userdata(MyUserData2) {
|
||||
Ok(_) => panic!("expected MetaMethodTypeError, got no error"),
|
||||
Err(Error::MetaMethodTypeError { .. }) => {}
|
||||
Err(e) => panic!("expected MetaMethodTypeError, got {:?}", e),
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
struct MyUserData3;
|
||||
|
||||
impl UserData for MyUserData3 {
|
||||
fn add_fields<'lua, F: UserDataFields<'lua, Self>>(fields: &mut F) {
|
||||
fields.add_meta_field_with("__name", |_| Ok("CustomName"));
|
||||
}
|
||||
}
|
||||
|
||||
let ud = lua.create_userdata(MyUserData3)?;
|
||||
let metatable = ud.get_metatable()?;
|
||||
assert_eq!(metatable.get::<String>("__name")?.to_str()?, "CustomName");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -609,6 +613,7 @@ fn test_userdata_wrapped() -> Result<()> {
|
||||
|
||||
impl UserData for MyUserData {
|
||||
fn add_fields<'lua, F: UserDataFields<'lua, Self>>(fields: &mut F) {
|
||||
fields.add_field("static", "constant");
|
||||
fields.add_field_method_get("data", |_, this| Ok(this.0));
|
||||
fields.add_field_method_set("data", |_, this, val| {
|
||||
this.0 = val;
|
||||
@@ -626,6 +631,7 @@ fn test_userdata_wrapped() -> Result<()> {
|
||||
globals.set("rc_refcell_ud", ud1.clone())?;
|
||||
lua.load(
|
||||
r#"
|
||||
assert(rc_refcell_ud.static == "constant")
|
||||
rc_refcell_ud.data = rc_refcell_ud.data + 1
|
||||
assert(rc_refcell_ud.data == 2)
|
||||
"#,
|
||||
@@ -641,6 +647,7 @@ fn test_userdata_wrapped() -> Result<()> {
|
||||
globals.set("arc_mutex_ud", ud2.clone())?;
|
||||
lua.load(
|
||||
r#"
|
||||
assert(arc_mutex_ud.static == "constant")
|
||||
arc_mutex_ud.data = arc_mutex_ud.data + 1
|
||||
assert(arc_mutex_ud.data == 3)
|
||||
"#,
|
||||
@@ -655,6 +662,7 @@ fn test_userdata_wrapped() -> Result<()> {
|
||||
globals.set("arc_rwlock_ud", ud3.clone())?;
|
||||
lua.load(
|
||||
r#"
|
||||
assert(arc_rwlock_ud.static == "constant")
|
||||
arc_rwlock_ud.data = arc_rwlock_ud.data + 1
|
||||
assert(arc_rwlock_ud.data == 4)
|
||||
"#,
|
||||
@@ -681,7 +689,7 @@ fn test_userdata_proxy() -> Result<()> {
|
||||
|
||||
impl UserData for MyUserData {
|
||||
fn add_fields<'lua, F: UserDataFields<'lua, Self>>(fields: &mut F) {
|
||||
fields.add_field_function_get("static_field", |_, _| Ok(123));
|
||||
fields.add_field("static_field", 123);
|
||||
fields.add_field_method_get("n", |_, this| Ok(this.0));
|
||||
}
|
||||
|
||||
@@ -775,10 +783,7 @@ fn test_userdata_ext() -> Result<()> {
|
||||
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:?}"),
|
||||
}
|
||||
assert_eq!(ud.get::<_, Option<u32>>("non-existent")?, None);
|
||||
match ud.set::<_, u32>("non-existent", 123) {
|
||||
Err(Error::RuntimeError(_)) => {}
|
||||
r => panic!("expected RuntimeError, got {r:?}"),
|
||||
@@ -820,3 +825,22 @@ fn test_userdata_method_errors() -> Result<()> {
|
||||
|
||||
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!(matches!(
|
||||
ud.borrow::<i64>(),
|
||||
Err(Error::UserDataTypeMismatch)
|
||||
));
|
||||
assert_eq!(ud.take::<&str>()?, "cba");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
+73
-1
@@ -1,6 +1,9 @@
|
||||
use std::collections::HashMap;
|
||||
use std::os::raw::c_void;
|
||||
use std::ptr;
|
||||
use std::string::String as StdString;
|
||||
|
||||
use mlua::{Lua, MultiValue, Result, Value};
|
||||
use mlua::{Error, LightUserData, Lua, MultiValue, Result, UserData, UserDataMethods, Value};
|
||||
|
||||
#[test]
|
||||
fn test_value_eq() -> Result<()> {
|
||||
@@ -28,6 +31,7 @@ fn test_value_eq() -> Result<()> {
|
||||
"#,
|
||||
)
|
||||
.exec()?;
|
||||
globals.set("null", Value::NULL)?;
|
||||
|
||||
let table1: Value = globals.get("table1")?;
|
||||
let table2: Value = globals.get("table2")?;
|
||||
@@ -41,6 +45,7 @@ fn test_value_eq() -> Result<()> {
|
||||
let func3: Value = globals.get("func3")?;
|
||||
let thread1: Value = globals.get("thread1")?;
|
||||
let thread2: Value = globals.get("thread2")?;
|
||||
let null: Value = globals.get("null")?;
|
||||
|
||||
assert!(table1 != table2);
|
||||
assert!(table1.equals(&table2)?);
|
||||
@@ -54,6 +59,7 @@ fn test_value_eq() -> Result<()> {
|
||||
assert!(!func1.equals(&func3)?);
|
||||
assert!(thread1 == thread2);
|
||||
assert!(thread1.equals(&thread2)?);
|
||||
assert!(null == Value::NULL);
|
||||
|
||||
assert!(!table1.to_pointer().is_null());
|
||||
assert!(!ptr::eq(table1.to_pointer(), table2.to_pointer()));
|
||||
@@ -81,3 +87,69 @@ fn test_multi_value() {
|
||||
multi_value.clear();
|
||||
assert!(multi_value.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_value_to_string() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
assert_eq!(Value::Nil.to_string()?, "nil");
|
||||
assert_eq!(Value::Boolean(true).to_string()?, "true");
|
||||
assert_eq!(Value::NULL.to_string()?, "null");
|
||||
assert_eq!(
|
||||
Value::LightUserData(LightUserData(0x1 as *const c_void as *mut _)).to_string()?,
|
||||
"lightuserdata: 0x1"
|
||||
);
|
||||
assert_eq!(Value::Integer(1).to_string()?, "1");
|
||||
assert_eq!(Value::Number(34.59).to_string()?, "34.59");
|
||||
#[cfg(feature = "luau")]
|
||||
assert_eq!(
|
||||
Value::Vector(10.0, 11.1, 12.2).to_string()?,
|
||||
"vector(10, 11.1, 12.2)"
|
||||
);
|
||||
assert_eq!(
|
||||
Value::String(lua.create_string("hello")?).to_string()?,
|
||||
"hello"
|
||||
);
|
||||
|
||||
let table: Value = lua.load("{}").eval()?;
|
||||
assert!(table.to_string()?.starts_with("table:"));
|
||||
let table: Value = lua
|
||||
.load("setmetatable({}, {__tostring = function() return 'test table' end})")
|
||||
.eval()?;
|
||||
assert_eq!(table.to_string()?, "test table");
|
||||
|
||||
let func: Value = lua.load("function() end").eval()?;
|
||||
assert!(func.to_string()?.starts_with("function:"));
|
||||
|
||||
let thread: Value = lua.load("coroutine.create(function() end)").eval()?;
|
||||
assert!(thread.to_string()?.starts_with("thread:"));
|
||||
|
||||
lua.register_userdata_type::<StdString>(|reg| {
|
||||
reg.add_meta_method("__tostring", |_, this, ()| Ok(this.clone()));
|
||||
})?;
|
||||
let ud: Value = Value::UserData(lua.create_any_userdata(String::from("string userdata"))?);
|
||||
assert_eq!(ud.to_string()?, "string userdata");
|
||||
|
||||
struct MyUserData;
|
||||
impl UserData for MyUserData {}
|
||||
let ud: Value = Value::UserData(lua.create_userdata(MyUserData)?);
|
||||
assert!(ud.to_string()?.starts_with("MyUserData:"));
|
||||
|
||||
let err = Value::Error(Error::RuntimeError("test error".to_string()));
|
||||
assert_eq!(err.to_string()?, "runtime error: test error");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_debug_format() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
lua.register_userdata_type::<HashMap<i32, StdString>>(|_| {})?;
|
||||
let ud = lua
|
||||
.create_any_userdata::<HashMap<i32, StdString>>(HashMap::new())
|
||||
.map(Value::UserData)?;
|
||||
assert!(format!("{ud:#?}").starts_with("HashMap<i32, String>:"));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user