mirror of
https://github.com/mlua-rs/mlua
synced 2026-06-08 16:05:43 +00:00
Compare commits
62 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 75a15ceabf | |||
| 60730fd068 | |||
| df778b7b33 | |||
| 45299c0ef1 | |||
| 38eec1236c | |||
| 145c5b316b | |||
| e97e69a309 | |||
| 2ac7b23596 | |||
| 8200bee467 | |||
| fe6ab250bf | |||
| 804972b099 | |||
| 727f99ee4d | |||
| 3c801e7b17 | |||
| a38e484fe9 | |||
| eed48889cd | |||
| b5896173fd | |||
| 205989f569 | |||
| 12472de1d2 | |||
| a68708c12e | |||
| 4c0474d573 | |||
| 60e859f643 | |||
| 9ed0d90746 | |||
| 514ec24252 | |||
| 244e6c9c12 | |||
| 4749e3a22a | |||
| cf153f38de | |||
| c0a0983025 | |||
| 69ff0c5509 | |||
| bf79d6c212 | |||
| 0b9a85e183 | |||
| 59974d7bde | |||
| 61e846326c | |||
| 3547985bb0 | |||
| 4c92580201 | |||
| e3f34f319c | |||
| b16f3895a0 | |||
| a4c919231c | |||
| e4d6e92287 | |||
| 642201a7e0 | |||
| c36808b251 | |||
| 2022de2156 | |||
| 66e01548ce | |||
| 93b505cff9 | |||
| 44f5688c32 | |||
| 2bee5ed33a | |||
| 2d775695ef | |||
| 34476ebf53 | |||
| 5043447f23 | |||
| b879abc418 | |||
| a1e39a8620 | |||
| f5021daaea | |||
| ec0fb7614e | |||
| 04c0763146 | |||
| 7d0aa7c01b | |||
| 2a3980ef62 | |||
| adb979761e | |||
| 37d722d71c | |||
| 6e3af593f6 | |||
| 54c14cc5b8 | |||
| 7f0de5790a | |||
| 6488477bc4 | |||
| 53b7b5b70b |
+34
-11
@@ -18,7 +18,7 @@ jobs:
|
||||
- os: windows-latest
|
||||
target: x86_64-pc-windows-msvc
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
toolchain: ${{ matrix.rust }}
|
||||
@@ -44,7 +44,7 @@ jobs:
|
||||
matrix:
|
||||
lua: [lua54, lua53, lua52, lua51, luajit]
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
toolchain: stable
|
||||
@@ -60,7 +60,7 @@ jobs:
|
||||
matrix:
|
||||
lua: [lua54, lua53, lua52, lua51, luajit]
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
toolchain: stable
|
||||
@@ -82,7 +82,7 @@ jobs:
|
||||
matrix:
|
||||
lua: [lua54, lua53, lua52, lua51]
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
toolchain: stable
|
||||
@@ -113,7 +113,7 @@ jobs:
|
||||
- os: windows-latest
|
||||
target: x86_64-pc-windows-msvc
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
toolchain: ${{ matrix.rust }}
|
||||
@@ -145,7 +145,7 @@ jobs:
|
||||
- os: ubuntu-22.04
|
||||
target: x86_64-unknown-linux-gnu
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
toolchain: ${{ matrix.rust }}
|
||||
@@ -166,14 +166,14 @@ jobs:
|
||||
matrix:
|
||||
os: [ubuntu-22.04, macos-latest]
|
||||
rust: [stable]
|
||||
lua: [lua54, lua53, lua52, lua51, luajit]
|
||||
lua: [lua54, lua53, lua52, lua51, luajit, luau]
|
||||
include:
|
||||
- os: ubuntu-22.04
|
||||
target: x86_64-unknown-linux-gnu
|
||||
- os: macos-latest
|
||||
target: x86_64-apple-darwin
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
toolchain: ${{ matrix.rust }}
|
||||
@@ -197,7 +197,7 @@ jobs:
|
||||
shell: msys2 {0}
|
||||
steps:
|
||||
- uses: msys2/setup-msys2@v2
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install Rust & Lua
|
||||
run: |
|
||||
pacman -S --noconfirm mingw-w64-x86_64-rust mingw-w64-x86_64-lua mingw-w64-x86_64-luajit mingw-w64-x86_64-pkg-config
|
||||
@@ -206,11 +206,34 @@ jobs:
|
||||
(cd tests/module && cargo build --release --features "${{ matrix.lua }}")
|
||||
(cd tests/module/loader && cargo test --release --features "${{ matrix.lua }}")
|
||||
|
||||
test_wasm32_emscripten:
|
||||
name: Test on wasm32-unknown-emscripten
|
||||
runs-on: ubuntu-22.04
|
||||
needs: build
|
||||
strategy:
|
||||
matrix:
|
||||
lua: [lua54, lua53, lua52, lua51, luau]
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
toolchain: stable
|
||||
target: wasm32-unknown-emscripten
|
||||
- name: Install Emscripten
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y --no-install-recommends emscripten
|
||||
- name: Run ${{ matrix.lua }} tests
|
||||
run: |
|
||||
cargo test --tests --features "${{ matrix.lua }},vendored"
|
||||
cargo test --tests --features "${{ matrix.lua }},vendored,async,send,serialize,macros,parking_lot"
|
||||
cargo test --tests --features "${{ matrix.lua }},vendored,async,serialize,macros,parking_lot,unstable"
|
||||
|
||||
rustfmt:
|
||||
name: Rustfmt
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
toolchain: stable
|
||||
@@ -224,7 +247,7 @@ jobs:
|
||||
matrix:
|
||||
lua: [lua54, lua53, lua52, lua51, luajit, luau, luau-jit, luau-vector4]
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
toolchain: nightly
|
||||
|
||||
@@ -1,3 +1,31 @@
|
||||
## v0.9.5
|
||||
|
||||
- Minimal Luau updated to 0.609
|
||||
- Luau max stack size increased to 1M (from 100K)
|
||||
- Implemented `IntoLua` for refs to `String`/`Table`/`Function`/`AnyUserData`/`Thread` + `RegistryKey`
|
||||
- Implemented `IntoLua` and `FromLua` for `OwnedThread`/`OwnedString`
|
||||
- Fixed `FromLua` derive proc macro to cover more cases
|
||||
|
||||
## v0.9.4
|
||||
|
||||
- Fixed loading all-in-one modules under mixed states (eg. main state and coroutines)
|
||||
|
||||
## v0.9.3
|
||||
|
||||
- WebAssembly support (`wasm32-unknown-emscripten` target)
|
||||
- Performance improvements (faster Lua function calls for lua51/jit/luau)
|
||||
|
||||
## v0.9.2
|
||||
|
||||
- Added binary modules support to Luau
|
||||
- Added Luau package module (uses `StdLib::PACKAGE`) with loaders (follows lua5.1 interface)
|
||||
- Added support of Luau 0.601+ buffer type (represented as userdata in Rust)
|
||||
- LuaJIT `cdata` type is also represented as userdata in Rust (instead of panic)
|
||||
- Vendored LuaJIT switched to rolling vanilla (from openresty)
|
||||
- Added `Table::for_each` method for fast table pairs traversal (faster than `pairs`)
|
||||
- Performance improvements around table traversal (and faster serialization)
|
||||
- Bug fixes and improvements
|
||||
|
||||
## v0.9.1
|
||||
|
||||
- impl Default for Lua
|
||||
|
||||
+20
-9
@@ -1,7 +1,7 @@
|
||||
[package]
|
||||
name = "mlua"
|
||||
version = "0.9.1" # remember to update mlua_derive
|
||||
authors = ["Aleksandr Orlenko <zxteam@pm.me>", "kyren <catherine@chucklefish.org>"]
|
||||
version = "0.9.5" # remember to update mlua_derive
|
||||
authors = ["Aleksandr Orlenko <zxteam@pm.me>", "kyren <catherine@kyju.org>"]
|
||||
rust-version = "1.71"
|
||||
edition = "2021"
|
||||
repository = "https://github.com/khvzak/mlua"
|
||||
@@ -32,7 +32,7 @@ lua52 = ["ffi/lua52"]
|
||||
lua51 = ["ffi/lua51"]
|
||||
luajit = ["ffi/luajit"]
|
||||
luajit52 = ["luajit", "ffi/luajit52"]
|
||||
luau = ["ffi/luau"]
|
||||
luau = ["ffi/luau", "libloading"]
|
||||
luau-jit = ["luau", "ffi/luau-codegen"]
|
||||
luau-vector4 = ["luau", "ffi/luau-vector4"]
|
||||
vendored = ["ffi/vendored"]
|
||||
@@ -44,38 +44,49 @@ macros = ["mlua_derive/macros"]
|
||||
unstable = []
|
||||
|
||||
[dependencies]
|
||||
mlua_derive = { version = "=0.9.0", optional = true, path = "mlua_derive" }
|
||||
mlua_derive = { version = "=0.9.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-util = { version = "0.3", optional = true, default-features = false, features = ["std"] }
|
||||
serde = { version = "1.0", optional = true }
|
||||
erased-serde = { version = "0.3", optional = true }
|
||||
erased-serde = { version = "0.4", optional = true }
|
||||
serde-value = { version = "0.7", optional = true }
|
||||
parking_lot = { version = "0.12", optional = true }
|
||||
|
||||
ffi = { package = "mlua-sys", version = "0.3.2", path = "mlua-sys" }
|
||||
ffi = { package = "mlua-sys", version = "0.5.1", path = "mlua-sys" }
|
||||
|
||||
[target.'cfg(unix)'.dependencies]
|
||||
libloading = { version = "0.8", optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
rustyline = "12.0"
|
||||
criterion = { version = "0.5", features = ["async_tokio"] }
|
||||
trybuild = "1.0"
|
||||
futures = "0.3.5"
|
||||
hyper = { version = "0.14", features = ["client", "server"] }
|
||||
reqwest = { version = "0.11", features = ["json"] }
|
||||
tokio = { version = "1.0", features = ["full"] }
|
||||
tokio = { version = "1.0", features = ["macros", "rt", "time"] }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
maplit = "1.0"
|
||||
tempfile = "3"
|
||||
static_assertions = "1.0"
|
||||
|
||||
[target.'cfg(not(target_arch = "wasm32"))'.dev-dependencies]
|
||||
criterion = { version = "0.5", features = ["async_tokio"] }
|
||||
rustyline = "13.0"
|
||||
tokio = { version = "1.0", features = ["full"] }
|
||||
|
||||
[[bench]]
|
||||
name = "benchmark"
|
||||
harness = false
|
||||
required-features = ["async"]
|
||||
|
||||
[[bench]]
|
||||
name = "serde"
|
||||
harness = false
|
||||
required-features = ["serialize"]
|
||||
|
||||
[[example]]
|
||||
name = "async_http_client"
|
||||
required-features = ["async", "macros"]
|
||||
|
||||
@@ -7,8 +7,8 @@
|
||||
[crates.io]: https://crates.io/crates/mlua
|
||||
[API Documentation]: https://docs.rs/mlua/badge.svg
|
||||
[docs.rs]: https://docs.rs/mlua
|
||||
[Coverage Status]: https://codecov.io/gh/khvzak/mlua/branch/master/graph/badge.svg?token=99339FS1CG
|
||||
[codecov.io]: https://codecov.io/gh/khvzak/mlua
|
||||
[Coverage Status]: https://codecov.io/gh/mlua-rs/mlua/branch/master/graph/badge.svg?token=99339FS1CG
|
||||
[codecov.io]: https://codecov.io/gh/mlua-rs/mlua
|
||||
[MSRV]: https://img.shields.io/badge/rust-1.71+-brightgreen.svg?&logo=rust
|
||||
|
||||
[Guided Tour] | [Benchmarks] | [FAQ]
|
||||
@@ -28,6 +28,8 @@ Started as `rlua` fork, `mlua` supports Lua 5.4, 5.3, 5.2, 5.1 (including LuaJIT
|
||||
|
||||
`mlua` tested on Windows/macOS/Linux including module mode in [GitHub Actions] on `x86_64` platform and cross-compilation to `aarch64` (other targets are also supported).
|
||||
|
||||
WebAssembly (WASM) is supported through `wasm32-unknown-emscripten` target for all Lua versions excluding JIT.
|
||||
|
||||
[GitHub Actions]: https://github.com/khvzak/mlua/actions
|
||||
[Roblox Luau]: https://luau-lang.org
|
||||
|
||||
|
||||
@@ -84,6 +84,52 @@ fn table_get_set(c: &mut Criterion) {
|
||||
});
|
||||
}
|
||||
|
||||
fn table_traversal_pairs(c: &mut Criterion) {
|
||||
let lua = Lua::new();
|
||||
|
||||
c.bench_function("table traversal [pairs]", |b| {
|
||||
b.iter_batched(
|
||||
|| lua.globals(),
|
||||
|globals| {
|
||||
for kv in globals.pairs::<String, LuaValue>() {
|
||||
let (_k, _v) = kv.unwrap();
|
||||
}
|
||||
},
|
||||
BatchSize::SmallInput,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
fn table_traversal_for_each(c: &mut Criterion) {
|
||||
let lua = Lua::new();
|
||||
|
||||
c.bench_function("table traversal [for_each]", |b| {
|
||||
b.iter_batched(
|
||||
|| lua.globals(),
|
||||
|globals| globals.for_each::<String, LuaValue>(|_k, _v| Ok(())),
|
||||
BatchSize::SmallInput,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
fn table_traversal_sequence(c: &mut Criterion) {
|
||||
let lua = Lua::new();
|
||||
|
||||
let table = lua.create_sequence_from(1..1000).unwrap();
|
||||
|
||||
c.bench_function("table traversal [sequence]", |b| {
|
||||
b.iter_batched(
|
||||
|| table.clone(),
|
||||
|table| {
|
||||
for v in table.sequence_values::<i32>() {
|
||||
let _i = v.unwrap();
|
||||
}
|
||||
},
|
||||
BatchSize::SmallInput,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
fn create_function(c: &mut Criterion) {
|
||||
let lua = Lua::new();
|
||||
|
||||
@@ -331,6 +377,9 @@ criterion_group! {
|
||||
create_array,
|
||||
create_string_table,
|
||||
table_get_set,
|
||||
table_traversal_pairs,
|
||||
table_traversal_for_each,
|
||||
table_traversal_sequence,
|
||||
create_function,
|
||||
call_lua_function,
|
||||
call_sum_callback,
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
use criterion::{criterion_group, criterion_main, BatchSize, Criterion};
|
||||
use std::time::Duration;
|
||||
|
||||
use mlua::prelude::*;
|
||||
|
||||
fn collect_gc_twice(lua: &Lua) {
|
||||
lua.gc_collect().unwrap();
|
||||
lua.gc_collect().unwrap();
|
||||
}
|
||||
|
||||
fn serialize_json(c: &mut Criterion) {
|
||||
let lua = Lua::new();
|
||||
|
||||
lua.globals()
|
||||
.set(
|
||||
"encode",
|
||||
LuaFunction::wrap(|_, t: LuaValue| Ok(serde_json::to_string(&t).unwrap())),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
c.bench_function("serialize table to json [10]", |b| {
|
||||
b.iter_batched(
|
||||
|| {
|
||||
collect_gc_twice(&lua);
|
||||
lua.load(
|
||||
r#"
|
||||
local encode = encode
|
||||
return function()
|
||||
for i = 1, 10 do
|
||||
encode({
|
||||
name = "Clark Kent",
|
||||
nickname = "Superman",
|
||||
address = {
|
||||
city = "Metropolis",
|
||||
},
|
||||
age = 32,
|
||||
superman = true,
|
||||
})
|
||||
end
|
||||
end
|
||||
"#,
|
||||
)
|
||||
.eval::<LuaFunction>()
|
||||
.unwrap()
|
||||
},
|
||||
|func| {
|
||||
func.call::<_, ()>(()).unwrap();
|
||||
},
|
||||
BatchSize::SmallInput,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
criterion_group! {
|
||||
name = benches;
|
||||
config = Criterion::default()
|
||||
.sample_size(300)
|
||||
.measurement_time(Duration::from_secs(10))
|
||||
.noise_threshold(0.02);
|
||||
targets =
|
||||
serialize_json,
|
||||
}
|
||||
|
||||
criterion_main!(benches);
|
||||
@@ -19,7 +19,7 @@ impl UserData for BodyReader {
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
#[tokio::main(flavor = "current_thread")]
|
||||
async fn main() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use mlua::{chunk, ExternalResult, Lua, LuaSerdeExt, Result};
|
||||
|
||||
#[tokio::main]
|
||||
#[tokio::main(flavor = "current_thread")]
|
||||
async fn main() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ fn main() -> Result<()> {
|
||||
|
||||
// You can load and evaluate Lua code. The returned type of `Lua::load` is a builder
|
||||
// that allows you to change settings before running Lua code. Here, we are using it to set
|
||||
// the name of the laoded chunk to "example code", which will be used when Lua error
|
||||
// the name of the loaded chunk to "example code", which will be used when Lua error
|
||||
// messages are printed.
|
||||
|
||||
lua.load(
|
||||
@@ -89,7 +89,7 @@ fn main() -> Result<()> {
|
||||
let print: Function = globals.get("print")?;
|
||||
print.call::<_, ()>("hello from rust")?;
|
||||
|
||||
// This API generally handles variadics using tuples. This is one way to call a function with
|
||||
// This API generally handles variadic using tuples. This is one way to call a function with
|
||||
// multiple parameters:
|
||||
|
||||
print.call::<_, ()>(("hello", "again", "from", "rust"))?;
|
||||
@@ -100,7 +100,7 @@ fn main() -> Result<()> {
|
||||
["hello", "yet", "again", "from", "rust"].iter().cloned(),
|
||||
))?;
|
||||
|
||||
// You can bind rust functions to Lua as well. Callbacks receive the Lua state inself as their
|
||||
// You can bind rust functions to Lua as well. Callbacks receive the Lua state itself as their
|
||||
// first parameter, and the arguments given to the function as the second parameter. The type
|
||||
// of the arguments can be anything that is convertible from the parameters given by Lua, in
|
||||
// this case, the function expects two string sequences.
|
||||
|
||||
+4
-4
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "mlua-sys"
|
||||
version = "0.3.2"
|
||||
version = "0.5.1"
|
||||
authors = ["Aleksandr Orlenko <zxteam@pm.me>"]
|
||||
rust-version = "1.71"
|
||||
edition = "2021"
|
||||
@@ -38,6 +38,6 @@ module = []
|
||||
cc = "1.0"
|
||||
cfg-if = "1.0"
|
||||
pkg-config = "0.3.17"
|
||||
lua-src = { version = ">= 546.0.0, < 546.1.0", optional = true }
|
||||
luajit-src = { version = ">= 210.4.6, < 210.5.0", optional = true }
|
||||
luau0-src = { version = "0.7.0", optional = true }
|
||||
lua-src = { version = ">= 546.0.2, < 546.1.0", optional = true }
|
||||
luajit-src = { version = ">= 210.5.0, < 210.6.0", optional = true }
|
||||
luau0-src = { version = "0.8.0", optional = true }
|
||||
|
||||
@@ -4,6 +4,12 @@ use std::env;
|
||||
use std::ops::Bound;
|
||||
|
||||
pub fn probe_lua() {
|
||||
let target_arch = env::var("CARGO_CFG_TARGET_ARCH").unwrap();
|
||||
|
||||
if target_arch == "wasm32" && cfg!(not(feature = "vendored")) {
|
||||
panic!("Please enable `vendored` feature to build for wasm32");
|
||||
}
|
||||
|
||||
let lib_dir = env::var("LUA_LIB").unwrap_or_default();
|
||||
let lua_lib = env::var("LUA_LIB_NAME").unwrap_or_default();
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@ pub fn probe_lua() {
|
||||
#[cfg(feature = "luau")]
|
||||
let artifacts = luau0_src::Build::new()
|
||||
.enable_codegen(cfg!(feature = "luau-codegen"))
|
||||
.set_max_cstack_size(1000000)
|
||||
.set_vector_size(if cfg!(feature = "luau-vector4") { 4 } else { 3 })
|
||||
.build();
|
||||
|
||||
|
||||
@@ -9,8 +9,8 @@ cfg_if::cfg_if! {
|
||||
}
|
||||
|
||||
fn main() {
|
||||
#[cfg(all(feature = "luau", feature = "module"))]
|
||||
compile_error!("Luau does not support `module` mode");
|
||||
#[cfg(all(feature = "luau", feature = "module", windows))]
|
||||
compile_error!("Luau does not support `module` mode on Windows");
|
||||
|
||||
#[cfg(all(feature = "module", feature = "vendored"))]
|
||||
compile_error!("`vendored` and `module` features are mutually exclusive");
|
||||
|
||||
@@ -65,6 +65,7 @@ pub const SYS_MIN_ALIGN: usize = 8;
|
||||
target_arch = "sparc64",
|
||||
target_arch = "riscv64",
|
||||
target_arch = "wasm64",
|
||||
target_arch = "loongarch64",
|
||||
))]
|
||||
#[doc(hidden)]
|
||||
pub const SYS_MIN_ALIGN: usize = 16;
|
||||
|
||||
@@ -43,7 +43,7 @@ extern "C-unwind" {
|
||||
pub fn luaL_checkudata(L: *mut lua_State, ud: c_int, tname: *const c_char) -> *mut c_void;
|
||||
|
||||
pub fn luaL_where(L: *mut lua_State, lvl: c_int);
|
||||
pub fn luaL_error(L: *mut lua_State, fmt: *const c_char, ...) -> !;
|
||||
pub fn luaL_error(L: *mut lua_State, fmt: *const c_char, ...) -> c_int;
|
||||
|
||||
pub fn luaL_checkoption(
|
||||
L: *mut lua_State,
|
||||
|
||||
@@ -228,13 +228,23 @@ extern "C-unwind" {
|
||||
//
|
||||
#[cfg_attr(all(windows, raw_dylib), link(name = "lua51", kind = "raw-dylib"))]
|
||||
extern "C-unwind" {
|
||||
pub fn lua_error(L: *mut lua_State) -> !;
|
||||
#[link_name = "lua_error"]
|
||||
fn lua_error_(L: *mut lua_State) -> c_int;
|
||||
pub fn lua_next(L: *mut lua_State, idx: c_int) -> c_int;
|
||||
pub fn lua_concat(L: *mut lua_State, n: c_int);
|
||||
pub fn lua_getallocf(L: *mut lua_State, ud: *mut *mut c_void) -> lua_Alloc;
|
||||
pub fn lua_setallocf(L: *mut lua_State, f: lua_Alloc, ud: *mut c_void);
|
||||
}
|
||||
|
||||
// lua_error does not return but is declared to return int, and Rust translates
|
||||
// ! to void which can cause link-time errors if the platform linker is aware
|
||||
// of return types and requires they match (for example: wasm does this).
|
||||
#[inline(always)]
|
||||
pub unsafe fn lua_error(L: *mut lua_State) -> ! {
|
||||
lua_error_(L);
|
||||
unreachable!();
|
||||
}
|
||||
|
||||
//
|
||||
// Some useful macros (implemented as Rust functions)
|
||||
//
|
||||
@@ -318,6 +328,14 @@ pub unsafe fn lua_getglobal_(L: *mut lua_State, var: *const c_char) {
|
||||
lua_getfield_(L, LUA_GLOBALSINDEX, var)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn lua_tolightuserdata(L: *mut lua_State, idx: c_int) -> *mut c_void {
|
||||
if lua_islightuserdata(L, idx) != 0 {
|
||||
return lua_touserdata(L, idx);
|
||||
}
|
||||
ptr::null_mut()
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn lua_tostring(L: *mut lua_State, i: c_int) -> *const c_char {
|
||||
lua_tolstring(L, i, ptr::null_mut())
|
||||
|
||||
@@ -49,7 +49,7 @@ extern "C-unwind" {
|
||||
pub fn luaL_checkudata(L: *mut lua_State, ud: c_int, tname: *const c_char) -> *mut c_void;
|
||||
|
||||
pub fn luaL_where(L: *mut lua_State, lvl: c_int);
|
||||
pub fn luaL_error(L: *mut lua_State, fmt: *const c_char, ...) -> !;
|
||||
pub fn luaL_error(L: *mut lua_State, fmt: *const c_char, ...) -> c_int;
|
||||
|
||||
pub fn luaL_checkoption(
|
||||
L: *mut lua_State,
|
||||
|
||||
@@ -308,7 +308,8 @@ extern "C-unwind" {
|
||||
//
|
||||
// Miscellaneous functions
|
||||
//
|
||||
pub fn lua_error(L: *mut lua_State) -> !;
|
||||
#[link_name = "lua_error"]
|
||||
fn lua_error_(L: *mut lua_State) -> c_int;
|
||||
pub fn lua_next(L: *mut lua_State, idx: c_int) -> c_int;
|
||||
pub fn lua_concat(L: *mut lua_State, n: c_int);
|
||||
pub fn lua_len(L: *mut lua_State, idx: c_int);
|
||||
@@ -316,6 +317,15 @@ extern "C-unwind" {
|
||||
pub fn lua_setallocf(L: *mut lua_State, f: lua_Alloc, ud: *mut c_void);
|
||||
}
|
||||
|
||||
// lua_error does not return but is declared to return int, and Rust translates
|
||||
// ! to void which can cause link-time errors if the platform linker is aware
|
||||
// of return types and requires they match (for example: wasm does this).
|
||||
#[inline(always)]
|
||||
pub unsafe fn lua_error(L: *mut lua_State) -> ! {
|
||||
lua_error_(L);
|
||||
unreachable!();
|
||||
}
|
||||
|
||||
//
|
||||
// Some useful macros (implemented as Rust functions)
|
||||
//
|
||||
@@ -407,6 +417,14 @@ pub unsafe fn lua_pushglobaltable(L: *mut lua_State) {
|
||||
lua_rawgeti_(L, LUA_REGISTRYINDEX, LUA_RIDX_GLOBALS as _)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn lua_tolightuserdata(L: *mut lua_State, idx: c_int) -> *mut c_void {
|
||||
if lua_islightuserdata(L, idx) != 0 {
|
||||
return lua_touserdata(L, idx);
|
||||
}
|
||||
ptr::null_mut()
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn lua_tostring(L: *mut lua_State, i: c_int) -> *const c_char {
|
||||
lua_tolstring(L, i, ptr::null_mut())
|
||||
@@ -453,7 +471,12 @@ extern "C-unwind" {
|
||||
pub fn lua_upvalueid(L: *mut lua_State, fidx: c_int, n: c_int) -> *mut c_void;
|
||||
pub fn lua_upvaluejoin(L: *mut lua_State, fidx1: c_int, n1: c_int, fidx2: c_int, n2: c_int);
|
||||
|
||||
pub fn lua_sethook(L: *mut lua_State, func: Option<lua_Hook>, mask: c_int, count: c_int);
|
||||
pub fn lua_sethook(
|
||||
L: *mut lua_State,
|
||||
func: Option<lua_Hook>,
|
||||
mask: c_int,
|
||||
count: c_int,
|
||||
) -> c_int;
|
||||
pub fn lua_gethook(L: *mut lua_State) -> Option<lua_Hook>;
|
||||
pub fn lua_gethookmask(L: *mut lua_State) -> c_int;
|
||||
pub fn lua_gethookcount(L: *mut lua_State) -> c_int;
|
||||
|
||||
@@ -20,7 +20,7 @@ pub struct luaL_Reg {
|
||||
pub func: lua_CFunction,
|
||||
}
|
||||
|
||||
#[cfg_attr(all(windows, raw_dylib), link(name = "lua54", kind = "raw-dylib"))]
|
||||
#[cfg_attr(all(windows, raw_dylib), link(name = "lua53", kind = "raw-dylib"))]
|
||||
extern "C-unwind" {
|
||||
pub fn luaL_checkversion_(L: *mut lua_State, ver: lua_Number, sz: usize);
|
||||
|
||||
@@ -51,7 +51,7 @@ extern "C-unwind" {
|
||||
pub fn luaL_checkudata(L: *mut lua_State, ud: c_int, tname: *const c_char) -> *mut c_void;
|
||||
|
||||
pub fn luaL_where(L: *mut lua_State, lvl: c_int);
|
||||
pub fn luaL_error(L: *mut lua_State, fmt: *const c_char, ...) -> !;
|
||||
pub fn luaL_error(L: *mut lua_State, fmt: *const c_char, ...) -> c_int;
|
||||
|
||||
pub fn luaL_checkoption(
|
||||
L: *mut lua_State,
|
||||
|
||||
@@ -314,7 +314,8 @@ extern "C-unwind" {
|
||||
//
|
||||
// Miscellaneous functions
|
||||
//
|
||||
pub fn lua_error(L: *mut lua_State) -> !;
|
||||
#[link_name = "lua_error"]
|
||||
fn lua_error_(L: *mut lua_State) -> c_int;
|
||||
pub fn lua_next(L: *mut lua_State, idx: c_int) -> c_int;
|
||||
pub fn lua_concat(L: *mut lua_State, n: c_int);
|
||||
pub fn lua_len(L: *mut lua_State, idx: c_int);
|
||||
@@ -323,6 +324,15 @@ extern "C-unwind" {
|
||||
pub fn lua_setallocf(L: *mut lua_State, f: lua_Alloc, ud: *mut c_void);
|
||||
}
|
||||
|
||||
// lua_error does not return but is declared to return int, and Rust translates
|
||||
// ! to void which can cause link-time errors if the platform linker is aware
|
||||
// of return types and requires they match (for example: wasm does this).
|
||||
#[inline(always)]
|
||||
pub unsafe fn lua_error(L: *mut lua_State) -> ! {
|
||||
lua_error_(L);
|
||||
unreachable!();
|
||||
}
|
||||
|
||||
//
|
||||
// Some useful macros (implemented as Rust functions)
|
||||
//
|
||||
@@ -414,6 +424,14 @@ pub unsafe fn lua_pushglobaltable(L: *mut lua_State) -> c_int {
|
||||
lua_rawgeti(L, LUA_REGISTRYINDEX, LUA_RIDX_GLOBALS)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn lua_tolightuserdata(L: *mut lua_State, idx: c_int) -> *mut c_void {
|
||||
if lua_islightuserdata(L, idx) != 0 {
|
||||
return lua_touserdata(L, idx);
|
||||
}
|
||||
ptr::null_mut()
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn lua_tostring(L: *mut lua_State, i: c_int) -> *const c_char {
|
||||
lua_tolstring(L, i, ptr::null_mut())
|
||||
|
||||
@@ -50,7 +50,7 @@ extern "C-unwind" {
|
||||
pub fn luaL_checkudata(L: *mut lua_State, ud: c_int, tname: *const c_char) -> *mut c_void;
|
||||
|
||||
pub fn luaL_where(L: *mut lua_State, lvl: c_int);
|
||||
pub fn luaL_error(L: *mut lua_State, fmt: *const c_char, ...) -> !;
|
||||
pub fn luaL_error(L: *mut lua_State, fmt: *const c_char, ...) -> c_int;
|
||||
|
||||
pub fn luaL_checkoption(
|
||||
L: *mut lua_State,
|
||||
|
||||
@@ -149,13 +149,21 @@ extern "C-unwind" {
|
||||
pub fn lua_tointegerx(L: *mut lua_State, idx: c_int, isnum: *mut c_int) -> lua_Integer;
|
||||
pub fn lua_toboolean(L: *mut lua_State, idx: c_int) -> c_int;
|
||||
pub fn lua_tolstring(L: *mut lua_State, idx: c_int, len: *mut usize) -> *const c_char;
|
||||
pub fn lua_rawlen(L: *mut lua_State, idx: c_int) -> usize;
|
||||
#[link_name = "lua_rawlen"]
|
||||
fn lua_rawlen_(L: *mut lua_State, idx: c_int) -> lua_Unsigned;
|
||||
pub fn lua_tocfunction(L: *mut lua_State, idx: c_int) -> Option<lua_CFunction>;
|
||||
pub fn lua_touserdata(L: *mut lua_State, idx: c_int) -> *mut c_void;
|
||||
pub fn lua_tothread(L: *mut lua_State, idx: c_int) -> *mut lua_State;
|
||||
pub fn lua_topointer(L: *mut lua_State, idx: c_int) -> *const c_void;
|
||||
}
|
||||
|
||||
// lua_rawlen's return type changed from size_t to lua_Unsigned int in Lua 5.4.
|
||||
// This adapts the crate API to the new Lua ABI.
|
||||
#[inline(always)]
|
||||
pub unsafe fn lua_rawlen(L: *mut lua_State, idx: c_int) -> usize {
|
||||
lua_rawlen_(L, idx) as usize
|
||||
}
|
||||
|
||||
//
|
||||
// Comparison and arithmetic functions
|
||||
//
|
||||
@@ -336,7 +344,8 @@ extern "C-unwind" {
|
||||
//
|
||||
// Miscellaneous functions
|
||||
//
|
||||
pub fn lua_error(L: *mut lua_State) -> !;
|
||||
#[link_name = "lua_error"]
|
||||
fn lua_error_(L: *mut lua_State) -> c_int;
|
||||
pub fn lua_next(L: *mut lua_State, idx: c_int) -> c_int;
|
||||
pub fn lua_concat(L: *mut lua_State, n: c_int);
|
||||
pub fn lua_len(L: *mut lua_State, idx: c_int);
|
||||
@@ -348,6 +357,15 @@ extern "C-unwind" {
|
||||
pub fn lua_closeslot(L: *mut lua_State, idx: c_int);
|
||||
}
|
||||
|
||||
// lua_error does not return but is declared to return int, and Rust translates
|
||||
// ! to void which can cause link-time errors if the platform linker is aware
|
||||
// of return types and requires they match (for example: wasm does this).
|
||||
#[inline(always)]
|
||||
pub unsafe fn lua_error(L: *mut lua_State) -> ! {
|
||||
lua_error_(L);
|
||||
unreachable!();
|
||||
}
|
||||
|
||||
//
|
||||
// Some useful macros (implemented as Rust functions)
|
||||
//
|
||||
@@ -439,6 +457,14 @@ pub unsafe fn lua_pushglobaltable(L: *mut lua_State) -> c_int {
|
||||
lua_rawgeti(L, LUA_REGISTRYINDEX, LUA_RIDX_GLOBALS)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn lua_tolightuserdata(L: *mut lua_State, idx: c_int) -> *mut c_void {
|
||||
if lua_islightuserdata(L, idx) != 0 {
|
||||
return lua_touserdata(L, idx);
|
||||
}
|
||||
ptr::null_mut()
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn lua_tostring(L: *mut lua_State, i: c_int) -> *const c_char {
|
||||
lua_tolstring(L, i, ptr::null_mut())
|
||||
|
||||
@@ -52,6 +52,8 @@ extern "C-unwind" {
|
||||
pub fn luaL_newmetatable_(L: *mut lua_State, tname: *const c_char) -> c_int;
|
||||
pub fn luaL_checkudata(L: *mut lua_State, ud: c_int, tname: *const c_char) -> *mut c_void;
|
||||
|
||||
pub fn luaL_checkbuffer(L: *mut lua_State, narg: c_int, len: *mut usize) -> *mut c_void;
|
||||
|
||||
pub fn luaL_where(L: *mut lua_State, lvl: c_int);
|
||||
|
||||
#[link_name = "luaL_errorL"]
|
||||
@@ -152,5 +154,48 @@ pub unsafe fn luaL_sandbox(L: *mut lua_State, enabled: c_int) {
|
||||
}
|
||||
|
||||
//
|
||||
// TODO: Generic Buffer Manipulation
|
||||
// Generic Buffer Manipulation
|
||||
//
|
||||
|
||||
/// Buffer size used for on-stack string operations. This limit depends on native stack size.
|
||||
pub const LUA_BUFFERSIZE: usize = 512;
|
||||
|
||||
#[repr(C)]
|
||||
pub struct luaL_Strbuf {
|
||||
p: *mut c_char, // current position in buffer
|
||||
end: *mut c_char, // end of the current buffer
|
||||
L: *mut lua_State,
|
||||
storage: *mut c_void, // TString
|
||||
buffer: [c_char; LUA_BUFFERSIZE],
|
||||
}
|
||||
|
||||
// For compatibility
|
||||
pub type luaL_Buffer = luaL_Strbuf;
|
||||
|
||||
extern "C-unwind" {
|
||||
pub fn luaL_buffinit(L: *mut lua_State, B: *mut luaL_Strbuf);
|
||||
pub fn luaL_buffinitsize(L: *mut lua_State, B: *mut luaL_Strbuf, size: usize) -> *mut c_char;
|
||||
pub fn luaL_prepbuffsize(B: *mut luaL_Strbuf, size: usize) -> *mut c_char;
|
||||
pub fn luaL_addlstring(B: *mut luaL_Strbuf, s: *const c_char, l: usize);
|
||||
pub fn luaL_addvalue(B: *mut luaL_Strbuf);
|
||||
pub fn luaL_addvalueany(B: *mut luaL_Strbuf, idx: c_int);
|
||||
pub fn luaL_pushresult(B: *mut luaL_Strbuf);
|
||||
pub fn luaL_pushresultsize(B: *mut luaL_Strbuf, size: usize);
|
||||
}
|
||||
|
||||
pub unsafe fn luaL_addchar(B: *mut luaL_Strbuf, c: c_char) {
|
||||
if (*B).p >= (*B).end {
|
||||
luaL_prepbuffsize(B, 1);
|
||||
}
|
||||
*(*B).p = c;
|
||||
(*B).p = (*B).p.add(1);
|
||||
}
|
||||
|
||||
pub unsafe fn luaL_addstring(B: *mut luaL_Strbuf, s: *const c_char) {
|
||||
// Calculate length of s
|
||||
let mut len = 0;
|
||||
while *s.add(len) != 0 {
|
||||
len += 1;
|
||||
}
|
||||
luaL_addlstring(B, s, len);
|
||||
}
|
||||
|
||||
@@ -2,13 +2,19 @@
|
||||
|
||||
use std::marker::{PhantomData, PhantomPinned};
|
||||
use std::os::raw::{c_char, c_double, c_float, c_int, c_uint, c_void};
|
||||
use std::ptr;
|
||||
use std::{mem, ptr};
|
||||
|
||||
// Option for multiple returns in 'lua_pcall' and 'lua_call'
|
||||
pub const LUA_MULTRET: c_int = -1;
|
||||
|
||||
// Max number of Lua stack slots
|
||||
const LUAI_MAXCSTACK: c_int = 100000;
|
||||
const LUAI_MAXCSTACK: c_int = 1000000;
|
||||
|
||||
// Number of valid Lua userdata tags
|
||||
const LUA_UTAG_LIMIT: c_int = 128;
|
||||
|
||||
// Number of valid Lua lightuserdata tags
|
||||
const LUA_LUTAG_LIMIT: c_int = 128;
|
||||
|
||||
//
|
||||
// Pseudo-indices
|
||||
@@ -55,6 +61,7 @@ pub const LUA_TTABLE: c_int = 6;
|
||||
pub const LUA_TFUNCTION: c_int = 7;
|
||||
pub const LUA_TUSERDATA: c_int = 8;
|
||||
pub const LUA_TTHREAD: c_int = 9;
|
||||
pub const LUA_TBUFFER: c_int = 10;
|
||||
|
||||
/// Guaranteed number of Lua stack slots available to a C function.
|
||||
pub const LUA_MINSTACK: c_int = 20;
|
||||
@@ -143,10 +150,13 @@ extern "C-unwind" {
|
||||
pub fn lua_objlen(L: *mut lua_State, idx: c_int) -> usize;
|
||||
pub fn lua_tocfunction(L: *mut lua_State, idx: c_int) -> Option<lua_CFunction>;
|
||||
pub fn lua_tolightuserdata(L: *mut lua_State, idx: c_int) -> *mut c_void;
|
||||
pub fn lua_tolightuserdatatagged(L: *mut lua_State, idx: c_int, tag: c_int) -> *mut c_void;
|
||||
pub fn lua_touserdata(L: *mut lua_State, idx: c_int) -> *mut c_void;
|
||||
pub fn lua_touserdatatagged(L: *mut lua_State, idx: c_int, tag: c_int) -> *mut c_void;
|
||||
pub fn lua_userdatatag(L: *mut lua_State, idx: c_int) -> c_int;
|
||||
pub fn lua_lightuserdatatag(L: *mut lua_State, idx: c_int) -> c_int;
|
||||
pub fn lua_tothread(L: *mut lua_State, idx: c_int) -> *mut lua_State;
|
||||
pub fn lua_tobuffer(L: *mut lua_State, idx: c_int, len: *mut usize) -> *mut c_void;
|
||||
pub fn lua_topointer(L: *mut lua_State, idx: c_int) -> *const c_void;
|
||||
|
||||
//
|
||||
@@ -177,10 +187,12 @@ extern "C-unwind" {
|
||||
pub fn lua_pushboolean(L: *mut lua_State, b: c_int);
|
||||
pub fn lua_pushthread(L: *mut lua_State) -> c_int;
|
||||
|
||||
pub fn lua_pushlightuserdata(L: *mut lua_State, p: *mut c_void);
|
||||
pub fn lua_pushlightuserdatatagged(L: *mut lua_State, p: *mut c_void, tag: c_int);
|
||||
pub fn lua_newuserdatatagged(L: *mut lua_State, sz: usize, tag: c_int) -> *mut c_void;
|
||||
pub fn lua_newuserdatadtor(L: *mut lua_State, sz: usize, dtor: lua_Udestructor) -> *mut c_void;
|
||||
|
||||
pub fn lua_newbuffer(L: *mut lua_State, sz: usize) -> *mut c_void;
|
||||
|
||||
//
|
||||
// Get functions (Lua -> stack)
|
||||
//
|
||||
@@ -276,8 +288,11 @@ extern "C-unwind" {
|
||||
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<lua_Destructor>);
|
||||
pub fn lua_getuserdatadtor(L: *mut lua_State, tag: c_int) -> Option<lua_Destructor>;
|
||||
pub fn lua_setlightuserdataname(L: *mut lua_State, tag: c_int, name: *const c_char);
|
||||
pub fn lua_getlightuserdataname(L: *mut lua_State, tag: c_int) -> *const c_char;
|
||||
pub fn lua_clonefunction(L: *mut lua_State, idx: c_int);
|
||||
pub fn lua_cleartable(L: *mut lua_State, idx: c_int);
|
||||
pub fn lua_getallocf(L: *mut lua_State, ud: *mut *mut c_void) -> lua_Alloc;
|
||||
}
|
||||
|
||||
//
|
||||
@@ -325,6 +340,15 @@ pub unsafe fn lua_newuserdata(L: *mut lua_State, sz: usize) -> *mut c_void {
|
||||
lua_newuserdatatagged(L, sz, 0)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn lua_newuserdata_t<T>(L: *mut lua_State) -> *mut T {
|
||||
unsafe extern "C-unwind" fn destructor<T>(ud: *mut c_void) {
|
||||
ptr::drop_in_place(ud as *mut T);
|
||||
}
|
||||
|
||||
lua_newuserdatadtor(L, mem::size_of::<T>(), destructor::<T>) as *mut T
|
||||
}
|
||||
|
||||
// TODO: lua_strlen
|
||||
|
||||
#[inline(always)]
|
||||
@@ -362,6 +386,11 @@ pub unsafe fn lua_isthread(L: *mut lua_State, n: c_int) -> c_int {
|
||||
(lua_type(L, n) == LUA_TTHREAD) as c_int
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn lua_isbuffer(L: *mut lua_State, n: c_int) -> c_int {
|
||||
(lua_type(L, n) == LUA_TBUFFER) as c_int
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn lua_isnone(L: *mut lua_State, n: c_int) -> c_int {
|
||||
(lua_type(L, n) == LUA_TNONE) as c_int
|
||||
@@ -379,18 +408,22 @@ pub unsafe fn lua_pushliteral(L: *mut lua_State, s: &'static str) {
|
||||
lua_pushlstring_(L, c_str.as_ptr(), c_str.as_bytes().len())
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn lua_pushcfunction(L: *mut lua_State, f: lua_CFunction) {
|
||||
lua_pushcclosurek(L, f, ptr::null(), 0, None)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn lua_pushcfunctiond(L: *mut lua_State, f: lua_CFunction, debugname: *const c_char) {
|
||||
lua_pushcclosurek(L, f, debugname, 0, None)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn lua_pushcclosure(L: *mut lua_State, f: lua_CFunction, nup: c_int) {
|
||||
lua_pushcclosurek(L, f, ptr::null(), nup, None)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn lua_pushcclosured(
|
||||
L: *mut lua_State,
|
||||
f: lua_CFunction,
|
||||
@@ -400,6 +433,11 @@ pub unsafe fn lua_pushcclosured(
|
||||
lua_pushcclosurek(L, f, debugname, nup, None)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn lua_pushlightuserdata(L: *mut lua_State, p: *mut c_void) {
|
||||
lua_pushlightuserdatatagged(L, p, 0)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn lua_setglobal(L: *mut lua_State, var: *const c_char) {
|
||||
lua_setfield(L, LUA_GLOBALSINDEX, var)
|
||||
|
||||
@@ -9,6 +9,7 @@ pub const LUA_TABLIBNAME: &str = "table";
|
||||
pub const LUA_OSLIBNAME: &str = "os";
|
||||
pub const LUA_STRLIBNAME: &str = "string";
|
||||
pub const LUA_BITLIBNAME: &str = "bit32";
|
||||
pub const LUA_BUFFERLIBNAME: &str = "buffer";
|
||||
pub const LUA_UTF8LIBNAME: &str = "utf8";
|
||||
pub const LUA_MATHLIBNAME: &str = "math";
|
||||
pub const LUA_DBLIBNAME: &str = "debug";
|
||||
@@ -20,6 +21,7 @@ extern "C-unwind" {
|
||||
pub fn luaopen_os(L: *mut lua_State) -> c_int;
|
||||
pub fn luaopen_string(L: *mut lua_State) -> c_int;
|
||||
pub fn luaopen_bit32(L: *mut lua_State) -> c_int;
|
||||
pub fn luaopen_buffer(L: *mut lua_State) -> c_int;
|
||||
pub fn luaopen_utf8(L: *mut lua_State) -> c_int;
|
||||
pub fn luaopen_math(L: *mut lua_State) -> c_int;
|
||||
pub fn luaopen_debug(L: *mut lua_State) -> c_int;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "mlua_derive"
|
||||
version = "0.9.0"
|
||||
version = "0.9.2"
|
||||
authors = ["Aleksandr Orlenko <zxteam@pm.me>"]
|
||||
edition = "2021"
|
||||
description = "Procedural macros for the mlua crate."
|
||||
@@ -19,6 +19,6 @@ quote = "1.0"
|
||||
proc-macro2 = { version = "1.0", features = ["span-locations"] }
|
||||
proc-macro-error = { version = "1.0", optional = true }
|
||||
syn = { version = "2.0", features = ["full"] }
|
||||
itertools = { version = "0.11", optional = true }
|
||||
itertools = { version = "0.12", optional = true }
|
||||
regex = { version = "1.4", optional = true }
|
||||
once_cell = { version = "1.0", optional = true }
|
||||
|
||||
@@ -7,16 +7,17 @@ pub fn from_lua(input: TokenStream) -> TokenStream {
|
||||
ident, generics, ..
|
||||
} = parse_macro_input!(input as DeriveInput);
|
||||
|
||||
let ident_str = ident.to_string();
|
||||
let (impl_generics, ty_generics, _) = generics.split_for_impl();
|
||||
let where_clause = match &generics.where_clause {
|
||||
Some(where_clause) => quote! { #where_clause, Self: 'static + Clone },
|
||||
None => quote! { where Self: 'static + Clone },
|
||||
};
|
||||
let ident_str = ident.to_string();
|
||||
|
||||
quote! {
|
||||
impl #generics ::mlua::FromLua<'_> for #ident #generics #where_clause {
|
||||
impl #impl_generics ::mlua::FromLua<'_> for #ident #ty_generics #where_clause {
|
||||
#[inline]
|
||||
fn from_lua(value: ::mlua::Value<'_>, lua: &'_ ::mlua::Lua) -> ::mlua::Result<Self> {
|
||||
fn from_lua(value: ::mlua::Value<'_>, _: &'_ ::mlua::Lua) -> ::mlua::Result<Self> {
|
||||
match value {
|
||||
::mlua::Value::UserData(ud) => Ok(ud.borrow::<Self>()?.clone()),
|
||||
_ => Err(::mlua::Error::FromLuaConversionError {
|
||||
|
||||
@@ -51,7 +51,11 @@ pub fn lua_module(attr: TokenStream, item: TokenStream) -> TokenStream {
|
||||
let func_name = &func.sig.ident;
|
||||
let module_name = args.name.unwrap_or_else(|| func_name.clone());
|
||||
let ext_entrypoint_name = Ident::new(&format!("luaopen_{module_name}"), Span::call_site());
|
||||
let skip_memory_check = args.skip_memory_check;
|
||||
let skip_memory_check = if args.skip_memory_check {
|
||||
quote! { lua.skip_memory_check(true); }
|
||||
} else {
|
||||
quote! {}
|
||||
};
|
||||
|
||||
let wrapped = quote! {
|
||||
::mlua::require_module_feature!();
|
||||
@@ -61,8 +65,8 @@ pub fn lua_module(attr: TokenStream, item: TokenStream) -> TokenStream {
|
||||
#[no_mangle]
|
||||
unsafe extern "C-unwind" fn #ext_entrypoint_name(state: *mut ::mlua::lua_State) -> ::std::os::raw::c_int {
|
||||
let lua = ::mlua::Lua::init_from_ptr(state);
|
||||
lua.skip_memory_check(#skip_memory_check);
|
||||
lua.entrypoint1(#func_name)
|
||||
#skip_memory_check
|
||||
lua.entrypoint1(state, #func_name)
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
+195
-3
@@ -16,12 +16,15 @@ use crate::lua::Lua;
|
||||
use crate::string::String;
|
||||
use crate::table::Table;
|
||||
use crate::thread::Thread;
|
||||
use crate::types::{LightUserData, MaybeSend};
|
||||
use crate::types::{LightUserData, MaybeSend, RegistryKey};
|
||||
use crate::userdata::{AnyUserData, UserData, UserDataRef, UserDataRefMut};
|
||||
use crate::value::{FromLua, IntoLua, Nil, Value};
|
||||
|
||||
#[cfg(all(feature = "unstable", any(not(feature = "send"), doc)))]
|
||||
use crate::{function::OwnedFunction, table::OwnedTable, userdata::OwnedAnyUserData};
|
||||
use crate::{
|
||||
function::OwnedFunction, string::OwnedString, table::OwnedTable, thread::OwnedThread,
|
||||
userdata::OwnedAnyUserData,
|
||||
};
|
||||
|
||||
impl<'lua> IntoLua<'lua> for Value<'lua> {
|
||||
#[inline]
|
||||
@@ -44,6 +47,18 @@ impl<'lua> IntoLua<'lua> for String<'lua> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> IntoLua<'lua> for &String<'lua> {
|
||||
#[inline]
|
||||
fn into_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
|
||||
Ok(Value::String(self.clone()))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn push_into_stack(self, lua: &'lua Lua) -> Result<()> {
|
||||
Ok(lua.push_ref(&self.0))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> FromLua<'lua> for String<'lua> {
|
||||
#[inline]
|
||||
fn from_lua(value: Value<'lua>, lua: &'lua Lua) -> Result<String<'lua>> {
|
||||
@@ -57,6 +72,38 @@ impl<'lua> FromLua<'lua> for String<'lua> {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "unstable", any(not(feature = "send"), doc)))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all(feature = "unstable", not(feature = "send")))))]
|
||||
impl<'lua> IntoLua<'lua> for OwnedString {
|
||||
#[inline]
|
||||
fn into_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
|
||||
Ok(Value::String(String(lua.adopt_owned_ref(self.0))))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "unstable", any(not(feature = "send"), doc)))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all(feature = "unstable", not(feature = "send")))))]
|
||||
impl<'lua> IntoLua<'lua> for &OwnedString {
|
||||
#[inline]
|
||||
fn into_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
|
||||
OwnedString::into_lua(self.clone(), lua)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn push_into_stack(self, lua: &'lua Lua) -> Result<()> {
|
||||
Ok(lua.push_owned_ref(&self.0))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "unstable", any(not(feature = "send"), doc)))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all(feature = "unstable", not(feature = "send")))))]
|
||||
impl<'lua> FromLua<'lua> for OwnedString {
|
||||
#[inline]
|
||||
fn from_lua(value: Value<'lua>, lua: &'lua Lua) -> Result<OwnedString> {
|
||||
String::from_lua(value, lua).map(|s| s.into_owned())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> IntoLua<'lua> for Table<'lua> {
|
||||
#[inline]
|
||||
fn into_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
|
||||
@@ -64,6 +111,18 @@ impl<'lua> IntoLua<'lua> for Table<'lua> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> IntoLua<'lua> for &Table<'lua> {
|
||||
#[inline]
|
||||
fn into_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
|
||||
Ok(Value::Table(self.clone()))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn push_into_stack(self, lua: &'lua Lua) -> Result<()> {
|
||||
Ok(lua.push_ref(&self.0))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> FromLua<'lua> for Table<'lua> {
|
||||
#[inline]
|
||||
fn from_lua(value: Value<'lua>, _: &'lua Lua) -> Result<Table<'lua>> {
|
||||
@@ -87,6 +146,20 @@ impl<'lua> IntoLua<'lua> for OwnedTable {
|
||||
}
|
||||
}
|
||||
|
||||
#[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>> {
|
||||
OwnedTable::into_lua(self.clone(), lua)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn push_into_stack(self, lua: &'lua Lua) -> Result<()> {
|
||||
Ok(lua.push_owned_ref(&self.0))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "unstable", any(not(feature = "send"), doc)))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all(feature = "unstable", not(feature = "send")))))]
|
||||
impl<'lua> FromLua<'lua> for OwnedTable {
|
||||
@@ -103,6 +176,18 @@ impl<'lua> IntoLua<'lua> for Function<'lua> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> IntoLua<'lua> for &Function<'lua> {
|
||||
#[inline]
|
||||
fn into_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
|
||||
Ok(Value::Function(self.clone()))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn push_into_stack(self, lua: &'lua Lua) -> Result<()> {
|
||||
Ok(lua.push_ref(&self.0))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> FromLua<'lua> for Function<'lua> {
|
||||
#[inline]
|
||||
fn from_lua(value: Value<'lua>, _: &'lua Lua) -> Result<Function<'lua>> {
|
||||
@@ -126,6 +211,20 @@ impl<'lua> IntoLua<'lua> for OwnedFunction {
|
||||
}
|
||||
}
|
||||
|
||||
#[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>> {
|
||||
OwnedFunction::into_lua(self.clone(), lua)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn push_into_stack(self, lua: &'lua Lua) -> Result<()> {
|
||||
Ok(lua.push_owned_ref(&self.0))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "unstable", any(not(feature = "send"), doc)))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all(feature = "unstable", not(feature = "send")))))]
|
||||
impl<'lua> FromLua<'lua> for OwnedFunction {
|
||||
@@ -142,6 +241,18 @@ impl<'lua> IntoLua<'lua> for Thread<'lua> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> IntoLua<'lua> for &Thread<'lua> {
|
||||
#[inline]
|
||||
fn into_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
|
||||
Ok(Value::Thread(self.clone()))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn push_into_stack(self, lua: &'lua Lua) -> Result<()> {
|
||||
Ok(lua.push_ref(&self.0))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> FromLua<'lua> for Thread<'lua> {
|
||||
#[inline]
|
||||
fn from_lua(value: Value<'lua>, _: &'lua Lua) -> Result<Thread<'lua>> {
|
||||
@@ -156,6 +267,38 @@ impl<'lua> FromLua<'lua> for Thread<'lua> {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "unstable", any(not(feature = "send"), doc)))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all(feature = "unstable", not(feature = "send")))))]
|
||||
impl<'lua> IntoLua<'lua> for OwnedThread {
|
||||
#[inline]
|
||||
fn into_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
|
||||
Ok(Value::Thread(Thread(lua.adopt_owned_ref(self.0), self.1)))
|
||||
}
|
||||
}
|
||||
|
||||
#[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 &OwnedThread {
|
||||
#[inline]
|
||||
fn into_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
|
||||
OwnedThread::into_lua(self.clone(), lua)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn push_into_stack(self, lua: &'lua Lua) -> Result<()> {
|
||||
Ok(lua.push_owned_ref(&self.0))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "unstable", any(not(feature = "send"), doc)))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all(feature = "unstable", not(feature = "send")))))]
|
||||
impl<'lua> FromLua<'lua> for OwnedThread {
|
||||
#[inline]
|
||||
fn from_lua(value: Value<'lua>, lua: &'lua Lua) -> Result<OwnedThread> {
|
||||
Thread::from_lua(value, lua).map(|s| s.into_owned())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> IntoLua<'lua> for AnyUserData<'lua> {
|
||||
#[inline]
|
||||
fn into_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
|
||||
@@ -163,6 +306,18 @@ impl<'lua> IntoLua<'lua> for AnyUserData<'lua> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> IntoLua<'lua> for &AnyUserData<'lua> {
|
||||
#[inline]
|
||||
fn into_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
|
||||
Ok(Value::UserData(self.clone()))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn push_into_stack(self, lua: &'lua Lua) -> Result<()> {
|
||||
Ok(lua.push_ref(&self.0))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> FromLua<'lua> for AnyUserData<'lua> {
|
||||
#[inline]
|
||||
fn from_lua(value: Value<'lua>, _: &'lua Lua) -> Result<AnyUserData<'lua>> {
|
||||
@@ -182,7 +337,24 @@ impl<'lua> FromLua<'lua> for AnyUserData<'lua> {
|
||||
impl<'lua> IntoLua<'lua> for OwnedAnyUserData {
|
||||
#[inline]
|
||||
fn into_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
|
||||
Ok(Value::UserData(AnyUserData(lua.adopt_owned_ref(self.0))))
|
||||
Ok(Value::UserData(AnyUserData(
|
||||
lua.adopt_owned_ref(self.0),
|
||||
self.1,
|
||||
)))
|
||||
}
|
||||
}
|
||||
|
||||
#[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>> {
|
||||
OwnedAnyUserData::into_lua(self.clone(), lua)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn push_into_stack(self, lua: &'lua Lua) -> Result<()> {
|
||||
Ok(lua.push_owned_ref(&self.0))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -237,6 +409,26 @@ impl<'lua> FromLua<'lua> for Error {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> IntoLua<'lua> for &RegistryKey {
|
||||
#[inline]
|
||||
fn into_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
|
||||
lua.registry_value(self)
|
||||
}
|
||||
|
||||
unsafe fn push_into_stack(self, lua: &'lua Lua) -> Result<()> {
|
||||
if !lua.owns_registry_value(self) {
|
||||
return Err(Error::MismatchedRegistryKey);
|
||||
}
|
||||
|
||||
if self.is_nil() {
|
||||
ffi::lua_pushnil(lua.state());
|
||||
} else {
|
||||
ffi::lua_rawgeti(lua.state(), ffi::LUA_REGISTRYINDEX, self.registry_id as _);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> IntoLua<'lua> for bool {
|
||||
#[inline]
|
||||
fn into_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
|
||||
|
||||
+3
-4
@@ -6,12 +6,11 @@ use std::slice;
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
use crate::lua::Lua;
|
||||
use crate::memory::MemoryState;
|
||||
use crate::table::Table;
|
||||
use crate::types::{Callback, LuaRef, MaybeSend};
|
||||
use crate::util::{
|
||||
assert_stack, check_stack, error_traceback, linenumber_to_usize, pop_error, ptr_to_lossy_str,
|
||||
ptr_to_str, StackGuard,
|
||||
assert_stack, check_stack, linenumber_to_usize, pop_error, ptr_to_lossy_str, ptr_to_str,
|
||||
StackGuard,
|
||||
};
|
||||
use crate::value::{FromLuaMulti, IntoLua, IntoLuaMulti, Value};
|
||||
|
||||
@@ -131,7 +130,7 @@ impl<'lua> Function<'lua> {
|
||||
check_stack(state, 2)?;
|
||||
|
||||
// Push error handler
|
||||
MemoryState::relax_limit_with(state, || ffi::lua_pushcfunction(state, error_traceback));
|
||||
lua.push_error_traceback();
|
||||
let stack_start = ffi::lua_gettop(state);
|
||||
// Push function and the arguments
|
||||
lua.push_ref(&self.0);
|
||||
|
||||
+4
-2
@@ -353,8 +353,10 @@ impl HookTriggers {
|
||||
// Returns the `count` parameter to pass to `lua_sethook`, if applicable. Otherwise, zero is
|
||||
// returned.
|
||||
pub(crate) const fn count(&self) -> c_int {
|
||||
let Some(n) = self.every_nth_instruction else { return 0 };
|
||||
n as c_int
|
||||
match self.every_nth_instruction {
|
||||
Some(n) => n as c_int,
|
||||
None => 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+160
-109
@@ -3,15 +3,14 @@ use std::cell::{RefCell, UnsafeCell};
|
||||
use std::ffi::{CStr, CString};
|
||||
use std::fmt;
|
||||
use std::marker::PhantomData;
|
||||
use std::mem::MaybeUninit;
|
||||
use std::mem::{self, MaybeUninit};
|
||||
use std::ops::Deref;
|
||||
use std::os::raw::{c_char, c_int, c_void};
|
||||
use std::panic::{catch_unwind, resume_unwind, AssertUnwindSafe, Location};
|
||||
use std::ptr::NonNull;
|
||||
use std::ptr;
|
||||
use std::result::Result as StdResult;
|
||||
use std::sync::atomic::{AtomicPtr, Ordering};
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::{mem, ptr, str};
|
||||
|
||||
use rustc_hash::FxHashMap;
|
||||
|
||||
@@ -27,15 +26,15 @@ use crate::table::Table;
|
||||
use crate::thread::Thread;
|
||||
use crate::types::{
|
||||
AppData, AppDataRef, AppDataRefMut, Callback, CallbackUpvalue, DestructedUserdata, Integer,
|
||||
LightUserData, LuaRef, MaybeSend, Number, RegistryKey,
|
||||
LightUserData, LuaRef, MaybeSend, Number, RegistryKey, SubtypeId,
|
||||
};
|
||||
use crate::userdata::{AnyUserData, MetaMethod, UserData, UserDataCell};
|
||||
use crate::userdata_impl::{UserDataProxy, UserDataRegistry};
|
||||
use crate::util::{
|
||||
self, assert_stack, check_stack, get_destructed_userdata_metatable, get_gc_metatable,
|
||||
get_gc_userdata, get_main_state, get_userdata, init_error_registry, init_gc_metatable,
|
||||
init_userdata_metatable, pop_error, push_gc_userdata, push_string, push_table, rawset_field,
|
||||
safe_pcall, safe_xpcall, short_type_name, StackGuard, WrappedFailure,
|
||||
self, assert_stack, check_stack, error_traceback, get_destructed_userdata_metatable,
|
||||
get_gc_metatable, get_gc_userdata, get_main_state, get_userdata, init_error_registry,
|
||||
init_gc_metatable, init_userdata_metatable, pop_error, push_gc_userdata, push_string,
|
||||
push_table, rawset_field, safe_pcall, safe_xpcall, short_type_name, StackGuard, WrappedFailure,
|
||||
};
|
||||
use crate::value::{FromLua, FromLuaMulti, IntoLua, IntoLuaMulti, MultiValue, Nil, Value};
|
||||
|
||||
@@ -60,6 +59,7 @@ use {
|
||||
crate::types::{AsyncCallback, AsyncCallbackUpvalue, AsyncPollUpvalue},
|
||||
futures_util::future::{self, Future},
|
||||
futures_util::task::{noop_waker_ref, Context, Poll, Waker},
|
||||
std::ptr::NonNull,
|
||||
};
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
@@ -94,7 +94,6 @@ pub(crate) struct ExtraData {
|
||||
|
||||
safe: bool,
|
||||
libs: StdLib,
|
||||
mem_state: Option<NonNull<MemoryState>>,
|
||||
#[cfg(feature = "module")]
|
||||
skip_memory_check: bool,
|
||||
|
||||
@@ -244,11 +243,14 @@ impl Drop for Lua {
|
||||
impl Drop for LuaInner {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
#[cfg(feature = "luau")]
|
||||
{
|
||||
(*ffi::lua_callbacks(self.state())).userdata = ptr::null_mut();
|
||||
}
|
||||
let mem_state = MemoryState::get(self.main_state);
|
||||
|
||||
ffi::lua_close(self.main_state);
|
||||
|
||||
// Deallocate MemoryState
|
||||
if !mem_state.is_null() {
|
||||
drop(Box::from_raw(mem_state));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -261,9 +263,6 @@ impl Drop for ExtraData {
|
||||
}
|
||||
|
||||
*mlua_expect!(self.registry_unref_list.lock(), "unref list poisoned") = None;
|
||||
if let Some(mem_state) = self.mem_state {
|
||||
drop(unsafe { Box::from_raw(mem_state.as_ptr()) });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -341,7 +340,6 @@ impl Lua {
|
||||
|
||||
let lua = unsafe { Self::inner_new(libs, options) };
|
||||
|
||||
#[cfg(not(feature = "luau"))]
|
||||
if libs.contains(StdLib::PACKAGE) {
|
||||
mlua_expect!(lua.disable_c_modules(), "Error during disabling C modules");
|
||||
}
|
||||
@@ -359,23 +357,22 @@ impl Lua {
|
||||
///
|
||||
/// [`StdLib`]: crate::StdLib
|
||||
pub unsafe fn unsafe_new_with(libs: StdLib, options: LuaOptions) -> Lua {
|
||||
// Workaround to avoid stripping a few unused Lua symbols that could be imported
|
||||
// by C modules in unsafe mode
|
||||
let mut _symbols: Vec<*const extern "C-unwind" fn()> =
|
||||
vec![ffi::lua_isuserdata as _, ffi::lua_tocfunction as _];
|
||||
|
||||
#[cfg(not(feature = "luau"))]
|
||||
_symbols.extend_from_slice(&[
|
||||
ffi::lua_atpanic as _,
|
||||
ffi::luaL_loadstring as _,
|
||||
ffi::luaL_openlibs as _,
|
||||
]);
|
||||
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
|
||||
{
|
||||
// Workaround to avoid stripping a few unused Lua symbols that could be imported
|
||||
// by C modules in unsafe mode
|
||||
let mut _symbols: Vec<*const extern "C-unwind" fn()> = vec![
|
||||
ffi::lua_atpanic as _,
|
||||
ffi::lua_isuserdata as _,
|
||||
ffi::lua_tocfunction as _,
|
||||
ffi::luaL_loadstring as _,
|
||||
ffi::luaL_openlibs as _,
|
||||
];
|
||||
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
|
||||
{
|
||||
_symbols.push(ffi::lua_getglobal as _);
|
||||
_symbols.push(ffi::lua_setglobal as _);
|
||||
_symbols.push(ffi::luaL_setfuncs as _);
|
||||
}
|
||||
_symbols.push(ffi::lua_getglobal as _);
|
||||
_symbols.push(ffi::lua_setglobal as _);
|
||||
_symbols.push(ffi::luaL_setfuncs as _);
|
||||
}
|
||||
|
||||
Self::inner_new(libs, options)
|
||||
@@ -383,12 +380,11 @@ impl Lua {
|
||||
|
||||
/// Creates a new Lua state with required `libs` and `options`
|
||||
unsafe fn inner_new(libs: StdLib, options: LuaOptions) -> Lua {
|
||||
let mut mem_state: *mut MemoryState = Box::into_raw(Box::default());
|
||||
let mem_state: *mut MemoryState = Box::into_raw(Box::default());
|
||||
let mut state = ffi::lua_newstate(ALLOCATOR, mem_state as *mut c_void);
|
||||
// If state is null then switch to Lua internal allocator
|
||||
if state.is_null() {
|
||||
drop(Box::from_raw(mem_state));
|
||||
mem_state = ptr::null_mut();
|
||||
state = ffi::luaL_newstate();
|
||||
}
|
||||
assert!(!state.is_null(), "Failed to instantiate Lua VM");
|
||||
@@ -404,7 +400,6 @@ impl Lua {
|
||||
|
||||
let lua = Lua::init_from_ptr(state);
|
||||
let extra = lua.extra.get();
|
||||
(*extra).mem_state = NonNull::new(mem_state);
|
||||
|
||||
mlua_expect!(
|
||||
load_from_std_lib(state, libs),
|
||||
@@ -440,7 +435,7 @@ impl Lua {
|
||||
}
|
||||
|
||||
#[cfg(feature = "luau")]
|
||||
mlua_expect!(lua.prepare_luau_state(), "Error preparing Luau state");
|
||||
mlua_expect!(lua.configure_luau(), "Error configuring Luau");
|
||||
|
||||
lua
|
||||
}
|
||||
@@ -504,6 +499,13 @@ impl Lua {
|
||||
ptr
|
||||
};
|
||||
|
||||
// Store `error_traceback` function on the ref stack
|
||||
#[cfg(any(feature = "lua51", feature = "luajit", feature = "luau"))]
|
||||
{
|
||||
ffi::lua_pushcfunction(ref_thread, error_traceback);
|
||||
assert_eq!(ffi::lua_gettop(ref_thread), ExtraData::ERROR_TRACEBACK_IDX);
|
||||
}
|
||||
|
||||
// Create ExtraData
|
||||
let extra = Arc::new(UnsafeCell::new(ExtraData {
|
||||
inner: MaybeUninit::uninit(),
|
||||
@@ -514,7 +516,6 @@ impl Lua {
|
||||
app_data: AppData::default(),
|
||||
safe: false,
|
||||
libs: StdLib::NONE,
|
||||
mem_state: None,
|
||||
#[cfg(feature = "module")]
|
||||
skip_memory_check: false,
|
||||
ref_thread,
|
||||
@@ -547,14 +548,8 @@ impl Lua {
|
||||
|
||||
// Store it in the registry
|
||||
mlua_expect!(
|
||||
(|state| {
|
||||
push_gc_userdata(state, Arc::clone(&extra), true)?;
|
||||
protect_lua!(state, 1, 0, fn(state) {
|
||||
let extra_key = &EXTRA_REGISTRY_KEY as *const u8 as *const c_void;
|
||||
ffi::lua_rawsetp(state, ffi::LUA_REGISTRYINDEX, extra_key);
|
||||
})
|
||||
})(main_state),
|
||||
"Error while storing extra data",
|
||||
set_extra_data(main_state, &extra),
|
||||
"Error while storing extra data"
|
||||
);
|
||||
|
||||
// Register `DestructedUserdata` type
|
||||
@@ -572,13 +567,6 @@ impl Lua {
|
||||
);
|
||||
assert_stack(main_state, ffi::LUA_MINSTACK);
|
||||
|
||||
// Set Luau callbacks userdata to extra data
|
||||
// We can use global callbacks userdata since we don't allow C modules in Luau
|
||||
#[cfg(feature = "luau")]
|
||||
{
|
||||
(*ffi::lua_callbacks(main_state)).userdata = extra.get() as *mut c_void;
|
||||
}
|
||||
|
||||
let inner = Arc::new(LuaInner {
|
||||
state: AtomicPtr::new(state),
|
||||
main_state,
|
||||
@@ -598,7 +586,6 @@ impl Lua {
|
||||
///
|
||||
/// [`StdLib`]: crate::StdLib
|
||||
pub fn load_from_std_lib(&self, libs: StdLib) -> Result<()> {
|
||||
#[cfg(not(feature = "luau"))]
|
||||
let is_safe = unsafe { (*self.extra.get()).safe };
|
||||
|
||||
#[cfg(not(feature = "luau"))]
|
||||
@@ -617,12 +604,9 @@ impl Lua {
|
||||
let res = unsafe { load_from_std_lib(self.main_state, libs) };
|
||||
|
||||
// If `package` library loaded into a safe lua state then disable C modules
|
||||
#[cfg(not(feature = "luau"))]
|
||||
{
|
||||
let curr_libs = unsafe { (*self.extra.get()).libs };
|
||||
if is_safe && (curr_libs ^ (curr_libs | libs)).contains(StdLib::PACKAGE) {
|
||||
mlua_expect!(self.disable_c_modules(), "Error during disabling C modules");
|
||||
}
|
||||
let curr_libs = unsafe { (*self.extra.get()).libs };
|
||||
if is_safe && (curr_libs ^ (curr_libs | libs)).contains(StdLib::PACKAGE) {
|
||||
mlua_expect!(self.disable_c_modules(), "Error during disabling C modules");
|
||||
}
|
||||
unsafe { (*self.extra.get()).libs |= libs };
|
||||
|
||||
@@ -724,18 +708,20 @@ impl Lua {
|
||||
// The returned value then pushed onto the stack.
|
||||
#[doc(hidden)]
|
||||
#[cfg(not(tarpaulin_include))]
|
||||
pub unsafe fn entrypoint<'lua, A, R, F>(self, func: F) -> c_int
|
||||
pub unsafe fn entrypoint<'lua, A, R, F>(self, state: *mut ffi::lua_State, func: F) -> c_int
|
||||
where
|
||||
A: FromLuaMulti<'lua>,
|
||||
R: IntoLua<'lua>,
|
||||
F: Fn(&'lua Lua, A) -> Result<R> + MaybeSend + 'static,
|
||||
{
|
||||
let (state, extra) = (self.state(), self.extra.get());
|
||||
// It must be safe to drop `self` as in the module mode we keep strong reference to `Lua` in the registry
|
||||
let extra = self.extra.get();
|
||||
// `self` is no longer needed and must be dropped at this point to avoid possible memory leak
|
||||
// in case of possible longjmp (lua_error) below
|
||||
drop(self);
|
||||
|
||||
callback_error_ext(state, extra, move |nargs| {
|
||||
let lua: &Lua = mem::transmute((*extra).inner.assume_init_ref());
|
||||
let _guard = StateGuard::new(&lua.0, state);
|
||||
let args = A::from_stack_args(nargs, 1, None, lua)?;
|
||||
func(lua, args)?.push_into_stack(lua)?;
|
||||
Ok(1)
|
||||
@@ -745,12 +731,12 @@ impl Lua {
|
||||
// A simple module entrypoint without arguments
|
||||
#[doc(hidden)]
|
||||
#[cfg(not(tarpaulin_include))]
|
||||
pub unsafe fn entrypoint1<'lua, R, F>(self, func: F) -> c_int
|
||||
pub unsafe fn entrypoint1<'lua, R, F>(self, state: *mut ffi::lua_State, func: F) -> c_int
|
||||
where
|
||||
R: IntoLua<'lua>,
|
||||
F: Fn(&'lua Lua) -> Result<R> + MaybeSend + 'static,
|
||||
{
|
||||
self.entrypoint(move |lua, _: ()| func(lua))
|
||||
self.entrypoint(state, move |lua, _: ()| func(lua))
|
||||
}
|
||||
|
||||
/// Skips memory checks for some operations.
|
||||
@@ -1098,9 +1084,9 @@ impl Lua {
|
||||
/// Returns the amount of memory (in bytes) currently used inside this Lua state.
|
||||
pub fn used_memory(&self) -> usize {
|
||||
unsafe {
|
||||
match (*self.extra.get()).mem_state.map(|x| x.as_ref()) {
|
||||
Some(mem_state) => mem_state.used_memory(),
|
||||
None => {
|
||||
match MemoryState::get(self.main_state) {
|
||||
mem_state if !mem_state.is_null() => (*mem_state).used_memory(),
|
||||
_ => {
|
||||
// Get data from the Lua GC
|
||||
let used_kbytes = ffi::lua_gc(self.main_state, ffi::LUA_GCCOUNT, 0);
|
||||
let used_kbytes_rem = ffi::lua_gc(self.main_state, ffi::LUA_GCCOUNTB, 0);
|
||||
@@ -1119,9 +1105,9 @@ impl Lua {
|
||||
/// Does not work in module mode where Lua state is managed externally.
|
||||
pub fn set_memory_limit(&self, limit: usize) -> Result<usize> {
|
||||
unsafe {
|
||||
match (*self.extra.get()).mem_state.map(|mut x| x.as_mut()) {
|
||||
Some(mem_state) => Ok(mem_state.set_memory_limit(limit)),
|
||||
None => Err(Error::MemoryLimitNotAvailable),
|
||||
match MemoryState::get(self.main_state) {
|
||||
mem_state if !mem_state.is_null() => Ok((*mem_state).set_memory_limit(limit)),
|
||||
_ => Err(Error::MemoryLimitNotAvailable),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2435,17 +2421,22 @@ impl Lua {
|
||||
ffi::lua_pop(state, 1);
|
||||
Nil
|
||||
}
|
||||
_ => Value::UserData(AnyUserData(self.pop_ref())),
|
||||
_ => Value::UserData(AnyUserData(self.pop_ref(), SubtypeId::None)),
|
||||
}
|
||||
}
|
||||
|
||||
ffi::LUA_TTHREAD => Value::Thread(Thread::new(self.pop_ref())),
|
||||
|
||||
#[cfg(feature = "luau")]
|
||||
ffi::LUA_TBUFFER => {
|
||||
// Buffer is represented as a userdata type
|
||||
Value::UserData(AnyUserData(self.pop_ref(), SubtypeId::Buffer))
|
||||
}
|
||||
|
||||
#[cfg(feature = "luajit")]
|
||||
ffi::LUA_TCDATA => {
|
||||
ffi::lua_pop(state, 1);
|
||||
// TODO: Fix this in a next major release
|
||||
panic!("cdata objects cannot be handled by mlua yet");
|
||||
// CDATA is represented as a userdata type
|
||||
Value::UserData(AnyUserData(self.pop_ref(), SubtypeId::CData))
|
||||
}
|
||||
|
||||
_ => mlua_panic!("LUA_TNONE in pop_value"),
|
||||
@@ -2530,7 +2521,7 @@ impl Lua {
|
||||
}
|
||||
_ => {
|
||||
ffi::lua_xpush(state, self.ref_thread(), idx);
|
||||
Value::UserData(AnyUserData(self.pop_ref_thread()))
|
||||
Value::UserData(AnyUserData(self.pop_ref_thread(), SubtypeId::None))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2540,10 +2531,18 @@ impl Lua {
|
||||
Value::Thread(Thread::new(self.pop_ref_thread()))
|
||||
}
|
||||
|
||||
#[cfg(feature = "luau")]
|
||||
ffi::LUA_TBUFFER => {
|
||||
// Buffer is represented as a userdata type
|
||||
ffi::lua_xpush(state, self.ref_thread(), idx);
|
||||
Value::UserData(AnyUserData(self.pop_ref_thread(), SubtypeId::Buffer))
|
||||
}
|
||||
|
||||
#[cfg(feature = "luajit")]
|
||||
ffi::LUA_TCDATA => {
|
||||
// TODO: Fix this in a next major release
|
||||
panic!("cdata objects cannot be handled by mlua yet");
|
||||
// CData is represented as a userdata type
|
||||
ffi::lua_xpush(state, self.ref_thread(), idx);
|
||||
Value::UserData(AnyUserData(self.pop_ref_thread(), SubtypeId::CData))
|
||||
}
|
||||
|
||||
_ => mlua_panic!("LUA_TNONE in pop_value"),
|
||||
@@ -2559,6 +2558,15 @@ impl Lua {
|
||||
ffi::lua_xpush(self.ref_thread(), self.state(), lref.index);
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "unstable", not(feature = "send")))]
|
||||
pub(crate) unsafe fn push_owned_ref(&self, loref: &crate::types::LuaOwnedRef) {
|
||||
assert!(
|
||||
Arc::ptr_eq(&loref.inner, &self.0),
|
||||
"Lua instance passed Value created from a different main Lua state"
|
||||
);
|
||||
ffi::lua_xpush(self.ref_thread(), self.state(), loref.index);
|
||||
}
|
||||
|
||||
// Pops the topmost element of the stack and stores a reference to it. This pins the object,
|
||||
// preventing garbage collection until the returned `LuaRef` is dropped.
|
||||
//
|
||||
@@ -2611,6 +2619,16 @@ impl Lua {
|
||||
LuaRef::new(self, index)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) unsafe fn push_error_traceback(&self) {
|
||||
let state = self.state();
|
||||
#[cfg(any(feature = "lua51", feature = "luajit", feature = "luau"))]
|
||||
ffi::lua_xpush(self.ref_thread(), state, ExtraData::ERROR_TRACEBACK_IDX);
|
||||
// Lua 5.2+ support light C functions that does not require extra allocations
|
||||
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
|
||||
ffi::lua_pushcfunction(state, error_traceback);
|
||||
}
|
||||
|
||||
unsafe fn register_userdata_metatable<'lua, T: 'static>(
|
||||
&'lua self,
|
||||
mut registry: UserDataRegistry<'lua, T>,
|
||||
@@ -2667,6 +2685,7 @@ impl Lua {
|
||||
rawset_field(state, metatable_index, "__index")?;
|
||||
}
|
||||
_ => {
|
||||
ffi::lua_pop(state, 1);
|
||||
// Propagate fields to the field getters
|
||||
for (k, f) in registry.fields {
|
||||
registry.field_getters.push((k, f))
|
||||
@@ -2953,11 +2972,14 @@ impl Lua {
|
||||
let fut = &mut (*upvalue).data;
|
||||
let mut ctx = Context::from_waker(lua.waker());
|
||||
match fut.as_mut().poll(&mut ctx) {
|
||||
Poll::Pending => Ok(0),
|
||||
Poll::Pending => {
|
||||
ffi::lua_pushnil(state);
|
||||
ffi::lua_pushlightuserdata(state, Lua::poll_pending().0);
|
||||
Ok(2)
|
||||
}
|
||||
Poll::Ready(nresults) => {
|
||||
let nresults = nresults?;
|
||||
match nresults {
|
||||
0..=2 => {
|
||||
match nresults? {
|
||||
nresults @ 0..=2 => {
|
||||
// Fast path for up to 2 results without creating a table
|
||||
ffi::lua_pushinteger(state, nresults as _);
|
||||
if nresults > 0 {
|
||||
@@ -2965,7 +2987,7 @@ impl Lua {
|
||||
}
|
||||
Ok(nresults + 1)
|
||||
}
|
||||
_ => {
|
||||
nresults => {
|
||||
let results = MultiValue::from_stack_multi(nresults, lua)?;
|
||||
ffi::lua_pushinteger(state, nresults as _);
|
||||
lua.push_value(Value::Table(lua.create_sequence_from(results)?))?;
|
||||
@@ -3009,20 +3031,17 @@ impl Lua {
|
||||
|
||||
let coroutine = self.globals().get::<_, Table>("coroutine")?;
|
||||
|
||||
let env = self.create_table_with_capacity(0, 4)?;
|
||||
let env = self.create_table_with_capacity(0, 3)?;
|
||||
env.set("get_poll", get_poll)?;
|
||||
// Cache `yield` function
|
||||
env.set("yield", coroutine.get::<_, Function>("yield")?)?;
|
||||
unsafe {
|
||||
env.set("unpack", self.create_c_function(unpack)?)?;
|
||||
}
|
||||
env.set("pending", {
|
||||
LightUserData(&ASYNC_POLL_PENDING as *const u8 as *mut c_void)
|
||||
})?;
|
||||
|
||||
self.load(
|
||||
r#"
|
||||
local poll = get_poll(...)
|
||||
local pending, yield, unpack = pending, yield, unpack
|
||||
while true do
|
||||
local nres, res, res2 = poll()
|
||||
if nres ~= nil then
|
||||
@@ -3036,7 +3055,7 @@ impl Lua {
|
||||
return unpack(res, nres)
|
||||
end
|
||||
end
|
||||
yield(pending)
|
||||
yield(res) -- `res` is a "pending" value
|
||||
end
|
||||
"#,
|
||||
)
|
||||
@@ -3058,6 +3077,14 @@ impl Lua {
|
||||
mem::replace(&mut (*self.extra.get()).waker, waker)
|
||||
}
|
||||
|
||||
/// Returns internal `Poll::Pending` constant used for executing async callbacks.
|
||||
#[cfg(feature = "async")]
|
||||
#[doc(hidden)]
|
||||
#[inline]
|
||||
pub fn poll_pending() -> LightUserData {
|
||||
LightUserData(&ASYNC_POLL_PENDING as *const u8 as *mut c_void)
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn make_userdata<T>(&self, data: UserDataCell<T>) -> Result<AnyUserData>
|
||||
where
|
||||
T: UserData + 'static,
|
||||
@@ -3127,9 +3154,10 @@ impl Lua {
|
||||
ffi::lua_setuservalue(state, -2);
|
||||
}
|
||||
|
||||
Ok(AnyUserData(self.pop_ref()))
|
||||
Ok(AnyUserData(self.pop_ref(), SubtypeId::None))
|
||||
}
|
||||
|
||||
// Luau version located in `luau/mod.rs`
|
||||
#[cfg(not(feature = "luau"))]
|
||||
fn disable_c_modules(&self) -> Result<()> {
|
||||
let package: Table = self.globals().get("package")?;
|
||||
@@ -3168,16 +3196,13 @@ impl Lua {
|
||||
#[inline]
|
||||
pub(crate) unsafe fn unlikely_memory_error(&self) -> bool {
|
||||
// MemoryInfo is empty in module mode so we cannot predict memory limits
|
||||
(*self.extra.get())
|
||||
.mem_state
|
||||
.map(|x| x.as_ref().memory_limit() == 0)
|
||||
.unwrap_or_else(|| {
|
||||
// Alternatively, check the special flag (only for module mode)
|
||||
#[cfg(feature = "module")]
|
||||
return (*self.extra.get()).skip_memory_check;
|
||||
#[cfg(not(feature = "module"))]
|
||||
return false;
|
||||
})
|
||||
match MemoryState::get(self.main_state) {
|
||||
mem_state if !mem_state.is_null() => (*mem_state).memory_limit() == 0,
|
||||
#[cfg(feature = "module")]
|
||||
_ => (*self.extra.get()).skip_memory_check, // Check the special flag (only for module mode)
|
||||
#[cfg(not(feature = "module"))]
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "unstable")]
|
||||
@@ -3223,11 +3248,9 @@ impl LuaInner {
|
||||
}
|
||||
|
||||
impl ExtraData {
|
||||
#[cfg(feature = "luau")]
|
||||
#[inline]
|
||||
pub(crate) fn mem_state(&self) -> NonNull<MemoryState> {
|
||||
self.mem_state.unwrap()
|
||||
}
|
||||
// Index of `error_traceback` function in auxiliary thread stack
|
||||
#[cfg(any(feature = "lua51", feature = "luajit", feature = "luau"))]
|
||||
const ERROR_TRACEBACK_IDX: c_int = 1;
|
||||
}
|
||||
|
||||
struct StateGuard<'a>(&'a LuaInner, *mut ffi::lua_State);
|
||||
@@ -3245,13 +3268,13 @@ impl<'a> Drop for StateGuard<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "luau")]
|
||||
unsafe fn extra_data(state: *mut ffi::lua_State) -> *mut ExtraData {
|
||||
(*ffi::lua_callbacks(state)).userdata as *mut ExtraData
|
||||
}
|
||||
#[cfg(feature = "luau")]
|
||||
if cfg!(not(feature = "module")) {
|
||||
// In the main app we can use `lua_callbacks` to access ExtraData
|
||||
return (*ffi::lua_callbacks(state)).userdata as *mut _;
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "luau"))]
|
||||
unsafe fn extra_data(state: *mut ffi::lua_State) -> *mut ExtraData {
|
||||
let extra_key = &EXTRA_REGISTRY_KEY as *const u8 as *const c_void;
|
||||
if ffi::lua_rawgetp(state, ffi::LUA_REGISTRYINDEX, extra_key) != ffi::LUA_TUSERDATA {
|
||||
// `ExtraData` can be null only when Lua state is foreign.
|
||||
@@ -3264,6 +3287,23 @@ unsafe fn extra_data(state: *mut ffi::lua_State) -> *mut ExtraData {
|
||||
(*extra_ptr).get()
|
||||
}
|
||||
|
||||
unsafe fn set_extra_data(
|
||||
state: *mut ffi::lua_State,
|
||||
extra: &Arc<UnsafeCell<ExtraData>>,
|
||||
) -> Result<()> {
|
||||
#[cfg(feature = "luau")]
|
||||
if cfg!(not(feature = "module")) {
|
||||
(*ffi::lua_callbacks(state)).userdata = extra.get() as *mut _;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
push_gc_userdata(state, Arc::clone(extra), true)?;
|
||||
protect_lua!(state, 1, 0, fn(state) {
|
||||
let extra_key = &EXTRA_REGISTRY_KEY as *const u8 as *const c_void;
|
||||
ffi::lua_rawsetp(state, ffi::LUA_REGISTRYINDEX, extra_key);
|
||||
})
|
||||
}
|
||||
|
||||
// Creates required entries in the metatable cache (see `util::METATABLE_CACHE`)
|
||||
pub(crate) fn init_metatable_cache(cache: &mut FxHashMap<TypeId, u8>) {
|
||||
cache.insert(TypeId::of::<Arc<UnsafeCell<ExtraData>>>(), 0);
|
||||
@@ -3502,6 +3542,12 @@ unsafe fn load_from_std_lib(state: *mut ffi::lua_State, libs: StdLib) -> Result<
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "luau")]
|
||||
if libs.contains(StdLib::BUFFER) {
|
||||
requiref(state, ffi::LUA_BUFFERLIBNAME, ffi::luaopen_buffer, 1)?;
|
||||
ffi::lua_pop(state, 1);
|
||||
}
|
||||
|
||||
if libs.contains(StdLib::MATH) {
|
||||
requiref(state, ffi::LUA_MATHLIBNAME, ffi::luaopen_math, 1)?;
|
||||
ffi::lua_pop(state, 1);
|
||||
@@ -3517,6 +3563,11 @@ unsafe fn load_from_std_lib(state: *mut ffi::lua_State, libs: StdLib) -> Result<
|
||||
requiref(state, ffi::LUA_LOADLIBNAME, ffi::luaopen_package, 1)?;
|
||||
ffi::lua_pop(state, 1);
|
||||
}
|
||||
#[cfg(feature = "luau")]
|
||||
if libs.contains(StdLib::PACKAGE) {
|
||||
let lua: &Lua = mem::transmute((*extra_data(state)).inner.assume_init_ref());
|
||||
crate::luau::register_package_module(lua)?;
|
||||
}
|
||||
|
||||
#[cfg(feature = "luajit")]
|
||||
{
|
||||
|
||||
@@ -1,25 +1,19 @@
|
||||
use std::ffi::CStr;
|
||||
use std::os::raw::{c_float, c_int};
|
||||
use std::string::String as StdString;
|
||||
|
||||
use crate::chunk::ChunkMode;
|
||||
use crate::error::{Error, Result};
|
||||
use crate::error::Result;
|
||||
use crate::lua::Lua;
|
||||
use crate::table::Table;
|
||||
use crate::util::{check_stack, StackGuard};
|
||||
use crate::value::Value;
|
||||
|
||||
// Since Luau has some missing standard function, we re-implement them here
|
||||
// Since Luau has some missing standard functions, we re-implement them here
|
||||
|
||||
impl Lua {
|
||||
pub(crate) unsafe fn prepare_luau_state(&self) -> Result<()> {
|
||||
pub(crate) unsafe fn configure_luau(&self) -> Result<()> {
|
||||
let globals = self.globals();
|
||||
|
||||
globals.raw_set(
|
||||
"collectgarbage",
|
||||
self.create_c_function(lua_collectgarbage)?,
|
||||
)?;
|
||||
globals.raw_set("require", self.create_function(lua_require)?)?;
|
||||
globals.raw_set("vector", self.create_c_function(lua_vector)?)?;
|
||||
|
||||
// Set `_VERSION` global to include version number
|
||||
@@ -30,6 +24,11 @@ impl Lua {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn disable_c_modules(&self) -> Result<()> {
|
||||
package::disable_dylibs(self);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
unsafe extern "C-unwind" fn lua_collectgarbage(state: *mut ffi::lua_State) -> c_int {
|
||||
@@ -69,58 +68,6 @@ unsafe extern "C-unwind" fn lua_collectgarbage(state: *mut ffi::lua_State) -> c_
|
||||
}
|
||||
}
|
||||
|
||||
fn lua_require(lua: &Lua, name: Option<StdString>) -> Result<Value> {
|
||||
let name = name.ok_or_else(|| Error::runtime("invalid module name"))?;
|
||||
|
||||
// Find module in the cache
|
||||
let state = lua.state();
|
||||
let loaded = unsafe {
|
||||
let _sg = StackGuard::new(state);
|
||||
check_stack(state, 2)?;
|
||||
protect_lua!(state, 0, 1, fn(state) {
|
||||
ffi::luaL_getsubtable(state, ffi::LUA_REGISTRYINDEX, cstr!("_LOADED"));
|
||||
})?;
|
||||
Table(lua.pop_ref())
|
||||
};
|
||||
if let Some(v) = loaded.raw_get(name.clone())? {
|
||||
return Ok(v);
|
||||
}
|
||||
|
||||
// Load file from filesystem
|
||||
let mut search_path = std::env::var("LUAU_PATH").unwrap_or_default();
|
||||
if search_path.is_empty() {
|
||||
search_path = "?.luau;?.lua".into();
|
||||
}
|
||||
|
||||
let (mut source, mut source_name) = (None, String::new());
|
||||
for path in search_path.split(';') {
|
||||
let file_path = path.replacen('?', &name, 1);
|
||||
if let Ok(buf) = std::fs::read(&file_path) {
|
||||
source = Some(buf);
|
||||
source_name = file_path;
|
||||
break;
|
||||
}
|
||||
}
|
||||
let source = source.ok_or_else(|| Error::runtime(format!("cannot find '{name}'")))?;
|
||||
|
||||
let value = lua
|
||||
.load(&source)
|
||||
.set_name(&format!("={source_name}"))
|
||||
.set_mode(ChunkMode::Text)
|
||||
.call::<_, Value>(())?;
|
||||
|
||||
// Save in the cache
|
||||
loaded.raw_set(
|
||||
name,
|
||||
match value.clone() {
|
||||
Value::Nil => Value::Boolean(true),
|
||||
v => v,
|
||||
},
|
||||
)?;
|
||||
|
||||
Ok(value)
|
||||
}
|
||||
|
||||
// Luau vector datatype constructor
|
||||
unsafe extern "C-unwind" fn lua_vector(state: *mut ffi::lua_State) -> c_int {
|
||||
let x = ffi::luaL_checknumber(state, 1) as c_float;
|
||||
@@ -135,3 +82,7 @@ unsafe extern "C-unwind" fn lua_vector(state: *mut ffi::lua_State) -> c_int {
|
||||
ffi::lua_pushvector(state, x, y, z, w);
|
||||
1
|
||||
}
|
||||
|
||||
pub(crate) use package::register_package_module;
|
||||
|
||||
mod package;
|
||||
@@ -0,0 +1,268 @@
|
||||
use std::ffi::CStr;
|
||||
use std::fmt::Write;
|
||||
use std::os::raw::c_int;
|
||||
use std::path::{PathBuf, MAIN_SEPARATOR_STR};
|
||||
use std::string::String as StdString;
|
||||
use std::{env, fs};
|
||||
|
||||
use crate::chunk::ChunkMode;
|
||||
use crate::error::Result;
|
||||
use crate::lua::Lua;
|
||||
use crate::table::Table;
|
||||
use crate::types::RegistryKey;
|
||||
use crate::value::{IntoLua, Value};
|
||||
|
||||
#[cfg(unix)]
|
||||
use {libloading::Library, rustc_hash::FxHashMap};
|
||||
|
||||
//
|
||||
// Luau package module
|
||||
//
|
||||
|
||||
#[cfg(unix)]
|
||||
const TARGET_MLUA_LUAU_ABI_VERSION: u32 = 1;
|
||||
|
||||
#[cfg(all(unix, feature = "module"))]
|
||||
#[no_mangle]
|
||||
#[used]
|
||||
pub static MLUA_LUAU_ABI_VERSION: u32 = TARGET_MLUA_LUAU_ABI_VERSION;
|
||||
|
||||
// We keep reference to the `package` table in registry under this key
|
||||
struct PackageKey(RegistryKey);
|
||||
|
||||
// We keep reference to the loaded dylibs in application data
|
||||
#[cfg(unix)]
|
||||
struct LoadedDylibs(FxHashMap<PathBuf, Library>);
|
||||
|
||||
#[cfg(unix)]
|
||||
impl std::ops::Deref for LoadedDylibs {
|
||||
type Target = FxHashMap<PathBuf, Library>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
impl std::ops::DerefMut for LoadedDylibs {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.0
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn register_package_module(lua: &Lua) -> Result<()> {
|
||||
// Create the package table and store it in app_data for later use (bypassing globals lookup)
|
||||
let package = lua.create_table()?;
|
||||
lua.set_app_data(PackageKey(lua.create_registry_value(package.clone())?));
|
||||
|
||||
// Set `package.path`
|
||||
let mut search_path = env::var("LUAU_PATH")
|
||||
.or_else(|_| env::var("LUA_PATH"))
|
||||
.unwrap_or_default();
|
||||
if search_path.is_empty() {
|
||||
search_path = "?.luau;?.lua".to_string();
|
||||
}
|
||||
package.raw_set("path", search_path)?;
|
||||
|
||||
// Set `package.cpath`
|
||||
#[cfg(unix)]
|
||||
{
|
||||
let mut search_cpath = env::var("LUAU_CPATH")
|
||||
.or_else(|_| env::var("LUA_CPATH"))
|
||||
.unwrap_or_default();
|
||||
if search_cpath.is_empty() {
|
||||
if cfg!(any(target_os = "macos", target_os = "ios")) {
|
||||
search_cpath = "?.dylib".to_string();
|
||||
} else {
|
||||
search_cpath = "?.so".to_string();
|
||||
}
|
||||
}
|
||||
package.raw_set("cpath", search_cpath)?;
|
||||
}
|
||||
|
||||
// Set `package.loaded` (table with a list of loaded modules)
|
||||
let loaded = lua.create_table()?;
|
||||
package.raw_set("loaded", loaded.clone())?;
|
||||
lua.set_named_registry_value("_LOADED", loaded)?;
|
||||
|
||||
// Set `package.loaders`
|
||||
let loaders = lua.create_sequence_from([lua.create_function(lua_loader)?])?;
|
||||
package.raw_set("loaders", loaders.clone())?;
|
||||
#[cfg(unix)]
|
||||
{
|
||||
loaders.push(lua.create_function(dylib_loader)?)?;
|
||||
lua.set_app_data(LoadedDylibs(FxHashMap::default()));
|
||||
}
|
||||
lua.set_named_registry_value("_LOADERS", loaders)?;
|
||||
|
||||
// Register the module and `require` function in globals
|
||||
let globals = lua.globals();
|
||||
globals.raw_set("package", package)?;
|
||||
globals.raw_set("require", unsafe { lua.create_c_function(lua_require)? })?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn disable_dylibs(lua: &Lua) {
|
||||
// Presence of `LoadedDylibs` in app data is used as a flag
|
||||
// to check whether binary modules are enabled
|
||||
#[cfg(unix)]
|
||||
lua.remove_app_data::<LoadedDylibs>();
|
||||
}
|
||||
|
||||
unsafe extern "C-unwind" fn lua_require(state: *mut ffi::lua_State) -> c_int {
|
||||
ffi::lua_settop(state, 1);
|
||||
let name = ffi::luaL_checkstring(state, 1);
|
||||
ffi::luaL_getsubtable(state, ffi::LUA_REGISTRYINDEX, cstr!("_LOADED")); // _LOADED is at index 2
|
||||
if ffi::lua_rawgetfield(state, 2, name) != ffi::LUA_TNIL {
|
||||
return 1; // module is already loaded
|
||||
}
|
||||
ffi::lua_pop(state, 1); // remove nil
|
||||
|
||||
// load the module
|
||||
let err_buf = ffi::lua_newuserdata_t::<StdString>(state);
|
||||
err_buf.write(StdString::new());
|
||||
ffi::luaL_getsubtable(state, ffi::LUA_REGISTRYINDEX, cstr!("_LOADERS")); // _LOADERS is at index 3
|
||||
for i in 1.. {
|
||||
if ffi::lua_rawgeti(state, -1, i) == ffi::LUA_TNIL {
|
||||
// no more loaders?
|
||||
if (*err_buf).is_empty() {
|
||||
ffi::luaL_error(state, cstr!("module '%s' not found"), name);
|
||||
} else {
|
||||
let bytes = (*err_buf).as_bytes();
|
||||
let extra = ffi::lua_pushlstring(state, bytes.as_ptr() as *const _, bytes.len());
|
||||
ffi::luaL_error(state, cstr!("module '%s' not found:%s"), name, extra);
|
||||
}
|
||||
}
|
||||
ffi::lua_pushvalue(state, 1); // name arg
|
||||
ffi::lua_call(state, 1, 2); // call loader
|
||||
match ffi::lua_type(state, -2) {
|
||||
ffi::LUA_TFUNCTION => break, // loader found
|
||||
ffi::LUA_TSTRING => {
|
||||
// error message
|
||||
let msg = ffi::lua_tostring(state, -2);
|
||||
let msg = CStr::from_ptr(msg).to_string_lossy();
|
||||
_ = write!(&mut *err_buf, "\n\t{msg}");
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
ffi::lua_pop(state, 2); // remove both results
|
||||
}
|
||||
ffi::lua_pushvalue(state, 1); // name is 1st argument to module loader
|
||||
ffi::lua_rotate(state, -2, 1); // loader data <-> name
|
||||
|
||||
// stack: ...; loader function; module name; loader data
|
||||
ffi::lua_call(state, 2, 1);
|
||||
// stack: ...; result from loader function
|
||||
if ffi::lua_isnil(state, -1) != 0 {
|
||||
ffi::lua_pop(state, 1);
|
||||
ffi::lua_pushboolean(state, 1); // use true as result
|
||||
}
|
||||
ffi::lua_pushvalue(state, -1); // make copy of entrypoint result
|
||||
ffi::lua_setfield(state, 2, name); /* _LOADED[name] = returned value */
|
||||
1
|
||||
}
|
||||
|
||||
/// Searches for the given `name` in the given `path`.
|
||||
///
|
||||
/// `path` is a string containing a sequence of templates separated by semicolons.
|
||||
fn package_searchpath(name: &str, search_path: &str, try_prefix: bool) -> Option<PathBuf> {
|
||||
let mut names = vec![name.replace('.', MAIN_SEPARATOR_STR)];
|
||||
if try_prefix && name.contains('.') {
|
||||
let prefix = name.split_once('.').map(|(prefix, _)| prefix).unwrap();
|
||||
names.push(prefix.to_string());
|
||||
}
|
||||
for path in search_path.split(';') {
|
||||
for name in &names {
|
||||
let file_path = PathBuf::from(path.replace('?', name));
|
||||
if let Ok(true) = fs::metadata(&file_path).map(|m| m.is_file()) {
|
||||
return Some(file_path);
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
//
|
||||
// Module loaders
|
||||
//
|
||||
|
||||
/// Tries to load a lua (text) file
|
||||
fn lua_loader(lua: &Lua, modname: StdString) -> Result<Value> {
|
||||
let package = {
|
||||
let key = lua.app_data_ref::<PackageKey>().unwrap();
|
||||
lua.registry_value::<Table>(&key.0)
|
||||
}?;
|
||||
let search_path = package.get::<_, StdString>("path").unwrap_or_default();
|
||||
|
||||
if let Some(file_path) = package_searchpath(&modname, &search_path, false) {
|
||||
match fs::read(&file_path) {
|
||||
Ok(buf) => {
|
||||
return lua
|
||||
.load(&buf)
|
||||
.set_name(&format!("={}", file_path.display()))
|
||||
.set_mode(ChunkMode::Text)
|
||||
.into_function()
|
||||
.map(Value::Function);
|
||||
}
|
||||
Err(err) => {
|
||||
return format!("cannot open '{}': {err}", file_path.display()).into_lua(lua);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Value::Nil)
|
||||
}
|
||||
|
||||
/// Tries to load a dynamic library
|
||||
#[cfg(unix)]
|
||||
fn dylib_loader(lua: &Lua, modname: StdString) -> Result<Value> {
|
||||
let package = {
|
||||
let key = lua.app_data_ref::<PackageKey>().unwrap();
|
||||
lua.registry_value::<Table>(&key.0)
|
||||
}?;
|
||||
let search_cpath = package.get::<_, StdString>("cpath").unwrap_or_default();
|
||||
|
||||
let find_symbol = |lib: &Library| unsafe {
|
||||
if let Ok(entry) = lib.get::<ffi::lua_CFunction>(format!("luaopen_{modname}\0").as_bytes())
|
||||
{
|
||||
return lua.create_c_function(*entry).map(Value::Function);
|
||||
}
|
||||
// Try all in one mode
|
||||
if let Ok(entry) = lib.get::<ffi::lua_CFunction>(
|
||||
format!("luaopen_{}\0", modname.replace('.', "_")).as_bytes(),
|
||||
) {
|
||||
return lua.create_c_function(*entry).map(Value::Function);
|
||||
}
|
||||
"cannot find module entrypoint".into_lua(lua)
|
||||
};
|
||||
|
||||
if let Some(file_path) = package_searchpath(&modname, &search_cpath, true) {
|
||||
let file_path = file_path.canonicalize()?;
|
||||
// Load the library and check for symbol
|
||||
unsafe {
|
||||
let mut loaded_dylibs = match lua.app_data_mut::<LoadedDylibs>() {
|
||||
Some(loaded_dylibs) => loaded_dylibs,
|
||||
None => return "dynamic libraries are disabled in safe mode".into_lua(lua),
|
||||
};
|
||||
// Check if it's already loaded
|
||||
if let Some(lib) = loaded_dylibs.get(&file_path) {
|
||||
return find_symbol(lib);
|
||||
}
|
||||
if let Ok(lib) = Library::new(&file_path) {
|
||||
// Check version
|
||||
let mod_version = lib.get::<*const u32>(b"MLUA_LUAU_ABI_VERSION");
|
||||
let mod_version = mod_version.map(|v| **v).unwrap_or_default();
|
||||
if mod_version != TARGET_MLUA_LUAU_ABI_VERSION {
|
||||
let err = format!("wrong module ABI version (expected {TARGET_MLUA_LUAU_ABI_VERSION}, got {mod_version})");
|
||||
return err.into_lua(lua);
|
||||
}
|
||||
let symbol = find_symbol(&lib);
|
||||
loaded_dylibs.insert(file_path, lib);
|
||||
return symbol;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Value::Nil)
|
||||
}
|
||||
+24
-29
@@ -2,11 +2,9 @@ use std::alloc::{self, Layout};
|
||||
use std::os::raw::c_void;
|
||||
use std::ptr;
|
||||
|
||||
#[cfg(feature = "luau")]
|
||||
use crate::lua::ExtraData;
|
||||
|
||||
pub(crate) static ALLOCATOR: ffi::lua_Alloc = allocator;
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Default)]
|
||||
pub(crate) struct MemoryState {
|
||||
used_memory: isize,
|
||||
@@ -20,6 +18,21 @@ pub(crate) struct MemoryState {
|
||||
}
|
||||
|
||||
impl MemoryState {
|
||||
#[inline]
|
||||
pub(crate) unsafe fn get(state: *mut ffi::lua_State) -> *mut Self {
|
||||
let mut mem_state = ptr::null_mut();
|
||||
#[cfg(feature = "luau")]
|
||||
{
|
||||
ffi::lua_getallocf(state, &mut mem_state);
|
||||
mlua_assert!(!mem_state.is_null(), "Luau state has no allocator userdata");
|
||||
}
|
||||
#[cfg(not(feature = "luau"))]
|
||||
if ffi::lua_getallocf(state, &mut mem_state) != ALLOCATOR {
|
||||
mem_state = ptr::null_mut();
|
||||
}
|
||||
mem_state as *mut MemoryState
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn used_memory(&self) -> usize {
|
||||
self.used_memory as usize
|
||||
@@ -37,36 +50,21 @@ impl MemoryState {
|
||||
prev_limit as usize
|
||||
}
|
||||
|
||||
// This function is used primarily for calling `lua_pushcfunction` in lua5.1/jit
|
||||
// This function is used primarily for calling `lua_pushcfunction` in lua5.1/jit/luau
|
||||
// to bypass the memory limit (if set).
|
||||
#[cfg(any(feature = "lua51", feature = "luajit"))]
|
||||
#[cfg(any(feature = "lua51", feature = "luajit", feature = "luau"))]
|
||||
#[inline]
|
||||
pub(crate) unsafe fn relax_limit_with(state: *mut ffi::lua_State, f: impl FnOnce()) {
|
||||
let mut mem_state: *mut c_void = ptr::null_mut();
|
||||
if ffi::lua_getallocf(state, &mut mem_state) == ALLOCATOR {
|
||||
(*(mem_state as *mut MemoryState)).ignore_limit = true;
|
||||
let mem_state = Self::get(state);
|
||||
if !mem_state.is_null() {
|
||||
(*mem_state).ignore_limit = true;
|
||||
f();
|
||||
(*(mem_state as *mut MemoryState)).ignore_limit = false;
|
||||
(*mem_state).ignore_limit = false;
|
||||
} else {
|
||||
f();
|
||||
}
|
||||
}
|
||||
|
||||
// Same as the above but for Luau
|
||||
// It does not have `lua_getallocf` function, so instead we use `lua_callbacks`
|
||||
#[cfg(feature = "luau")]
|
||||
#[inline]
|
||||
pub(crate) unsafe fn relax_limit_with(state: *mut ffi::lua_State, f: impl FnOnce()) {
|
||||
let extra = (*ffi::lua_callbacks(state)).userdata as *mut ExtraData;
|
||||
if extra.is_null() {
|
||||
return f();
|
||||
}
|
||||
let mem_state = (*extra).mem_state();
|
||||
(*mem_state.as_ptr()).ignore_limit = true;
|
||||
f();
|
||||
(*mem_state.as_ptr()).ignore_limit = false;
|
||||
}
|
||||
|
||||
// Does nothing apart from calling `f()`, we don't need to bypass any limits
|
||||
#[cfg(any(feature = "lua52", feature = "lua53", feature = "lua54"))]
|
||||
#[inline]
|
||||
@@ -76,12 +74,9 @@ impl MemoryState {
|
||||
|
||||
// Returns `true` if the memory limit was reached on the last memory operation
|
||||
#[cfg(feature = "luau")]
|
||||
#[inline]
|
||||
pub(crate) unsafe fn limit_reached(state: *mut ffi::lua_State) -> bool {
|
||||
let extra = (*ffi::lua_callbacks(state)).userdata as *mut ExtraData;
|
||||
if extra.is_null() {
|
||||
return false;
|
||||
}
|
||||
(*(*extra).mem_state().as_ptr()).limit_reached
|
||||
(*Self::get(state)).limit_reached
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+3
-2
@@ -10,7 +10,7 @@ use serde::Serialize;
|
||||
use crate::error::{Error, Result};
|
||||
use crate::function::Function;
|
||||
use crate::lua::Lua;
|
||||
use crate::types::{Callback, CallbackUpvalue, LuaRef, MaybeSend};
|
||||
use crate::types::{Callback, CallbackUpvalue, LuaRef, MaybeSend, SubtypeId};
|
||||
use crate::userdata::{
|
||||
AnyUserData, MetaMethod, UserData, UserDataCell, UserDataFields, UserDataMethods,
|
||||
};
|
||||
@@ -434,6 +434,7 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
|
||||
push_table(state, 0, fields_nrec, true)?;
|
||||
}
|
||||
for (k, f) in registry.fields {
|
||||
#[rustfmt::skip]
|
||||
let NonStaticMethod::Function(f) = f else { unreachable!() };
|
||||
mlua_assert!(f(lua, 0)? == 1, "field function must return one value");
|
||||
rawset_field(state, -2, &k)?;
|
||||
@@ -510,7 +511,7 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
|
||||
#[cfg(not(feature = "luau"))]
|
||||
std::ptr::write(ud_ptr as _, UserDataCell::new(data));
|
||||
ffi::lua_setmetatable(state, -2);
|
||||
let ud = AnyUserData(lua.pop_ref());
|
||||
let ud = AnyUserData(lua.pop_ref(), SubtypeId::None);
|
||||
lua.register_raw_userdata_metatable(mt_ptr, None);
|
||||
|
||||
#[cfg(any(feature = "lua51", feature = "luajit"))]
|
||||
|
||||
+15
-2
@@ -16,32 +16,44 @@ impl StdLib {
|
||||
feature = "luau"
|
||||
))]
|
||||
pub const COROUTINE: StdLib = StdLib(1);
|
||||
|
||||
/// [`table`](https://www.lua.org/manual/5.4/manual.html#6.6) library
|
||||
pub const TABLE: StdLib = StdLib(1 << 1);
|
||||
|
||||
/// [`io`](https://www.lua.org/manual/5.4/manual.html#6.8) library
|
||||
#[cfg(not(feature = "luau"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(not(feature = "luau"))))]
|
||||
pub const IO: StdLib = StdLib(1 << 2);
|
||||
|
||||
/// [`os`](https://www.lua.org/manual/5.4/manual.html#6.9) library
|
||||
pub const OS: StdLib = StdLib(1 << 3);
|
||||
|
||||
/// [`string`](https://www.lua.org/manual/5.4/manual.html#6.4) library
|
||||
pub const STRING: StdLib = StdLib(1 << 4);
|
||||
|
||||
/// [`utf8`](https://www.lua.org/manual/5.4/manual.html#6.5) library
|
||||
///
|
||||
/// Requires `feature = "lua54/lua53/luau"`
|
||||
#[cfg(any(feature = "lua54", feature = "lua53", feature = "luau"))]
|
||||
pub const UTF8: StdLib = StdLib(1 << 5);
|
||||
|
||||
/// [`bit`](https://www.lua.org/manual/5.2/manual.html#6.7) library
|
||||
///
|
||||
/// Requires `feature = "lua52/luajit/luau"`
|
||||
#[cfg(any(feature = "lua52", feature = "luajit", feature = "luau", doc))]
|
||||
pub const BIT: StdLib = StdLib(1 << 6);
|
||||
|
||||
/// [`math`](https://www.lua.org/manual/5.4/manual.html#6.7) library
|
||||
pub const MATH: StdLib = StdLib(1 << 7);
|
||||
|
||||
/// [`package`](https://www.lua.org/manual/5.4/manual.html#6.3) library
|
||||
#[cfg(not(feature = "luau"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(not(feature = "luau"))))]
|
||||
pub const PACKAGE: StdLib = StdLib(1 << 8);
|
||||
|
||||
/// [`buffer`](https://luau-lang.org/library#buffer-library) library
|
||||
#[cfg(any(feature = "luau", doc))]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
|
||||
pub const BUFFER: StdLib = StdLib(1 << 9);
|
||||
|
||||
/// [`jit`](http://luajit.org/ext_jit.html) library
|
||||
///
|
||||
/// Requires `feature = "luajit"`
|
||||
@@ -55,6 +67,7 @@ impl StdLib {
|
||||
#[cfg(any(feature = "luajit", doc))]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "luajit")))]
|
||||
pub const FFI: StdLib = StdLib(1 << 30);
|
||||
|
||||
/// (**unsafe**) [`debug`](https://www.lua.org/manual/5.4/manual.html#6.10) library
|
||||
pub const DEBUG: StdLib = StdLib(1 << 31);
|
||||
|
||||
|
||||
+89
-44
@@ -651,6 +651,34 @@ impl<'lua> Table<'lua> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Iterates over the pairs of the table, invoking the given closure on each pair.
|
||||
///
|
||||
/// This method is similar to [`Table::pairs`], but optimized for performance.
|
||||
/// It does not invoke the `__pairs` metamethod.
|
||||
pub fn for_each<K, V>(&self, mut f: impl FnMut(K, V) -> Result<()>) -> Result<()>
|
||||
where
|
||||
K: FromLua<'lua>,
|
||||
V: FromLua<'lua>,
|
||||
{
|
||||
let lua = self.0.lua;
|
||||
let state = lua.state();
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(state);
|
||||
check_stack(state, 5)?;
|
||||
|
||||
lua.push_ref(&self.0);
|
||||
ffi::lua_pushnil(state);
|
||||
while ffi::lua_next(state, -2) != 0 {
|
||||
let k = K::from_stack(-2, lua)?;
|
||||
let v = V::from_stack(-1, lua)?;
|
||||
f(k, v)?;
|
||||
// Keep key for next iteration
|
||||
ffi::lua_pop(state, 1);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Consume this table and return an iterator over all values in the sequence part of the table.
|
||||
///
|
||||
/// The iterator will yield all values `t[1]`, `t[2]` and so on, until a `nil` value is
|
||||
@@ -692,7 +720,7 @@ impl<'lua> Table<'lua> {
|
||||
pub fn sequence_values<V: FromLua<'lua>>(self) -> TableSequence<'lua, V> {
|
||||
TableSequence {
|
||||
table: self.0,
|
||||
index: Some(1),
|
||||
index: 1,
|
||||
len: None,
|
||||
_phantom: PhantomData,
|
||||
}
|
||||
@@ -712,15 +740,15 @@ impl<'lua> Table<'lua> {
|
||||
let len = len.unwrap_or_else(|| self.raw_len()) as Integer;
|
||||
TableSequence {
|
||||
table: self.0,
|
||||
index: Some(1),
|
||||
index: 1,
|
||||
len: Some(len),
|
||||
_phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets element value at position `idx` without invoking metamethods.
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn raw_seti<V: IntoLua<'lua>>(&self, idx: usize, value: V) -> Result<()> {
|
||||
#[doc(hidden)]
|
||||
pub fn raw_seti<V: IntoLua<'lua>>(&self, idx: usize, value: V) -> Result<()> {
|
||||
#[cfg(feature = "luau")]
|
||||
self.check_readonly_write()?;
|
||||
|
||||
@@ -1079,21 +1107,48 @@ impl<'a, 'lua> Serialize for SerializableTable<'a, 'lua> {
|
||||
|
||||
// HashMap
|
||||
let mut map = serializer.serialize_map(None)?;
|
||||
let pairs = MapPairs::new(self.table.clone(), self.options.sort_keys)
|
||||
.map_err(serde::ser::Error::custom)?;
|
||||
for kv in pairs {
|
||||
let (key, value) = kv.map_err(serde::ser::Error::custom)?;
|
||||
let mut serialize_err = None;
|
||||
let mut process_pair = |key, value| {
|
||||
let skip_key = check_value_for_skip(&key, self.options, &self.visited)
|
||||
.map_err(serde::ser::Error::custom)?;
|
||||
.map_err(|err| Error::SerializeError(err.to_string()))?;
|
||||
let skip_value = check_value_for_skip(&value, self.options, &self.visited)
|
||||
.map_err(serde::ser::Error::custom)?;
|
||||
.map_err(|err| Error::SerializeError(err.to_string()))?;
|
||||
if skip_key || skip_value {
|
||||
continue;
|
||||
// continue iteration
|
||||
return Ok(());
|
||||
}
|
||||
map.serialize_entry(
|
||||
&SerializableValue::new(&key, options, Some(visited)),
|
||||
&SerializableValue::new(&value, options, Some(visited)),
|
||||
)?;
|
||||
)
|
||||
.map_err(|err| {
|
||||
serialize_err = Some(err);
|
||||
Error::SerializeError(String::new())
|
||||
})
|
||||
};
|
||||
|
||||
let res = if !self.options.sort_keys {
|
||||
// Fast track
|
||||
self.table.for_each(process_pair)
|
||||
} else {
|
||||
MapPairs::new(self.table.clone(), self.options.sort_keys)
|
||||
.map_err(serde::ser::Error::custom)?
|
||||
.try_for_each(|kv| {
|
||||
let (key, value) = kv?;
|
||||
process_pair(key, value)
|
||||
})
|
||||
};
|
||||
match res {
|
||||
Ok(_) => {}
|
||||
Err(Error::SerializeError(_)) if serialize_err.is_some() => {
|
||||
return Err(serialize_err.unwrap());
|
||||
}
|
||||
Err(Error::SerializeError(msg)) => {
|
||||
return Err(serde::ser::Error::custom(msg));
|
||||
}
|
||||
Err(err) => {
|
||||
return Err(serde::ser::Error::custom(err.to_string()));
|
||||
}
|
||||
}
|
||||
map.end()
|
||||
}
|
||||
@@ -1129,16 +1184,15 @@ where
|
||||
lua.push_ref(&self.table);
|
||||
lua.push_value(prev_key)?;
|
||||
|
||||
let next = protect_lua!(state, 2, ffi::LUA_MULTRET, |state| {
|
||||
ffi::lua_next(state, -2)
|
||||
})?;
|
||||
if next != 0 {
|
||||
let value = lua.pop_value();
|
||||
let key = lua.pop_value();
|
||||
// It must be safe to call `lua_next` unprotected as deleting a key from a table is
|
||||
// a permitted operation.
|
||||
// It fails only if the key is not found (never existed) which seems impossible scenario.
|
||||
if ffi::lua_next(state, -2) != 0 {
|
||||
let key = lua.stack_value(-2);
|
||||
Ok(Some((
|
||||
key.clone(),
|
||||
K::from_lua(key, lua)?,
|
||||
V::from_lua(value, lua)?,
|
||||
V::from_stack(-1, lua)?,
|
||||
)))
|
||||
} else {
|
||||
Ok(None)
|
||||
@@ -1166,7 +1220,7 @@ where
|
||||
/// [`Table::sequence_values`]: crate::Table::sequence_values
|
||||
pub struct TableSequence<'lua, V> {
|
||||
table: LuaRef<'lua>,
|
||||
index: Option<Integer>,
|
||||
index: Integer,
|
||||
len: Option<Integer>,
|
||||
_phantom: PhantomData<V>,
|
||||
}
|
||||
@@ -1178,31 +1232,22 @@ where
|
||||
type Item = Result<V>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
if let Some(index) = self.index.take() {
|
||||
let lua = self.table.lua;
|
||||
let state = lua.state();
|
||||
|
||||
let res = (|| unsafe {
|
||||
let _sg = StackGuard::new(state);
|
||||
check_stack(state, 1)?;
|
||||
|
||||
lua.push_ref(&self.table);
|
||||
match ffi::lua_rawgeti(state, -1, index) {
|
||||
ffi::LUA_TNIL if index > self.len.unwrap_or(0) => Ok(None),
|
||||
_ => Ok(Some((index, lua.pop_value()))),
|
||||
}
|
||||
})();
|
||||
|
||||
match res {
|
||||
Ok(Some((index, r))) => {
|
||||
self.index = Some(index + 1);
|
||||
Some(V::from_lua(r, lua))
|
||||
}
|
||||
Ok(None) => None,
|
||||
Err(err) => Some(Err(err)),
|
||||
let lua = self.table.lua;
|
||||
let state = lua.state();
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(state);
|
||||
if let Err(err) = check_stack(state, 1) {
|
||||
return Some(Err(err));
|
||||
}
|
||||
|
||||
lua.push_ref(&self.table);
|
||||
match ffi::lua_rawgeti(state, -1, self.index) {
|
||||
ffi::LUA_TNIL if self.index > self.len.unwrap_or(0) => None,
|
||||
_ => {
|
||||
self.index += 1;
|
||||
Some(V::from_stack(-1, lua))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+3
-8
@@ -15,7 +15,7 @@ use crate::{
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
use {
|
||||
crate::{lua::ASYNC_POLL_PENDING, value::MultiValue},
|
||||
crate::value::MultiValue,
|
||||
futures_util::stream::Stream,
|
||||
std::{
|
||||
future::Future,
|
||||
@@ -239,7 +239,7 @@ impl<'lua> Thread<'lua> {
|
||||
///
|
||||
/// [Lua 5.4]: https://www.lua.org/manual/5.4/manual.html#lua_closethread
|
||||
#[cfg(any(feature = "lua54", feature = "luau"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(not(feature = "luau"))))]
|
||||
#[cfg_attr(docsrs, doc(cfg(any(feature = "lua54", feature = "luau"))))]
|
||||
pub fn reset(&self, func: crate::function::Function<'lua>) -> Result<()> {
|
||||
let lua = self.0.lua;
|
||||
let thread_state = self.state();
|
||||
@@ -530,12 +530,7 @@ where
|
||||
#[cfg(feature = "async")]
|
||||
#[inline(always)]
|
||||
unsafe fn is_poll_pending(state: *mut ffi::lua_State) -> bool {
|
||||
if ffi::lua_islightuserdata(state, -1) != 0 {
|
||||
let stack_ptr = ffi::lua_touserdata(state, -1) as *const u8;
|
||||
let pending_ptr = &ASYNC_POLL_PENDING as *const u8;
|
||||
return std::ptr::eq(stack_ptr, pending_ptr);
|
||||
}
|
||||
false
|
||||
ffi::lua_tolightuserdata(state, -1) == Lua::poll_pending().0
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
|
||||
@@ -29,6 +29,16 @@ pub type Integer = ffi::lua_Integer;
|
||||
/// Type of Lua floating point numbers.
|
||||
pub type Number = ffi::lua_Number;
|
||||
|
||||
// Represents different subtypes wrapped to AnyUserData
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
pub(crate) enum SubtypeId {
|
||||
None,
|
||||
#[cfg(feature = "luau")]
|
||||
Buffer,
|
||||
#[cfg(feature = "luajit")]
|
||||
CData,
|
||||
}
|
||||
|
||||
/// A "light" userdata value. Equivalent to an unmanaged raw pointer.
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
pub struct LightUserData(pub *mut c_void);
|
||||
|
||||
+15
-7
@@ -22,7 +22,7 @@ use crate::function::Function;
|
||||
use crate::lua::Lua;
|
||||
use crate::string::String;
|
||||
use crate::table::{Table, TablePairs};
|
||||
use crate::types::{LuaRef, MaybeSend};
|
||||
use crate::types::{LuaRef, MaybeSend, SubtypeId};
|
||||
use crate::util::{check_stack, get_userdata, take_userdata, StackGuard};
|
||||
use crate::value::{FromLua, FromLuaMulti, IntoLua, IntoLuaMulti, Value};
|
||||
use crate::UserDataRegistry;
|
||||
@@ -580,12 +580,12 @@ pub trait UserDataFields<'lua, T> {
|
||||
/// # use mlua::{Lua, Result, UserData};
|
||||
/// # fn main() -> Result<()> {
|
||||
/// # let lua = Lua::new();
|
||||
/// struct MyUserData(i32);
|
||||
/// struct MyUserData;
|
||||
///
|
||||
/// impl UserData for MyUserData {}
|
||||
///
|
||||
/// // `MyUserData` now implements `IntoLua`:
|
||||
/// lua.globals().set("myobject", MyUserData(123))?;
|
||||
/// lua.globals().set("myobject", MyUserData)?;
|
||||
///
|
||||
/// lua.load("assert(type(myobject) == 'userdata')").exec()?;
|
||||
/// # Ok(())
|
||||
@@ -791,7 +791,7 @@ impl<T> Deref for UserDataVariant<T> {
|
||||
/// [`is`]: crate::AnyUserData::is
|
||||
/// [`borrow`]: crate::AnyUserData::borrow
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct AnyUserData<'lua>(pub(crate) LuaRef<'lua>);
|
||||
pub struct AnyUserData<'lua>(pub(crate) LuaRef<'lua>, pub(crate) SubtypeId);
|
||||
|
||||
/// Owned handle to an internal Lua userdata.
|
||||
///
|
||||
@@ -801,14 +801,14 @@ pub struct AnyUserData<'lua>(pub(crate) LuaRef<'lua>);
|
||||
#[cfg(feature = "unstable")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "unstable")))]
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct OwnedAnyUserData(pub(crate) crate::types::LuaOwnedRef);
|
||||
pub struct OwnedAnyUserData(pub(crate) crate::types::LuaOwnedRef, pub(crate) SubtypeId);
|
||||
|
||||
#[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())
|
||||
AnyUserData(self.0.to_ref(), self.1)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1101,7 +1101,7 @@ impl<'lua> AnyUserData<'lua> {
|
||||
#[cfg_attr(docsrs, doc(cfg(all(feature = "unstable", not(feature = "send")))))]
|
||||
#[inline]
|
||||
pub fn into_owned(self) -> OwnedAnyUserData {
|
||||
OwnedAnyUserData(self.0.into_owned())
|
||||
OwnedAnyUserData(self.0.into_owned(), self.1)
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
@@ -1112,6 +1112,14 @@ impl<'lua> AnyUserData<'lua> {
|
||||
|
||||
/// Returns a type name of this `UserData` (from a metatable field).
|
||||
pub(crate) fn type_name(&self) -> Result<Option<StdString>> {
|
||||
match self.1 {
|
||||
SubtypeId::None => {}
|
||||
#[cfg(feature = "luau")]
|
||||
SubtypeId::Buffer => return Ok(Some("buffer".to_owned())),
|
||||
#[cfg(feature = "luajit")]
|
||||
SubtypeId::CData => return Ok(Some("cdata".to_owned())),
|
||||
}
|
||||
|
||||
let lua = self.0.lua;
|
||||
let state = lua.state();
|
||||
unsafe {
|
||||
|
||||
@@ -218,7 +218,7 @@ impl<'lua, T: 'static> UserDataRegistry<'lua, T> {
|
||||
MR: Future<Output = Result<R>> + 's,
|
||||
R: IntoLuaMulti<'lua>,
|
||||
{
|
||||
let name = get_function_name::<T>(name);
|
||||
let name = Arc::new(get_function_name::<T>(name));
|
||||
let method = Arc::new(method);
|
||||
|
||||
Box::new(move |lua, mut args| unsafe {
|
||||
@@ -312,7 +312,7 @@ impl<'lua, T: 'static> UserDataRegistry<'lua, T> {
|
||||
MR: Future<Output = Result<R>> + 's,
|
||||
R: IntoLuaMulti<'lua>,
|
||||
{
|
||||
let name = get_function_name::<T>(name);
|
||||
let name = Arc::new(get_function_name::<T>(name));
|
||||
let method = Arc::new(method);
|
||||
|
||||
Box::new(move |lua, mut args| unsafe {
|
||||
|
||||
+13
-32
@@ -6,7 +6,7 @@ use std::mem::MaybeUninit;
|
||||
use std::os::raw::{c_char, c_int, c_void};
|
||||
use std::panic::{catch_unwind, resume_unwind, AssertUnwindSafe};
|
||||
use std::sync::Arc;
|
||||
use std::{mem, ptr, slice, str};
|
||||
use std::{ptr, slice, str};
|
||||
|
||||
use once_cell::sync::Lazy;
|
||||
use rustc_hash::FxHashMap;
|
||||
@@ -282,38 +282,23 @@ pub unsafe fn rawset_field(state: *mut ffi::lua_State, table: c_int, field: &str
|
||||
}
|
||||
|
||||
// Internally uses 3 stack spaces, does not call checkstack.
|
||||
#[cfg(not(feature = "luau"))]
|
||||
#[inline]
|
||||
pub unsafe fn push_userdata<T>(state: *mut ffi::lua_State, t: T, protect: bool) -> Result<()> {
|
||||
#[cfg(not(feature = "luau"))]
|
||||
let ud = if protect {
|
||||
protect_lua!(state, 0, 1, |state| {
|
||||
ffi::lua_newuserdata(state, mem::size_of::<T>()) as *mut T
|
||||
ffi::lua_newuserdata(state, std::mem::size_of::<T>()) as *mut T
|
||||
})?
|
||||
} else {
|
||||
ffi::lua_newuserdata(state, mem::size_of::<T>()) as *mut T
|
||||
ffi::lua_newuserdata(state, std::mem::size_of::<T>()) as *mut T
|
||||
};
|
||||
ptr::write(ud, t);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Internally uses 3 stack spaces, does not call checkstack.
|
||||
#[cfg(feature = "luau")]
|
||||
#[inline]
|
||||
pub unsafe fn push_userdata<T>(state: *mut ffi::lua_State, t: T, protect: bool) -> Result<()> {
|
||||
unsafe extern "C-unwind" fn destructor<T>(ud: *mut c_void) {
|
||||
ptr::drop_in_place(ud as *mut T);
|
||||
}
|
||||
|
||||
let size = mem::size_of::<T>();
|
||||
#[cfg(feature = "luau")]
|
||||
let ud = if protect {
|
||||
protect_lua!(state, 0, 1, |state| {
|
||||
ffi::lua_newuserdatadtor(state, size, destructor::<T>) as *mut T
|
||||
})?
|
||||
protect_lua!(state, 0, 1, |state| { ffi::lua_newuserdata_t::<T>(state) })?
|
||||
} else {
|
||||
ffi::lua_newuserdatadtor(state, size, destructor::<T>) as *mut T
|
||||
ffi::lua_newuserdata_t::<T>(state)
|
||||
};
|
||||
ptr::write(ud, t);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -328,10 +313,10 @@ pub unsafe fn push_userdata_uv<T>(
|
||||
) -> Result<()> {
|
||||
let ud = if protect {
|
||||
protect_lua!(state, 0, 1, |state| {
|
||||
ffi::lua_newuserdatauv(state, mem::size_of::<T>(), nuvalue) as *mut T
|
||||
ffi::lua_newuserdatauv(state, std::mem::size_of::<T>(), nuvalue) as *mut T
|
||||
})?
|
||||
} else {
|
||||
ffi::lua_newuserdatauv(state, mem::size_of::<T>(), nuvalue) as *mut T
|
||||
ffi::lua_newuserdatauv(state, std::mem::size_of::<T>(), nuvalue) as *mut T
|
||||
};
|
||||
ptr::write(ud, t);
|
||||
Ok(())
|
||||
@@ -1009,16 +994,10 @@ pub(crate) enum WrappedFailure {
|
||||
|
||||
impl WrappedFailure {
|
||||
pub(crate) unsafe fn new_userdata(state: *mut ffi::lua_State) -> *mut Self {
|
||||
let size = mem::size_of::<WrappedFailure>();
|
||||
#[cfg(feature = "luau")]
|
||||
let ud = {
|
||||
unsafe extern "C-unwind" fn destructor(p: *mut c_void) {
|
||||
ptr::drop_in_place(p as *mut WrappedFailure);
|
||||
}
|
||||
ffi::lua_newuserdatadtor(state, size, destructor) as *mut Self
|
||||
};
|
||||
let ud = ffi::lua_newuserdata_t::<Self>(state);
|
||||
#[cfg(not(feature = "luau"))]
|
||||
let ud = ffi::lua_newuserdata(state, size) as *mut Self;
|
||||
let ud = ffi::lua_newuserdata(state, std::mem::size_of::<Self>()) as *mut Self;
|
||||
ptr::write(ud, WrappedFailure::None);
|
||||
ud
|
||||
}
|
||||
@@ -1064,6 +1043,8 @@ pub(crate) unsafe fn to_string(state: *mut ffi::lua_State, index: c_int) -> Stri
|
||||
ffi::LUA_TFUNCTION => format!("<function {:?}>", ffi::lua_topointer(state, index)),
|
||||
ffi::LUA_TUSERDATA => format!("<userdata {:?}>", ffi::lua_topointer(state, index)),
|
||||
ffi::LUA_TTHREAD => format!("<thread {:?}>", ffi::lua_topointer(state, index)),
|
||||
#[cfg(feature = "luau")]
|
||||
ffi::LUA_TBUFFER => format!("<buffer {:?}>", ffi::lua_topointer(state, index)),
|
||||
_ => "<unknown>".to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
+30
-4
@@ -24,7 +24,7 @@ use crate::lua::Lua;
|
||||
use crate::string::String;
|
||||
use crate::table::Table;
|
||||
use crate::thread::Thread;
|
||||
use crate::types::{Integer, LightUserData, Number};
|
||||
use crate::types::{Integer, LightUserData, Number, SubtypeId};
|
||||
use crate::userdata::AnyUserData;
|
||||
use crate::util::{check_stack, StackGuard};
|
||||
|
||||
@@ -88,7 +88,11 @@ impl<'lua> Value<'lua> {
|
||||
Value::Table(_) => "table",
|
||||
Value::Function(_) => "function",
|
||||
Value::Thread(_) => "thread",
|
||||
Value::UserData(_) => "userdata",
|
||||
Value::UserData(AnyUserData(_, SubtypeId::None)) => "userdata",
|
||||
#[cfg(feature = "luau")]
|
||||
Value::UserData(AnyUserData(_, SubtypeId::Buffer)) => "buffer",
|
||||
#[cfg(feature = "luajit")]
|
||||
Value::UserData(AnyUserData(_, SubtypeId::CData)) => "cdata",
|
||||
Value::Error(_) => "error",
|
||||
}
|
||||
}
|
||||
@@ -126,7 +130,7 @@ impl<'lua> Value<'lua> {
|
||||
| Value::Table(Table(r))
|
||||
| Value::Function(Function(r))
|
||||
| Value::Thread(Thread(r, ..))
|
||||
| Value::UserData(AnyUserData(r)) => r.to_pointer(),
|
||||
| Value::UserData(AnyUserData(r, ..)) => r.to_pointer(),
|
||||
_ => ptr::null(),
|
||||
}
|
||||
}
|
||||
@@ -148,7 +152,7 @@ impl<'lua> Value<'lua> {
|
||||
Value::Table(Table(r))
|
||||
| Value::Function(Function(r))
|
||||
| Value::Thread(Thread(r, ..))
|
||||
| Value::UserData(AnyUserData(r)) => unsafe {
|
||||
| Value::UserData(AnyUserData(r, ..)) => unsafe {
|
||||
let state = r.lua.state();
|
||||
let _guard = StackGuard::new(state);
|
||||
check_stack(state, 3)?;
|
||||
@@ -410,6 +414,28 @@ impl<'lua> Value<'lua> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `true` if the value is a Buffer wrapped in [`AnyUserData`].
|
||||
#[cfg(any(feature = "luau", doc))]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
|
||||
#[doc(hidden)]
|
||||
#[inline]
|
||||
pub fn is_buffer(&self) -> bool {
|
||||
self.as_userdata()
|
||||
.map(|ud| ud.1 == SubtypeId::Buffer)
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
/// Returns `true` if the value is a CData wrapped in [`AnyUserData`].
|
||||
#[cfg(any(feature = "luajit", doc))]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "luajit")))]
|
||||
#[doc(hidden)]
|
||||
#[inline]
|
||||
pub fn is_cdata(&self) -> bool {
|
||||
self.as_userdata()
|
||||
.map(|ud| ud.1 == SubtypeId::CData)
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
/// Wrap reference to this Value into [`SerializableValue`].
|
||||
///
|
||||
/// This allows customizing serialization behavior using serde.
|
||||
|
||||
+8
-1
@@ -10,10 +10,17 @@ use mlua::{
|
||||
UserData, UserDataMethods, Value,
|
||||
};
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
async fn sleep_ms(ms: u64) {
|
||||
tokio::time::sleep(Duration::from_millis(ms)).await;
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
async fn sleep_ms(_ms: u64) {
|
||||
// I was unable to make sleep() work in wasm32-emscripten target
|
||||
tokio::task::yield_now().await;
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_async_function() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
@@ -436,7 +443,7 @@ async fn test_async_userdata() -> Result<()> {
|
||||
let globals = lua.globals();
|
||||
|
||||
let userdata = lua.create_userdata(MyUserData(11))?;
|
||||
globals.set("userdata", userdata.clone())?;
|
||||
globals.set("userdata", &userdata)?;
|
||||
|
||||
lua.load(
|
||||
r#"
|
||||
|
||||
@@ -7,6 +7,12 @@ use mlua::{Lua, Result};
|
||||
fn test_chunk_path() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
if cfg!(target_arch = "wasm32") {
|
||||
// TODO: figure out why emscripten fails on file operations
|
||||
// Also see https://github.com/rust-lang/rust/issues/119250
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let temp_dir = tempfile::tempdir().unwrap();
|
||||
fs::write(
|
||||
temp_dir.path().join("module.lua"),
|
||||
|
||||
@@ -4,7 +4,7 @@ error: lifetime may not live long enough
|
||||
9 | reg.add_async_method("t", |_, this: &String, ()| async {
|
||||
| ___________________________________----------------------_^
|
||||
| | | |
|
||||
| | | return type of closure `[async block@$DIR/tests/compile/async_any_userdata_method.rs:9:58: 12:10]` contains a lifetime `'2`
|
||||
| | | return type of closure `{async block@$DIR/tests/compile/async_any_userdata_method.rs:9:58: 12:10}` contains a lifetime `'2`
|
||||
| | lifetime `'1` represents this closure's body
|
||||
10 | | s = this;
|
||||
11 | | Ok(())
|
||||
@@ -27,6 +27,8 @@ error[E0596]: cannot borrow `s` as mutable, as it is a captured variable in a `F
|
||||
error[E0597]: `s` does not live long enough
|
||||
--> tests/compile/async_any_userdata_method.rs:8:21
|
||||
|
|
||||
7 | let s = String::new();
|
||||
| - binding `s` declared here
|
||||
8 | let mut s = &s;
|
||||
| ^^ borrowed value does not live long enough
|
||||
9 | / reg.add_async_method("t", |_, this: &String, ()| async {
|
||||
|
||||
@@ -7,14 +7,46 @@ error[E0277]: the type `UnsafeCell<mlua::lua::ExtraData>` may contain interior m
|
||||
| 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 because it appears within the type `ArcInner<UnsafeCell<ExtraData>>`
|
||||
--> $RUST/alloc/src/sync.rs
|
||||
|
|
||||
| struct ArcInner<T: ?Sized> {
|
||||
| ^^^^^^^^
|
||||
note: required because it appears within the type `PhantomData<ArcInner<UnsafeCell<ExtraData>>>`
|
||||
--> $RUST/core/src/marker.rs
|
||||
|
|
||||
| pub struct PhantomData<T: ?Sized>;
|
||||
| ^^^^^^^^^^^
|
||||
note: required because it appears within the type `Arc<UnsafeCell<ExtraData>>`
|
||||
--> $RUST/alloc/src/sync.rs
|
||||
|
|
||||
| pub struct Arc<
|
||||
| ^^^
|
||||
note: required because it appears within the type `LuaInner`
|
||||
--> src/lua.rs
|
||||
|
|
||||
| pub struct LuaInner {
|
||||
| ^^^^^^^^
|
||||
note: required because it appears within the type `ArcInner<LuaInner>`
|
||||
--> $RUST/alloc/src/sync.rs
|
||||
|
|
||||
| struct ArcInner<T: ?Sized> {
|
||||
| ^^^^^^^^
|
||||
note: required because it appears within the type `PhantomData<ArcInner<LuaInner>>`
|
||||
--> $RUST/core/src/marker.rs
|
||||
|
|
||||
| pub struct PhantomData<T: ?Sized>;
|
||||
| ^^^^^^^^^^^
|
||||
note: required because it appears within the type `Arc<LuaInner>`
|
||||
--> $RUST/alloc/src/sync.rs
|
||||
|
|
||||
| pub struct Arc<
|
||||
| ^^^
|
||||
note: required because it appears within the type `Lua`
|
||||
--> src/lua.rs
|
||||
|
|
||||
| pub struct Lua(Arc<LuaInner>);
|
||||
| ^^^
|
||||
= note: required for `&Lua` to implement `UnwindSafe`
|
||||
note: required because it's used within this closure
|
||||
--> tests/compile/lua_norefunwindsafe.rs:7:18
|
||||
|
||||
@@ -4,22 +4,25 @@ error[E0277]: `Rc<Cell<i32>>` cannot be sent between threads safely
|
||||
11 | lua.create_function(move |_, ()| {
|
||||
| --------------- ^-----------
|
||||
| | |
|
||||
| _________|_______________within this `[closure@$DIR/tests/compile/non_send.rs:11:25: 11:37]`
|
||||
| _________|_______________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>>`
|
||||
= 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 |_, ()| {
|
||||
| ^^^^^^^^^^^^
|
||||
= note: required for `[closure@$DIR/tests/compile/non_send.rs:11:25: 11:37]` to implement `mlua::types::MaybeSend`
|
||||
= note: required for `{closure@$DIR/tests/compile/non_send.rs:11:25: 11:37}` to implement `mlua::types::MaybeSend`
|
||||
note: required by a bound in `Lua::create_function`
|
||||
--> src/lua.rs
|
||||
|
|
||||
| pub fn create_function<'lua, A, R, F>(&'lua self, func: F) -> Result<Function<'lua>>
|
||||
| --------------- required by a bound in this associated function
|
||||
...
|
||||
| F: Fn(&'lua Lua, A) -> Result<R> + MaybeSend + 'static,
|
||||
| ^^^^^^^^^ required by this bound in `Lua::create_function`
|
||||
|
||||
@@ -7,17 +7,57 @@ error[E0277]: the type `UnsafeCell<mlua::lua::ExtraData>` may contain interior m
|
||||
| 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 because it appears within the type `ArcInner<UnsafeCell<ExtraData>>`
|
||||
--> $RUST/alloc/src/sync.rs
|
||||
|
|
||||
| struct ArcInner<T: ?Sized> {
|
||||
| ^^^^^^^^
|
||||
note: required because it appears within the type `PhantomData<ArcInner<UnsafeCell<ExtraData>>>`
|
||||
--> $RUST/core/src/marker.rs
|
||||
|
|
||||
| pub struct PhantomData<T: ?Sized>;
|
||||
| ^^^^^^^^^^^
|
||||
note: required because it appears within the type `Arc<UnsafeCell<ExtraData>>`
|
||||
--> $RUST/alloc/src/sync.rs
|
||||
|
|
||||
| pub struct Arc<
|
||||
| ^^^
|
||||
note: required because it appears within the type `LuaInner`
|
||||
--> src/lua.rs
|
||||
|
|
||||
| pub struct LuaInner {
|
||||
| ^^^^^^^^
|
||||
note: required because it appears within the type `ArcInner<LuaInner>`
|
||||
--> $RUST/alloc/src/sync.rs
|
||||
|
|
||||
| struct ArcInner<T: ?Sized> {
|
||||
| ^^^^^^^^
|
||||
note: required because it appears within the type `PhantomData<ArcInner<LuaInner>>`
|
||||
--> $RUST/core/src/marker.rs
|
||||
|
|
||||
| pub struct PhantomData<T: ?Sized>;
|
||||
| ^^^^^^^^^^^
|
||||
note: required because it appears within the type `Arc<LuaInner>`
|
||||
--> $RUST/alloc/src/sync.rs
|
||||
|
|
||||
| pub struct Arc<
|
||||
| ^^^
|
||||
note: required because it appears within the type `Lua`
|
||||
--> src/lua.rs
|
||||
|
|
||||
| pub struct Lua(Arc<LuaInner>);
|
||||
| ^^^
|
||||
= note: required for `&Lua` to implement `UnwindSafe`
|
||||
= note: required because it appears within the type `LuaRef<'_>`
|
||||
= note: required because it appears within the type `Table<'_>`
|
||||
note: required because it appears within the type `LuaRef<'_>`
|
||||
--> src/types.rs
|
||||
|
|
||||
| pub(crate) struct LuaRef<'lua> {
|
||||
| ^^^^^^
|
||||
note: required because it appears within the type `Table<'_>`
|
||||
--> src/table.rs
|
||||
|
|
||||
| pub struct Table<'lua>(pub(crate) LuaRef<'lua>);
|
||||
| ^^^^^
|
||||
note: required because it's used within this closure
|
||||
--> tests/compile/ref_nounwindsafe.rs:8:18
|
||||
|
|
||||
|
||||
@@ -4,6 +4,8 @@ error[E0597]: `ibad` does not live long enough
|
||||
11 | lua.scope(|scope| {
|
||||
| ----- has type `&mlua::Scope<'_, '1>`
|
||||
...
|
||||
14 | let ibad = 42;
|
||||
| ---- binding `ibad` declared here
|
||||
15 | scope.create_nonstatic_userdata(MyUserData(&ibad)).unwrap();
|
||||
| -------------------------------------------^^^^^--
|
||||
| | |
|
||||
|
||||
@@ -1,35 +1,31 @@
|
||||
error[E0597]: `lua` does not live long enough
|
||||
--> tests/compile/static_callback_args.rs:12:5
|
||||
|
|
||||
10 | let lua = Lua::new();
|
||||
| --- binding `lua` declared here
|
||||
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
|
||||
12 | lua.create_function(|_, table: Table| {
|
||||
| ^^^ borrowed value does not live long enough
|
||||
13 | / BAD_TIME.with(|bt| {
|
||||
14 | | *bt.borrow_mut() = Some(table);
|
||||
15 | | });
|
||||
| |__________- argument requires that `lua` is borrowed for `'static`
|
||||
...
|
||||
32 | }
|
||||
| - `lua` dropped here while still borrowed
|
||||
32 | }
|
||||
| - `lua` dropped here while still borrowed
|
||||
|
||||
error[E0505]: cannot move out of `lua` because it is borrowed
|
||||
--> tests/compile/static_callback_args.rs:22:10
|
||||
|
|
||||
10 | let lua = Lua::new();
|
||||
| --- binding `lua` declared here
|
||||
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
|
||||
12 | lua.create_function(|_, table: Table| {
|
||||
| --- borrow of `lua` occurs here
|
||||
13 | / BAD_TIME.with(|bt| {
|
||||
14 | | *bt.borrow_mut() = Some(table);
|
||||
15 | | });
|
||||
| |__________- argument requires that `lua` is borrowed for `'static`
|
||||
...
|
||||
22 | drop(lua);
|
||||
| ^^^ move out of `lua` occurs here
|
||||
22 | drop(lua);
|
||||
| ^^^ move out of `lua` occurs here
|
||||
|
||||
+233
-1
@@ -3,7 +3,239 @@ use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet};
|
||||
use std::ffi::{CStr, CString};
|
||||
|
||||
use maplit::{btreemap, btreeset, hashmap, hashset};
|
||||
use mlua::{Error, Lua, Result};
|
||||
use mlua::{AnyUserData, Error, Function, IntoLua, Lua, Result, Table, Thread, UserDataRef, Value};
|
||||
|
||||
#[test]
|
||||
fn test_string_into_lua() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
// Direct conversion
|
||||
let s = lua.create_string("hello, world!")?;
|
||||
let s2 = (&s).into_lua(&lua)?;
|
||||
assert_eq!(s, s2.as_string().unwrap());
|
||||
|
||||
// Push into stack
|
||||
let table = lua.create_table()?;
|
||||
table.set("s", &s)?;
|
||||
assert_eq!(s, table.get::<_, String>("s")?);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "unstable", not(feature = "send")))]
|
||||
#[test]
|
||||
fn test_owned_string_into_lua() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
// Direct conversion
|
||||
let s = lua.create_string("hello, world")?.into_owned();
|
||||
let s2 = (&s).into_lua(&lua)?;
|
||||
assert_eq!(s.to_ref(), *s2.as_string().unwrap());
|
||||
|
||||
// Push into stack
|
||||
let table = lua.create_table()?;
|
||||
table.set("s", &s)?;
|
||||
assert_eq!(s.to_ref(), table.get::<_, String>("s")?);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "unstable", not(feature = "send")))]
|
||||
#[test]
|
||||
fn test_owned_string_from_lua() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let s = lua.unpack::<mlua::OwnedString>(lua.pack("hello, world")?)?;
|
||||
assert_eq!(s.to_ref(), "hello, world");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_table_into_lua() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
// Direct conversion
|
||||
let t = lua.create_table()?;
|
||||
let t2 = (&t).into_lua(&lua)?;
|
||||
assert_eq!(&t, t2.as_table().unwrap());
|
||||
|
||||
// Push into stack
|
||||
let f = lua.create_function(|_, (t, s): (Table, String)| t.set("s", s))?;
|
||||
f.call((&t, "hello"))?;
|
||||
assert_eq!("hello", t.get::<_, String>("s")?);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "unstable", not(feature = "send")))]
|
||||
#[test]
|
||||
fn test_owned_table_into_lua() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
// Direct conversion
|
||||
let t = lua.create_table()?.into_owned();
|
||||
let t2 = (&t).into_lua(&lua)?;
|
||||
assert_eq!(t.to_ref(), *t2.as_table().unwrap());
|
||||
|
||||
// Push into stack
|
||||
let f = lua.create_function(|_, (t, s): (Table, String)| t.set("s", s))?;
|
||||
f.call((&t, "hello"))?;
|
||||
assert_eq!("hello", t.to_ref().get::<_, String>("s")?);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_function_into_lua() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
// Direct conversion
|
||||
let f = lua.create_function(|_, ()| Ok::<_, Error>(()))?;
|
||||
let f2 = (&f).into_lua(&lua)?;
|
||||
assert_eq!(&f, f2.as_function().unwrap());
|
||||
|
||||
// Push into stack
|
||||
let table = lua.create_table()?;
|
||||
table.set("f", &f)?;
|
||||
assert_eq!(f, table.get::<_, Function>("f")?);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "unstable", not(feature = "send")))]
|
||||
#[test]
|
||||
fn test_owned_function_into_lua() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
// Direct conversion
|
||||
let f = lua
|
||||
.create_function(|_, ()| Ok::<_, Error>(()))?
|
||||
.into_owned();
|
||||
let f2 = (&f).into_lua(&lua)?;
|
||||
assert_eq!(f.to_ref(), *f2.as_function().unwrap());
|
||||
|
||||
// Push into stack
|
||||
let table = lua.create_table()?;
|
||||
table.set("f", &f)?;
|
||||
assert_eq!(f.to_ref(), table.get::<_, Function>("f")?);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_thread_into_lua() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
// Direct conversion
|
||||
let f = lua.create_function(|_, ()| Ok::<_, Error>(()))?;
|
||||
let th = lua.create_thread(f)?;
|
||||
let th2 = (&th).into_lua(&lua)?;
|
||||
assert_eq!(&th, th2.as_thread().unwrap());
|
||||
|
||||
// Push into stack
|
||||
let table = lua.create_table()?;
|
||||
table.set("th", &th)?;
|
||||
assert_eq!(th, table.get::<_, Thread>("th")?);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "unstable", not(feature = "send")))]
|
||||
#[test]
|
||||
fn test_owned_thread_into_lua() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
// Direct conversion
|
||||
let f = lua.create_function(|_, ()| Ok::<_, Error>(()))?;
|
||||
let th = lua.create_thread(f)?.into_owned();
|
||||
let th2 = (&th).into_lua(&lua)?;
|
||||
assert_eq!(&th.to_ref(), th2.as_thread().unwrap());
|
||||
|
||||
// Push into stack
|
||||
let table = lua.create_table()?;
|
||||
table.set("th", &th)?;
|
||||
assert_eq!(th.to_ref(), table.get::<_, Thread>("th")?);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "unstable", not(feature = "send")))]
|
||||
#[test]
|
||||
fn test_owned_thread_from_lua() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let th = lua.unpack::<mlua::OwnedThread>(Value::Thread(lua.current_thread()))?;
|
||||
assert_eq!(th.to_ref(), lua.current_thread());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_anyuserdata_into_lua() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
// Direct conversion
|
||||
let ud = lua.create_any_userdata(String::from("hello"))?;
|
||||
let ud2 = (&ud).into_lua(&lua)?;
|
||||
assert_eq!(&ud, ud2.as_userdata().unwrap());
|
||||
|
||||
// Push into stack
|
||||
let table = lua.create_table()?;
|
||||
table.set("ud", &ud)?;
|
||||
assert_eq!(ud, table.get::<_, AnyUserData>("ud")?);
|
||||
assert_eq!("hello", *table.get::<_, UserDataRef<String>>("ud")?);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "unstable", not(feature = "send")))]
|
||||
#[test]
|
||||
fn test_owned_anyuserdata_into_lua() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
// Direct conversion
|
||||
let ud = lua.create_any_userdata(String::from("hello"))?.into_owned();
|
||||
let ud2 = (&ud).into_lua(&lua)?;
|
||||
assert_eq!(ud.to_ref(), *ud2.as_userdata().unwrap());
|
||||
|
||||
// Push into stack
|
||||
let table = lua.create_table()?;
|
||||
table.set("ud", &ud)?;
|
||||
assert_eq!(ud.to_ref(), table.get::<_, AnyUserData>("ud")?);
|
||||
assert_eq!("hello", *table.get::<_, UserDataRef<String>>("ud")?);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_registry_value_into_lua() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let t = lua.create_table()?;
|
||||
let r = lua.create_registry_value(t)?;
|
||||
let f = lua.create_function(|_, t: Table| t.raw_set("hello", "world"))?;
|
||||
|
||||
f.call(&r)?;
|
||||
let v = r.into_lua(&lua)?;
|
||||
let t = v.as_table().unwrap();
|
||||
assert_eq!(t.get::<_, String>("hello")?, "world");
|
||||
|
||||
// Try to set nil registry key
|
||||
let r_nil = lua.create_registry_value(Value::Nil)?;
|
||||
t.set("hello", &r_nil)?;
|
||||
assert_eq!(t.get::<_, Value>("hello")?, Value::Nil);
|
||||
|
||||
// Check non-owned registry key
|
||||
let lua2 = Lua::new();
|
||||
let r2 = lua2.create_registry_value("abc")?;
|
||||
assert!(matches!(
|
||||
f.call::<_, ()>(&r2),
|
||||
Err(Error::MismatchedRegistryKey)
|
||||
));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_conv_vec() -> Result<()> {
|
||||
|
||||
+3
-1
@@ -41,7 +41,9 @@ fn test_error_context() -> Result<()> {
|
||||
.context("some new context")
|
||||
})?;
|
||||
let res = func3.call::<_, ()>(()).err().unwrap();
|
||||
let Error::CallbackError { cause, .. } = &res else { unreachable!() };
|
||||
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());
|
||||
|
||||
+4
-2
@@ -1,6 +1,5 @@
|
||||
#![cfg(not(feature = "luau"))]
|
||||
|
||||
use std::cell::RefCell;
|
||||
use std::ops::Deref;
|
||||
use std::sync::atomic::{AtomicI64, Ordering};
|
||||
use std::sync::{Arc, Mutex};
|
||||
@@ -170,10 +169,13 @@ fn test_hook_removal() -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Having the code compiled (even not run) on macos and luajit causes a memory reference issue
|
||||
// See https://github.com/LuaJIT/LuaJIT/issues/1099
|
||||
#[cfg(not(all(feature = "luajit", target_os = "macos")))]
|
||||
#[test]
|
||||
fn test_hook_swap_within_hook() -> Result<()> {
|
||||
thread_local! {
|
||||
static TL_LUA: RefCell<Option<Lua>> = RefCell::new(None);
|
||||
static TL_LUA: std::cell::RefCell<Option<Lua>> = Default::default();
|
||||
}
|
||||
|
||||
TL_LUA.with(|tl| {
|
||||
|
||||
+66
-5
@@ -1,6 +1,5 @@
|
||||
#![cfg(feature = "luau")]
|
||||
|
||||
use std::env;
|
||||
use std::fmt::Debug;
|
||||
use std::fs;
|
||||
use std::panic::{catch_unwind, AssertUnwindSafe};
|
||||
@@ -8,7 +7,8 @@ use std::sync::atomic::{AtomicU64, Ordering};
|
||||
use std::sync::Arc;
|
||||
|
||||
use mlua::{
|
||||
Compiler, CoverageInfo, Error, Lua, Result, Table, ThreadStatus, Value, Vector, VmState,
|
||||
Compiler, CoverageInfo, Error, Lua, LuaOptions, Result, StdLib, Table, ThreadStatus, Value,
|
||||
Vector, VmState,
|
||||
};
|
||||
|
||||
#[test]
|
||||
@@ -23,7 +23,18 @@ fn test_version() -> Result<()> {
|
||||
|
||||
#[test]
|
||||
fn test_require() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
// Ensure that require() is not available if package module is not loaded
|
||||
let mut lua = Lua::new_with(StdLib::NONE, LuaOptions::default())?;
|
||||
assert!(lua.globals().get::<_, Option<Value>>("require")?.is_none());
|
||||
assert!(lua.globals().get::<_, Option<Value>>("package")?.is_none());
|
||||
|
||||
if cfg!(target_arch = "wasm32") {
|
||||
// TODO: figure out why emscripten fails on file operations
|
||||
// Also see https://github.com/rust-lang/rust/issues/119250
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
lua = Lua::new();
|
||||
|
||||
let temp_dir = tempfile::tempdir().unwrap();
|
||||
fs::write(
|
||||
@@ -37,7 +48,10 @@ fn test_require() -> Result<()> {
|
||||
"#,
|
||||
)?;
|
||||
|
||||
env::set_var("LUAU_PATH", temp_dir.path().join("?.luau"));
|
||||
lua.globals()
|
||||
.get::<_, Table>("package")?
|
||||
.set("path", temp_dir.path().join("?.luau").to_string_lossy())?;
|
||||
|
||||
lua.load(
|
||||
r#"
|
||||
local module = require("module")
|
||||
@@ -49,7 +63,28 @@ fn test_require() -> Result<()> {
|
||||
assert(not ok and string.find(err, "module.luau") ~= nil)
|
||||
"#,
|
||||
)
|
||||
.exec()
|
||||
.exec()?;
|
||||
|
||||
// Require non-existent module
|
||||
match lua.load("require('non-existent')").exec() {
|
||||
Err(Error::RuntimeError(e)) if e.contains("module 'non-existent' not found") => {}
|
||||
r => panic!("expected RuntimeError(...) with a specific message, got {r:?}"),
|
||||
}
|
||||
|
||||
// Require binary module in safe mode
|
||||
lua.globals()
|
||||
.get::<_, Table>("package")?
|
||||
.set("cpath", temp_dir.path().join("?.so").to_string_lossy())?;
|
||||
fs::write(temp_dir.path().join("dylib.so"), "")?;
|
||||
match lua.load("require('dylib')").exec() {
|
||||
Err(Error::RuntimeError(e)) if cfg!(unix) && e.contains("module 'dylib' not found") => {
|
||||
assert!(e.contains("dynamic libraries are disabled in safe mode"))
|
||||
}
|
||||
Err(Error::RuntimeError(e)) if e.contains("module 'dylib' not found") => {}
|
||||
r => panic!("expected RuntimeError(...) with a specific message, got {r:?}"),
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "luau-vector4"))]
|
||||
@@ -407,3 +442,29 @@ fn test_coverage() -> Result<()> {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_buffer() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let buf1 = lua
|
||||
.load(
|
||||
r#"
|
||||
local buf = buffer.fromstring("hello")
|
||||
assert(buffer.len(buf) == 5)
|
||||
return buf
|
||||
"#,
|
||||
)
|
||||
.eval::<Value>()?;
|
||||
assert!(buf1.is_userdata() && buf1.is_buffer());
|
||||
assert_eq!(buf1.type_name(), "buffer");
|
||||
|
||||
let buf2 = lua.load("buffer.fromstring('hello')").eval::<Value>()?;
|
||||
assert_ne!(buf1, buf2);
|
||||
|
||||
// Check that we can pass buffer type to Lua
|
||||
let func = lua.create_function(|_, buf: Value| return buf.to_string())?;
|
||||
assert!(func.call::<_, String>(buf1)?.starts_with("buffer:"));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ lua53 = ["mlua/lua53"]
|
||||
lua52 = ["mlua/lua52"]
|
||||
lua51 = ["mlua/lua51"]
|
||||
luajit = ["mlua/luajit"]
|
||||
luau = ["mlua/luau"]
|
||||
|
||||
[dependencies]
|
||||
mlua = { path = "../..", features = ["module"] }
|
||||
|
||||
@@ -10,6 +10,7 @@ lua53 = ["mlua/lua53"]
|
||||
lua52 = ["mlua/lua52"]
|
||||
lua51 = ["mlua/lua51"]
|
||||
luajit = ["mlua/luajit"]
|
||||
luau = ["mlua/luau"]
|
||||
vendored = ["mlua/vendored"]
|
||||
|
||||
[dependencies]
|
||||
|
||||
@@ -4,7 +4,7 @@ use std::path::PathBuf;
|
||||
use mlua::{Lua, Result};
|
||||
|
||||
#[test]
|
||||
fn test_module() -> Result<()> {
|
||||
fn test_module_simple() -> Result<()> {
|
||||
let lua = make_lua()?;
|
||||
lua.load(
|
||||
r#"
|
||||
@@ -59,7 +59,8 @@ fn test_module_from_thread() -> Result<()> {
|
||||
assert(mod.sum(a, b) == a + b)
|
||||
end)
|
||||
|
||||
coroutine.resume(co, 3, 5)
|
||||
local ok, err = coroutine.resume(co, 3, 5)
|
||||
assert(ok, err)
|
||||
collectgarbage()
|
||||
|
||||
assert(mod.used_memory() > 0)
|
||||
@@ -68,6 +69,29 @@ fn test_module_from_thread() -> Result<()> {
|
||||
.exec()
|
||||
}
|
||||
|
||||
#[cfg(any(
|
||||
feature = "lua54",
|
||||
feature = "lua53",
|
||||
feature = "lua52",
|
||||
feature = "lua51"
|
||||
))]
|
||||
#[test]
|
||||
fn test_module_multi_from_thread() -> Result<()> {
|
||||
let lua = make_lua()?;
|
||||
lua.load(
|
||||
r#"
|
||||
local mod = require("test_module")
|
||||
local co = coroutine.create(function()
|
||||
local mod2 = require("test_module.second")
|
||||
assert(mod2.userdata ~= nil)
|
||||
end)
|
||||
local ok, err = coroutine.resume(co)
|
||||
assert(ok, err)
|
||||
"#,
|
||||
)
|
||||
.exec()
|
||||
}
|
||||
|
||||
fn make_lua() -> Result<Lua> {
|
||||
let (dylib_path, dylib_ext, separator);
|
||||
if cfg!(target_os = "macos") {
|
||||
|
||||
+1
-1
@@ -598,7 +598,7 @@ fn test_from_value_with_options() -> Result<(), Box<dyn StdError>> {
|
||||
|
||||
// Check recursion when using `Serialize` impl
|
||||
let t = lua.create_table()?;
|
||||
t.set("t", t.clone())?;
|
||||
t.set("t", &t)?;
|
||||
assert!(serde_json::to_string(&t).is_err());
|
||||
|
||||
// Serialize Lua globals table
|
||||
|
||||
+11
-2
@@ -73,13 +73,22 @@ fn test_static_lua_coroutine() -> Result<()> {
|
||||
async fn test_static_async() -> Result<()> {
|
||||
let lua = Lua::new().into_static();
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
async fn sleep_ms(ms: u64) {
|
||||
tokio::time::sleep(std::time::Duration::from_millis(ms)).await;
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
async fn sleep_ms(_ms: u64) {
|
||||
tokio::task::yield_now().await;
|
||||
}
|
||||
|
||||
let timer =
|
||||
lua.create_async_function(|_, (i, n, f): (u64, u64, mlua::Function)| async move {
|
||||
tokio::task::spawn_local(async move {
|
||||
let dur = std::time::Duration::from_millis(i);
|
||||
for _ in 0..n {
|
||||
tokio::task::spawn_local(f.call_async::<(), ()>(()));
|
||||
tokio::time::sleep(dur).await;
|
||||
sleep_ms(i).await;
|
||||
}
|
||||
});
|
||||
Ok(())
|
||||
|
||||
@@ -217,6 +217,74 @@ fn test_table_sequence_from() -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_table_pairs() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let table = lua
|
||||
.load(
|
||||
r#"
|
||||
{
|
||||
foo = "bar",
|
||||
baz = "baf",
|
||||
[123] = 456,
|
||||
[789] = 101112,
|
||||
5,
|
||||
}
|
||||
"#,
|
||||
)
|
||||
.eval::<Table>()?;
|
||||
|
||||
let table2 = table.clone();
|
||||
for (i, kv) in table.pairs::<String, Value>().enumerate() {
|
||||
let (k, _v) = kv.unwrap();
|
||||
match i {
|
||||
// Try to add a new key
|
||||
0 => table2.set("new_key", "new_value")?,
|
||||
// Try to delete the 2nd key
|
||||
1 => {
|
||||
table2.set(k, Value::Nil)?;
|
||||
lua.gc_collect()?;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_table_for_each() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let table = lua
|
||||
.load(
|
||||
r#"
|
||||
{
|
||||
foo = "bar",
|
||||
baz = "baf",
|
||||
[123] = 456,
|
||||
[789] = 101112,
|
||||
5,
|
||||
}
|
||||
"#,
|
||||
)
|
||||
.eval::<Table>()?;
|
||||
|
||||
let mut i = 0;
|
||||
table.for_each::<String, Value>(|k, _| {
|
||||
if i == 0 {
|
||||
// Delete first key
|
||||
table.set(k, Value::Nil)?;
|
||||
lua.gc_collect()?;
|
||||
}
|
||||
Ok(i += 1)
|
||||
})?;
|
||||
assert_eq!(i, 5);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_table_scope() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
+32
-15
@@ -1,4 +1,5 @@
|
||||
use std::collections::HashMap;
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
use std::iter::FromIterator;
|
||||
use std::panic::{catch_unwind, AssertUnwindSafe};
|
||||
use std::string::String as StdString;
|
||||
@@ -312,30 +313,29 @@ fn test_error() -> Result<()> {
|
||||
globals.set("rust_error_function", rust_error_function)?;
|
||||
|
||||
let no_error = globals.get::<_, Function>("no_error")?;
|
||||
let lua_error = globals.get::<_, Function>("lua_error")?;
|
||||
let rust_error = globals.get::<_, Function>("rust_error")?;
|
||||
let return_error = globals.get::<_, Function>("return_error")?;
|
||||
let return_string_error = globals.get::<_, Function>("return_string_error")?;
|
||||
let test_pcall = globals.get::<_, Function>("test_pcall")?;
|
||||
let understand_recursion = globals.get::<_, Function>("understand_recursion")?;
|
||||
|
||||
assert!(no_error.call::<_, ()>(()).is_ok());
|
||||
|
||||
let lua_error = globals.get::<_, Function>("lua_error")?;
|
||||
match lua_error.call::<_, ()>(()) {
|
||||
Err(Error::RuntimeError(_)) => {}
|
||||
Err(e) => panic!("error is not RuntimeError kind, got {:?}", e),
|
||||
_ => panic!("error not returned"),
|
||||
}
|
||||
|
||||
let rust_error = globals.get::<_, Function>("rust_error")?;
|
||||
match rust_error.call::<_, ()>(()) {
|
||||
Err(Error::CallbackError { .. }) => {}
|
||||
Err(e) => panic!("error is not CallbackError kind, got {:?}", e),
|
||||
_ => panic!("error not returned"),
|
||||
}
|
||||
|
||||
let return_error = globals.get::<_, Function>("return_error")?;
|
||||
match return_error.call::<_, Value>(()) {
|
||||
Ok(Value::Error(_)) => {}
|
||||
_ => panic!("Value::Error not returned"),
|
||||
}
|
||||
|
||||
let return_string_error = globals.get::<_, Function>("return_string_error")?;
|
||||
assert!(return_string_error.call::<_, Error>(()).is_ok());
|
||||
|
||||
match lua
|
||||
@@ -358,9 +358,14 @@ fn test_error() -> Result<()> {
|
||||
_ => panic!("error not returned"),
|
||||
}
|
||||
|
||||
let test_pcall = globals.get::<_, Function>("test_pcall")?;
|
||||
test_pcall.call::<_, ()>(())?;
|
||||
|
||||
assert!(understand_recursion.call::<_, ()>(()).is_err());
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
{
|
||||
let understand_recursion = globals.get::<_, Function>("understand_recursion")?;
|
||||
assert!(understand_recursion.call::<_, ()>(()).is_err());
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -947,6 +952,7 @@ fn test_application_data() -> Result<()> {
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
fn test_recursion() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
@@ -959,21 +965,23 @@ fn test_recursion() -> Result<()> {
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
lua.globals().set("f", f.clone())?;
|
||||
lua.globals().set("f", &f)?;
|
||||
f.call::<_, ()>(1)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
fn test_too_many_returns() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
let f = lua.create_function(|_, ()| Ok(Variadic::from_iter(1..1000000)))?;
|
||||
assert!(f.call::<_, Vec<u32>>(()).is_err());
|
||||
assert!(f.call::<_, Variadic<u32>>(()).is_err());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
fn test_too_many_arguments() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
lua.load("function test(...) end").exec()?;
|
||||
@@ -988,19 +996,21 @@ fn test_too_many_arguments() -> Result<()> {
|
||||
|
||||
#[test]
|
||||
#[cfg(not(feature = "luajit"))]
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
fn test_too_many_recursions() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let f = lua
|
||||
.create_function(move |lua, ()| lua.globals().get::<_, Function>("f")?.call::<_, ()>(()))?;
|
||||
|
||||
lua.globals().set("f", f.clone())?;
|
||||
lua.globals().set("f", &f)?;
|
||||
assert!(f.call::<_, ()>(()).is_err());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
fn test_too_many_binds() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
let globals = lua.globals();
|
||||
@@ -1022,6 +1032,7 @@ fn test_too_many_binds() -> Result<()> {
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
fn test_ref_stack_exhaustion() {
|
||||
match catch_unwind(AssertUnwindSafe(|| -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
@@ -1325,10 +1336,10 @@ fn test_warnings() -> Result<()> {
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "luajit")]
|
||||
#[should_panic]
|
||||
fn test_luajit_cdata() {
|
||||
fn test_luajit_cdata() -> Result<()> {
|
||||
let lua = unsafe { Lua::unsafe_new() };
|
||||
let _v: Result<Value> = lua
|
||||
|
||||
let cdata = lua
|
||||
.load(
|
||||
r#"
|
||||
local ffi = require("ffi")
|
||||
@@ -1341,11 +1352,17 @@ fn test_luajit_cdata() {
|
||||
return ptr
|
||||
"#,
|
||||
)
|
||||
.eval();
|
||||
.eval::<Value>()?;
|
||||
assert!(cdata.is_userdata() && cdata.is_cdata());
|
||||
assert_eq!(cdata.type_name(), "cdata");
|
||||
assert!(cdata.to_string()?.starts_with("cdata<void *>:"));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "send")]
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
fn test_send() {
|
||||
let lua = Lua::new();
|
||||
std::thread::spawn(move || {
|
||||
|
||||
+1
-1
@@ -182,7 +182,7 @@ fn test_coroutine_panic() {
|
||||
let thrd_main = lua.create_function(|_, ()| -> Result<()> {
|
||||
panic!("test_panic");
|
||||
})?;
|
||||
lua.globals().set("main", thrd_main.clone())?;
|
||||
lua.globals().set("main", &thrd_main)?;
|
||||
let thrd: Thread = lua.create_thread(thrd_main)?;
|
||||
thrd.resume(())
|
||||
}) {
|
||||
|
||||
+35
-7
@@ -58,7 +58,7 @@ fn test_methods() -> Result<()> {
|
||||
|
||||
fn check_methods(lua: &Lua, userdata: AnyUserData) -> Result<()> {
|
||||
let globals = lua.globals();
|
||||
globals.set("userdata", userdata.clone())?;
|
||||
globals.set("userdata", &userdata)?;
|
||||
lua.load(
|
||||
r#"
|
||||
function get_it()
|
||||
@@ -342,7 +342,7 @@ fn test_userdata_take() -> Result<()> {
|
||||
}
|
||||
|
||||
fn check_userdata_take(lua: &Lua, userdata: AnyUserData, rc: Arc<i64>) -> Result<()> {
|
||||
lua.globals().set("userdata", userdata.clone())?;
|
||||
lua.globals().set("userdata", &userdata)?;
|
||||
assert_eq!(Arc::strong_count(&rc), 3);
|
||||
{
|
||||
let _value = userdata.borrow::<MyUserdata>()?;
|
||||
@@ -474,7 +474,7 @@ fn test_functions() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
let globals = lua.globals();
|
||||
let userdata = lua.create_userdata(MyUserData(42))?;
|
||||
globals.set("userdata", userdata.clone())?;
|
||||
globals.set("userdata", &userdata)?;
|
||||
lua.load(
|
||||
r#"
|
||||
function get_it()
|
||||
@@ -506,6 +506,9 @@ fn test_functions() -> Result<()> {
|
||||
|
||||
#[test]
|
||||
fn test_fields() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
let globals = lua.globals();
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
struct MyUserData(i64);
|
||||
|
||||
@@ -533,8 +536,6 @@ fn test_fields() -> Result<()> {
|
||||
}
|
||||
}
|
||||
|
||||
let lua = Lua::new();
|
||||
let globals = lua.globals();
|
||||
globals.set("ud", MyUserData(7))?;
|
||||
lua.load(
|
||||
r#"
|
||||
@@ -555,6 +556,33 @@ fn test_fields() -> Result<()> {
|
||||
)
|
||||
.exec()?;
|
||||
|
||||
// Case: fields + __index metamethod (function)
|
||||
struct MyUserData2(i64);
|
||||
|
||||
impl UserData for MyUserData2 {
|
||||
fn add_fields<'lua, F: UserDataFields<'lua, Self>>(fields: &mut F) {
|
||||
fields.add_field("z", 0);
|
||||
fields.add_field_method_get("x", |_, data| Ok(data.0));
|
||||
}
|
||||
|
||||
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
|
||||
methods.add_meta_method(MetaMethod::Index, |_, _, name: StdString| match &*name {
|
||||
"y" => Ok(Some(-1)),
|
||||
_ => Ok(None),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
globals.set("ud", MyUserData2(1))?;
|
||||
lua.load(
|
||||
r#"
|
||||
assert(ud.x == 1)
|
||||
assert(ud.y == -1)
|
||||
assert(ud.z == 0)
|
||||
"#,
|
||||
)
|
||||
.exec()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -963,9 +991,9 @@ fn test_userdata_derive() -> Result<()> {
|
||||
// More complex struct where generics and where clause
|
||||
|
||||
#[derive(Clone, Copy, mlua::FromLua)]
|
||||
struct MyUserData2<'a, T>(&'a T)
|
||||
struct MyUserData2<'a, T: ?Sized>(&'a T)
|
||||
where
|
||||
T: ?Sized;
|
||||
T: Copy;
|
||||
|
||||
lua.register_userdata_type::<MyUserData2<'static, i32>>(|reg| {
|
||||
reg.add_function("val", |_, this: MyUserData2<'static, i32>| Ok(*this.0));
|
||||
|
||||
Reference in New Issue
Block a user