Compare commits

..

54 Commits

Author SHA1 Message Date
Alex Orlenko 270b98a429 v0.9.6 2024-02-29 15:39:14 +00:00
Alex Orlenko 0ee3324462 Add LUA_TCDATA to util::to_string() helper 2024-02-29 12:57:04 +00:00
Alex Orlenko 8a9c4f0b15 Optimize table array traversal during serialization 2024-02-11 23:02:36 +00:00
Alex Orlenko 34db5f985e Refactor benchmarks 2024-02-11 17:48:25 +00:00
Alex Orlenko 020e8a78a8 Impl FromLua for RegistryKey 2024-02-10 15:41:48 +00:00
Alex Orlenko 3ca7b4942e Implement IntoLua for &Value 2024-02-07 00:10:49 +00:00
Alex Orlenko 1754226c74 Impl IntoLua::push_into_stack for integers 2024-02-03 21:32:49 +00:00
Alex Orlenko 3014c4d7a1 Add REF_STACK_RESERVE constant 2024-02-02 23:26:33 +00:00
Alex Orlenko 908f37656a Add to_pointer function to Function/Table/Thread 2024-02-02 09:23:16 +00:00
Alex Orlenko f4d783cb41 Impl push_into_stack for StdResult 2024-02-01 23:42:44 +00:00
Alex Orlenko f5982bc204 Add inline to FromLua<bool>::from_stack 2024-01-27 14:40:32 +00:00
Alex Orlenko e30b425224 Fix crash when initializing Luau sandbox without stdlibs (#361) 2024-01-27 11:51:59 +00:00
Alex Orlenko 512921404c Add fastpath push_into_stack/from_stack methods for bool type 2024-01-26 14:07:56 +00:00
Alex Orlenko dfd82edc42 Add Lua::push() helper 2024-01-26 13:52:19 +00:00
Alex Orlenko 75a15ceabf v0.9.5 2024-01-25 22:24:58 +00:00
Alex Orlenko 60730fd068 Update compile tests messages 2024-01-25 18:19:16 +00:00
Alex Orlenko df778b7b33 Impl Into/FromLua for OwnedString 2024-01-25 18:04:55 +00:00
Alex Orlenko 45299c0ef1 Update authors 2024-01-25 12:56:57 +00:00
Alex Orlenko 38eec1236c Update itertools dependency 2024-01-25 10:49:39 +00:00
Alex Orlenko 145c5b316b Fix FromLua derive proc macro to cover more cases 2024-01-25 10:26:43 +00:00
Alex Orlenko e97e69a309 Update Luau to 0.609 (luau-src v0.8.0) 2024-01-25 09:38:46 +00:00
Alex Orlenko 2ac7b23596 Impl Into/FromLua for OwnedThread 2024-01-25 09:34:37 +00:00
Alex Orlenko 8200bee467 Implement IntoLua for ref to String/Table/Function/AnyUserData
This would prevent cloning plus has better performance when pushing values to Lua stack (`IntoLua::push_into_stack` method)
2024-01-23 22:14:50 +00:00
Alex Orlenko fe6ab250bf Update codecov links after moving repo 2024-01-23 21:56:00 +00:00
Alex Orlenko 804972b099 Fix typos in examples/guided_tour 2024-01-23 20:50:57 +00:00
Alex Orlenko 727f99ee4d Implement IntoLua for &RegistryKey
This would allow just passing registry keys to arguments with fasttrack to push directly into stack.
2024-01-20 22:04:28 +00:00
Alex Orlenko 3c801e7b17 Expose internal POLL_PENDING constant (hidden) 2024-01-20 15:52:00 +00:00
Alex Orlenko a38e484fe9 Increase luau max stack size to 1M from 100k 2024-01-18 23:05:23 +00:00
Alex Orlenko eed48889cd v0.9.4 2024-01-10 15:41:31 +00:00
Alex Orlenko b5896173fd Include skip_memory_check code only when the corresponding attribute set for module 2024-01-10 10:08:33 +00:00
Alex Orlenko 205989f569 Fix edge case when loading many-in-one module from thread without using its state.
If Lua previously been initialized in main thread and then new module was loaded from thread
we reuse old state which confuses Lua loader.
2024-01-10 00:37:02 +00:00
Alex Orlenko 12472de1d2 v0.9.3 2024-01-08 21:02:45 +00:00
Alex Orlenko a68708c12e Update README & CHANGELOG 2024-01-08 18:44:49 +00:00
Alex Orlenko 4c0474d573 Fix docsrs attr for Thread::reset 2024-01-06 15:04:18 +00:00
Alex Orlenko 60e859f643 Fix (nightly) warning in doc 2024-01-06 12:55:18 +00:00
Alex Orlenko 9ed0d90746 Run tests for wasm32-unknown-emscripten 2024-01-06 12:12:56 +00:00
Alex Orlenko 514ec24252 Fix lua53/lua54 luaL_error definition (for wasm32) 2024-01-03 12:02:53 +00:00
Alex Orlenko 244e6c9c12 Bump rustyline dependency 2024-01-03 11:34:50 +00:00
Alex Orlenko 4749e3a22a Update minimal lua(u) versions (needed for wasm32) 2024-01-03 11:34:34 +00:00
Alex Orlenko cf153f38de Panic when try to build for wasm32 without vendored feature (except luau) 2024-01-03 11:33:42 +00:00
Alex Orlenko c0a0983025 mlua-sys: always inline lua_error 2024-01-03 11:31:39 +00:00
Alex Orlenko 69ff0c5509 mlua-sys: fix Lua 5.2 lua_sethook definition 2024-01-03 10:22:42 +00:00
Aymen-Hakim bf79d6c212 Update lauxlib.rs (#351)
lua54 in lua53 src.
2023-12-27 15:07:59 -05:00
ByteDream 0b9a85e183 Add lua emscripten support (#338) 2023-12-14 14:54:57 +00:00
Alex Orlenko 59974d7bde Merge pull request #337 from tari/emscripten-support
Fix build for emscripten target
2023-12-13 13:55:26 +00:00
Joel Natividad 61e846326c Update Cargo.toml (#342) 2023-12-10 17:04:37 +00:00
Alex Orlenko 3547985bb0 Merge pull request #339 from eatradish/fix-loongarch64-build
Add loongarch64 architecture support
2023-12-08 09:13:07 +00:00
eatradish 4c92580201 Add loongarch64 architecture support 2023-12-08 11:16:52 +08:00
Peter Marheine e3f34f319c Correct C return type for lua_error
The definition of lua_error in all of Lua 5.1 through 5.4 says lua_error
returns int, but the Rust definition of the same function treats it as
void (because it's known not to return). This causes link-time errors when
building for wasm targets because the wasm linker is aware of function return
types and errors out if they differ between definition and declaration.
2023-12-06 21:16:52 +11:00
Peter Marheine b16f3895a0 lua54: use changed return type for lua_rawlen
Lua 5.4 changed lua_rawlen to return lua_Unsigned, versus size_t in earlier
versions. Change the C API signature to match, and add a wrapper function with
the same name that maintains a stable Rust API by casting to usize.
2023-12-06 21:09:25 +11:00
Alex Orlenko a4c919231c Don't clone function name when calling async userdata method 2023-12-03 19:55:27 +00:00
Alex Orlenko e4d6e92287 (async) Move "pending" poll value from env to poll_future() results 2023-12-02 15:01:40 +00:00
Alex Orlenko 642201a7e0 Remove locals from __mlua_async_poll helper 2023-12-01 18:11:06 +00:00
Alex Orlenko c36808b251 Faster Function::call() for lua51/jit/luau 2023-12-01 12:03:46 +00:00
57 changed files with 1500 additions and 487 deletions
+23
View File
@@ -206,6 +206,29 @@ 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
+25
View File
@@ -1,3 +1,28 @@
## v0.9.6
- Added `to_pointer` function to `Function`/`Table`/`Thread`
- Implemented `IntoLua` for `&Value`
- Implemented `FromLua` for `RegistryKey`
- Faster (~5%) table array traversal during serialization
- Some performance improvements for bool/int types
## 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
+11 -8
View File
@@ -1,7 +1,7 @@
[package]
name = "mlua"
version = "0.9.2" # remember to update mlua_derive
authors = ["Aleksandr Orlenko <zxteam@pm.me>", "kyren <catherine@chucklefish.org>"]
version = "0.9.6" # 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"
@@ -44,36 +44,39 @@ 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.4.0", 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
+4 -2
View File
@@ -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
+187 -166
View File
@@ -1,5 +1,7 @@
use criterion::{criterion_group, criterion_main, BatchSize, Criterion};
use std::sync::atomic::{AtomicUsize, Ordering};
use std::time::Duration;
use criterion::{criterion_group, criterion_main, BatchSize, Criterion};
use tokio::runtime::Runtime;
use tokio::task;
@@ -10,10 +12,10 @@ fn collect_gc_twice(lua: &Lua) {
lua.gc_collect().unwrap();
}
fn create_table(c: &mut Criterion) {
fn table_create_empty(c: &mut Criterion) {
let lua = Lua::new();
c.bench_function("create [table empty]", |b| {
c.bench_function("table [create empty]", |b| {
b.iter_batched(
|| collect_gc_twice(&lua),
|_| {
@@ -24,35 +26,33 @@ fn create_table(c: &mut Criterion) {
});
}
fn create_array(c: &mut Criterion) {
fn table_create_array(c: &mut Criterion) {
let lua = Lua::new();
c.bench_function("create [array] 10", |b| {
c.bench_function("table [create array]", |b| {
b.iter_batched(
|| collect_gc_twice(&lua),
|_| {
let table = lua.create_table().unwrap();
for i in 1..=10 {
table.set(i, i).unwrap();
}
lua.create_sequence_from(1..=10).unwrap();
},
BatchSize::SmallInput,
);
});
}
fn create_string_table(c: &mut Criterion) {
fn table_create_hash(c: &mut Criterion) {
let lua = Lua::new();
c.bench_function("create [table string] 10", |b| {
c.bench_function("table [create hash]", |b| {
b.iter_batched(
|| collect_gc_twice(&lua),
|_| {
let table = lua.create_table().unwrap();
for &s in &["1", "2", "3", "4", "5", "6", "7", "8", "9", "10"] {
let s = lua.create_string(s).unwrap();
table.set(s.clone(), s).unwrap();
}
lua.create_table_from(
["1", "2", "3", "4", "5", "6", "7", "8", "9", "10"]
.into_iter()
.map(|s| (s, s)),
)
.unwrap();
},
BatchSize::SmallInput,
);
@@ -62,17 +62,15 @@ fn create_string_table(c: &mut Criterion) {
fn table_get_set(c: &mut Criterion) {
let lua = Lua::new();
let table = lua.create_table().unwrap();
c.bench_function("table raw_get and raw_set [10]", |b| {
c.bench_function("table [get and set]", |b| {
b.iter_batched(
|| {
collect_gc_twice(&lua);
table.clear().unwrap();
lua.create_table().unwrap()
},
|_| {
for (i, &s) in ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j"]
.iter()
|table| {
for (i, s) in ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j"]
.into_iter()
.enumerate()
{
table.raw_set(s, i).unwrap();
@@ -87,7 +85,7 @@ 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| {
c.bench_function("table [traversal pairs]", |b| {
b.iter_batched(
|| lua.globals(),
|globals| {
@@ -103,7 +101,7 @@ fn table_traversal_pairs(c: &mut Criterion) {
fn table_traversal_for_each(c: &mut Criterion) {
let lua = Lua::new();
c.bench_function("table traversal [for_each]", |b| {
c.bench_function("table [traversal for_each]", |b| {
b.iter_batched(
|| lua.globals(),
|globals| globals.for_each::<String, LuaValue>(|_k, _v| Ok(())),
@@ -117,7 +115,7 @@ fn table_traversal_sequence(c: &mut Criterion) {
let table = lua.create_sequence_from(1..1000).unwrap();
c.bench_function("table traversal [sequence]", |b| {
c.bench_function("table [traversal sequence]", |b| {
b.iter_batched(
|| table.clone(),
|table| {
@@ -130,236 +128,255 @@ fn table_traversal_sequence(c: &mut Criterion) {
});
}
fn create_function(c: &mut Criterion) {
fn function_create(c: &mut Criterion) {
let lua = Lua::new();
c.bench_function("create [function] 10", |b| {
c.bench_function("function [create Rust]", |b| {
b.iter_batched(
|| collect_gc_twice(&lua),
|_| {
for i in 0..10 {
lua.create_function(move |_, ()| Ok(i)).unwrap();
}
lua.create_function(|_, ()| Ok(123)).unwrap();
},
BatchSize::SmallInput,
);
});
}
fn call_lua_function(c: &mut Criterion) {
fn function_call_sum(c: &mut Criterion) {
let lua = Lua::new();
c.bench_function("call Lua function [sum] 3 10", |b| {
b.iter_batched_ref(
|| {
collect_gc_twice(&lua);
lua.load("function(a, b, c) return a + b + c end")
.eval::<LuaFunction>()
.unwrap()
},
|function| {
for i in 0..10 {
let _result: i64 = function.call((i, i + 1, i + 2)).unwrap();
}
},
BatchSize::SmallInput,
);
});
}
fn call_sum_callback(c: &mut Criterion) {
let lua = Lua::new();
let callback = lua
.create_function(|_, (a, b, c): (i64, i64, i64)| Ok(a + b + c))
let sum = lua
.create_function(|_, (a, b, c): (i64, i64, i64)| Ok(a + b - c))
.unwrap();
lua.globals().set("callback", callback).unwrap();
c.bench_function("call Rust callback [sum] 3 10", |b| {
b.iter_batched_ref(
|| {
collect_gc_twice(&lua);
lua.load("function() for i = 1,10 do callback(i, i+1, i+2) end end")
.eval::<LuaFunction>()
.unwrap()
},
|function| {
function.call::<_, ()>(()).unwrap();
c.bench_function("function [call Rust sum]", |b| {
b.iter_batched(
|| collect_gc_twice(&lua),
|_| {
assert_eq!(sum.call::<_, i64>((10, 20, 30)).unwrap(), 0);
},
BatchSize::SmallInput,
);
});
}
fn call_async_sum_callback(c: &mut Criterion) {
let options = LuaOptions::new().thread_pool_size(1024);
let lua = Lua::new_with(LuaStdLib::ALL_SAFE, options).unwrap();
let callback = lua
.create_async_function(|_, (a, b, c): (i64, i64, i64)| async move {
task::yield_now().await;
Ok(a + b + c)
})
.unwrap();
lua.globals().set("callback", callback).unwrap();
c.bench_function("call async Rust callback [sum] 3 10", |b| {
let rt = Runtime::new().unwrap();
b.to_async(rt).iter_batched(
|| {
collect_gc_twice(&lua);
lua.load("function() for i = 1,10 do callback(i, i+1, i+2) end end")
.eval::<LuaFunction>()
.unwrap()
},
|function| async move {
function.call_async::<_, ()>(()).await.unwrap();
},
BatchSize::SmallInput,
);
});
}
fn call_concat_callback(c: &mut Criterion) {
fn function_call_lua_sum(c: &mut Criterion) {
let lua = Lua::new();
let callback = lua
let sum = lua
.load("function(a, b, c) return a + b - c end")
.eval::<LuaFunction>()
.unwrap();
c.bench_function("function [call Lua sum]", |b| {
b.iter_batched(
|| collect_gc_twice(&lua),
|_| {
assert_eq!(sum.call::<_, i64>((10, 20, 30)).unwrap(), 0);
},
BatchSize::SmallInput,
);
});
}
fn function_call_concat(c: &mut Criterion) {
let lua = Lua::new();
let concat = lua
.create_function(|_, (a, b): (LuaString, LuaString)| {
Ok(format!("{}{}", a.to_str()?, b.to_str()?))
})
.unwrap();
lua.globals().set("callback", callback).unwrap();
let i = AtomicUsize::new(0);
c.bench_function("call Rust callback [concat string] 10", |b| {
b.iter_batched_ref(
c.bench_function("function [call Rust concat string]", |b| {
b.iter_batched(
|| {
collect_gc_twice(&lua);
lua.load("function() for i = 1,10 do callback('a', tostring(i)) end end")
.eval::<LuaFunction>()
.unwrap()
i.fetch_add(1, Ordering::Relaxed)
},
|function| {
function.call::<_, ()>(()).unwrap();
|i| {
assert_eq!(
concat.call::<_, LuaString>(("num:", i)).unwrap(),
format!("num:{i}")
);
},
BatchSize::SmallInput,
);
});
}
fn create_registry_values(c: &mut Criterion) {
fn function_call_lua_concat(c: &mut Criterion) {
let lua = Lua::new();
c.bench_function("create [registry value] 10", |b| {
let concat = lua
.load("function(a, b) return a..b end")
.eval::<LuaFunction>()
.unwrap();
let i = AtomicUsize::new(0);
c.bench_function("function [call Lua concat string]", |b| {
b.iter_batched(
|| collect_gc_twice(&lua),
|_| {
for _ in 0..10 {
lua.create_registry_value(lua.pack(true).unwrap()).unwrap();
}
lua.expire_registry_values();
|| {
collect_gc_twice(&lua);
i.fetch_add(1, Ordering::Relaxed)
},
|i| {
assert_eq!(
concat.call::<_, LuaString>(("num:", i)).unwrap(),
format!("num:{i}")
);
},
BatchSize::SmallInput,
);
});
}
fn create_userdata(c: &mut Criterion) {
fn function_async_call_sum(c: &mut Criterion) {
let options = LuaOptions::new().thread_pool_size(1024);
let lua = Lua::new_with(LuaStdLib::ALL_SAFE, options).unwrap();
let sum = lua
.create_async_function(|_, (a, b, c): (i64, i64, i64)| async move {
task::yield_now().await;
Ok(a + b - c)
})
.unwrap();
c.bench_function("function [async call Rust sum]", |b| {
let rt = Runtime::new().unwrap();
b.to_async(rt).iter_batched(
|| collect_gc_twice(&lua),
|_| async {
assert_eq!(sum.call_async::<_, i64>((10, 20, 30)).await.unwrap(), 0);
},
BatchSize::SmallInput,
);
});
}
fn registry_value_create(c: &mut Criterion) {
let lua = Lua::new();
lua.gc_stop();
c.bench_function("registry value [create]", |b| {
b.iter_batched(
|| collect_gc_twice(&lua),
|_| lua.create_registry_value("hello").unwrap(),
BatchSize::SmallInput,
);
});
}
fn userdata_create(c: &mut Criterion) {
struct UserData(i64);
impl LuaUserData for UserData {}
let lua = Lua::new();
c.bench_function("create [table userdata] 10", |b| {
c.bench_function("userdata [create]", |b| {
b.iter_batched(
|| collect_gc_twice(&lua),
|_| {
let table: LuaTable = lua.create_table().unwrap();
for i in 1..11 {
table.set(i, UserData(i)).unwrap();
}
lua.create_userdata(UserData(123)).unwrap();
},
BatchSize::SmallInput,
);
});
}
fn call_userdata_index(c: &mut Criterion) {
fn userdata_call_index(c: &mut Criterion) {
struct UserData(i64);
impl LuaUserData for UserData {
fn add_methods<'lua, M: LuaUserDataMethods<'lua, Self>>(methods: &mut M) {
methods.add_meta_method(LuaMetaMethod::Index, move |_, _, index: String| Ok(index));
methods.add_meta_method(LuaMetaMethod::Index, move |_, _, key: LuaString| Ok(key));
}
}
let lua = Lua::new();
lua.globals().set("userdata", UserData(10)).unwrap();
let ud = lua.create_userdata(UserData(123)).unwrap();
let index = lua
.load("function(ud) return ud.test end")
.eval::<LuaFunction>()
.unwrap();
c.bench_function("call [userdata index] 10", |b| {
b.iter_batched_ref(
|| {
collect_gc_twice(&lua);
lua.load("function() for i = 1,10 do local v = userdata.test end end")
.eval::<LuaFunction>()
.unwrap()
},
|function| {
function.call::<_, ()>(()).unwrap();
c.bench_function("userdata [call index]", |b| {
b.iter_batched(
|| collect_gc_twice(&lua),
|_| {
assert_eq!(index.call::<_, LuaString>(&ud).unwrap(), "test");
},
BatchSize::SmallInput,
);
});
}
fn call_userdata_method(c: &mut Criterion) {
fn userdata_call_method(c: &mut Criterion) {
struct UserData(i64);
impl LuaUserData for UserData {
fn add_methods<'lua, M: LuaUserDataMethods<'lua, Self>>(methods: &mut M) {
methods.add_method("method", |_, this, ()| Ok(this.0));
methods.add_method("add", |_, this, i: i64| Ok(this.0 + i));
}
}
let lua = Lua::new();
lua.globals().set("userdata", UserData(10)).unwrap();
let ud = lua.create_userdata(UserData(123)).unwrap();
let method = lua
.load("function(ud, i) return ud:add(i) end")
.eval::<LuaFunction>()
.unwrap();
let i = AtomicUsize::new(0);
c.bench_function("call [userdata method] 10", |b| {
b.iter_batched_ref(
c.bench_function("userdata [call method]", |b| {
b.iter_batched(
|| {
collect_gc_twice(&lua);
lua.load("function() for i = 1,10 do userdata:method() end end")
.eval::<LuaFunction>()
.unwrap()
i.fetch_add(1, Ordering::Relaxed)
},
|function| {
function.call::<_, ()>(()).unwrap();
|i| {
assert_eq!(method.call::<_, usize>((&ud, i)).unwrap(), 123 + i);
},
BatchSize::SmallInput,
);
});
}
fn call_async_userdata_method(c: &mut Criterion) {
struct UserData(String);
fn userdata_async_call_method(c: &mut Criterion) {
struct UserData(i64);
impl LuaUserData for UserData {
fn add_methods<'lua, M: LuaUserDataMethods<'lua, Self>>(methods: &mut M) {
methods.add_async_method("method", |_, this, ()| async move { Ok(this.0.clone()) });
methods.add_async_method("add", |_, this, i: i64| async move {
task::yield_now().await;
Ok(this.0 + i)
});
}
}
let options = LuaOptions::new().thread_pool_size(1024);
let lua = Lua::new_with(LuaStdLib::ALL_SAFE, options).unwrap();
lua.globals()
.set("userdata", UserData("hello".to_string()))
let ud = lua.create_userdata(UserData(123)).unwrap();
let method = lua
.load("function(ud, i) return ud:add(i) end")
.eval::<LuaFunction>()
.unwrap();
let i = AtomicUsize::new(0);
c.bench_function("call async [userdata method] 10", |b| {
c.bench_function("userdata [async call method] 10", |b| {
let rt = Runtime::new().unwrap();
b.to_async(rt).iter_batched(
|| {
collect_gc_twice(&lua);
lua.load("function() for i = 1,10 do userdata:method() end end")
.eval::<LuaFunction>()
.unwrap()
(
method.clone(),
ud.clone(),
i.fetch_add(1, Ordering::Relaxed),
)
},
|function| async move {
function.call_async::<_, ()>(()).await.unwrap();
|(method, ud, i)| async move {
assert_eq!(
method.call_async::<_, usize>((ud, i)).await.unwrap(),
123 + i
);
},
BatchSize::SmallInput,
);
@@ -369,27 +386,31 @@ fn call_async_userdata_method(c: &mut Criterion) {
criterion_group! {
name = benches;
config = Criterion::default()
.sample_size(300)
.sample_size(500)
.measurement_time(Duration::from_secs(10))
.noise_threshold(0.02);
targets =
create_table,
create_array,
create_string_table,
table_create_empty,
table_create_array,
table_create_hash,
table_get_set,
table_traversal_pairs,
table_traversal_for_each,
table_traversal_sequence,
create_function,
call_lua_function,
call_sum_callback,
call_async_sum_callback,
call_concat_callback,
create_registry_values,
create_userdata,
call_userdata_index,
call_userdata_method,
call_async_userdata_method,
function_create,
function_call_sum,
function_call_lua_sum,
function_call_concat,
function_call_lua_concat,
function_async_call_sum,
registry_value_create,
userdata_create,
userdata_call_index,
userdata_call_method,
userdata_async_call_method,
}
criterion_main!(benches);
+25 -33
View File
@@ -8,43 +8,35 @@ fn collect_gc_twice(lua: &Lua) {
lua.gc_collect().unwrap();
}
fn serialize_json(c: &mut Criterion) {
fn encode_json(c: &mut Criterion) {
let lua = Lua::new();
lua.globals()
.set(
"encode",
LuaFunction::wrap(|_, t: LuaValue| Ok(serde_json::to_string(&t).unwrap())),
let encode = lua
.create_function(|_, t: LuaValue| Ok(serde_json::to_string(&t).unwrap()))
.unwrap();
let table = lua
.load(
r#"{
name = "Clark Kent",
address = {
city = "Smallville",
state = "Kansas",
country = "USA",
},
age = 22,
parents = {"Jonathan Kent", "Martha Kent"},
superman = true,
interests = {"flying", "saving the world", "kryptonite"},
}"#,
)
.eval::<LuaTable>()
.unwrap();
c.bench_function("serialize table to json [10]", |b| {
c.bench_function("serialize json", |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();
|| collect_gc_twice(&lua),
|_| {
encode.call::<_, LuaString>(&table).unwrap();
},
BatchSize::SmallInput,
);
@@ -54,11 +46,11 @@ fn serialize_json(c: &mut Criterion) {
criterion_group! {
name = benches;
config = Criterion::default()
.sample_size(300)
.sample_size(500)
.measurement_time(Duration::from_secs(10))
.noise_threshold(0.02);
targets =
serialize_json,
encode_json,
}
criterion_main!(benches);
+1 -1
View File
@@ -19,7 +19,7 @@ impl UserData for BodyReader {
}
}
#[tokio::main]
#[tokio::main(flavor = "current_thread")]
async fn main() -> Result<()> {
let lua = Lua::new();
+1 -1
View File
@@ -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();
+3 -3
View File
@@ -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.
+3 -3
View File
@@ -1,6 +1,6 @@
[package]
name = "mlua-sys"
version = "0.4.0"
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 }
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.7.8", optional = true }
luau0-src = { version = "0.8.0", optional = true }
+6
View File
@@ -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();
+1
View File
@@ -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();
+1
View File
@@ -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;
+1 -1
View File
@@ -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,
+19 -1
View File
@@ -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())
+1 -1
View File
@@ -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,
+25 -2
View File
@@ -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;
+2 -2
View File
@@ -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,
+19 -1
View File
@@ -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())
+1 -1
View File
@@ -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,
+28 -2
View File
@@ -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())
+6 -3
View File
@@ -144,9 +144,12 @@ pub unsafe fn luaL_sandbox(L: *mut lua_State, enabled: c_int) {
// set all builtin metatables to read-only
lua_pushliteral(L, "");
lua_getmetatable(L, -1);
lua_setreadonly(L, -1, enabled);
lua_pop(L, 2);
if lua_getmetatable(L, -1) != 0 {
lua_setreadonly(L, -1, enabled);
lua_pop(L, 2);
} else {
lua_pop(L, 1);
}
// set globals to readonly and activate safeenv since the env is immutable
lua_setreadonly(L, LUA_GLOBALSINDEX, enabled);
+21 -2
View File
@@ -8,7 +8,13 @@ use std::{mem, ptr};
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
@@ -144,9 +150,11 @@ 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;
@@ -179,7 +187,7 @@ 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;
@@ -280,6 +288,8 @@ 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;
@@ -398,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,
@@ -419,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)
+2 -2
View File
@@ -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 }
+4 -3
View File
@@ -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 {
+7 -3
View File
@@ -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)
}
};
+230 -2
View File
@@ -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]
@@ -30,6 +33,18 @@ impl<'lua> IntoLua<'lua> for Value<'lua> {
}
}
impl<'lua> IntoLua<'lua> for &Value<'lua> {
#[inline]
fn into_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
Ok(self.clone())
}
#[inline]
unsafe fn push_into_stack(self, lua: &'lua Lua) -> Result<()> {
lua.push_value_ref(self)
}
}
impl<'lua> FromLua<'lua> for Value<'lua> {
#[inline]
fn from_lua(lua_value: Value<'lua>, _: &'lua Lua) -> Result<Self> {
@@ -44,6 +59,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 +84,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 +123,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 +158,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 +188,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 +223,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 +253,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 +279,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 +318,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>> {
@@ -189,6 +356,20 @@ impl<'lua> IntoLua<'lua> for OwnedAnyUserData {
}
}
#[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))
}
}
#[cfg(all(feature = "unstable", any(not(feature = "send"), doc)))]
#[cfg_attr(docsrs, doc(cfg(all(feature = "unstable", not(feature = "send")))))]
impl<'lua> FromLua<'lua> for OwnedAnyUserData {
@@ -240,11 +421,44 @@ 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> FromLua<'lua> for RegistryKey {
#[inline]
fn from_lua(value: Value<'lua>, lua: &'lua Lua) -> Result<RegistryKey> {
lua.create_registry_value(value)
}
}
impl<'lua> IntoLua<'lua> for bool {
#[inline]
fn into_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
Ok(Value::Boolean(self))
}
#[inline]
unsafe fn push_into_stack(self, lua: &'lua Lua) -> Result<()> {
ffi::lua_pushboolean(lua.state(), self as c_int);
Ok(())
}
}
impl<'lua> FromLua<'lua> for bool {
@@ -256,6 +470,11 @@ impl<'lua> FromLua<'lua> for bool {
_ => Ok(true),
}
}
#[inline]
unsafe fn from_stack(idx: c_int, lua: &'lua Lua) -> Result<Self> {
Ok(ffi::lua_toboolean(lua.state(), idx) != 0)
}
}
impl<'lua> IntoLua<'lua> for LightUserData {
@@ -497,6 +716,15 @@ macro_rules! lua_convert_int {
message: Some("out of range".to_owned()),
})
}
#[inline]
unsafe fn push_into_stack(self, lua: &'lua Lua) -> Result<()> {
match cast(self) {
Some(i) => ffi::lua_pushinteger(lua.state(), i),
None => ffi::lua_pushnumber(lua.state(), self as ffi::lua_Number),
}
Ok(())
}
}
impl<'lua> FromLua<'lua> for $x {
+13 -4
View File
@@ -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);
@@ -495,6 +494,16 @@ impl<'lua> Function<'lua> {
}
}
/// Converts this function to a generic C pointer.
///
/// There is no way to convert the pointer back to its original value.
///
/// Typically this function is used only for hashing and debug information.
#[inline]
pub fn to_pointer(&self) -> *const c_void {
self.0.to_pointer()
}
/// Convert this handle to owned version.
#[cfg(all(feature = "unstable", any(not(feature = "send"), doc)))]
#[cfg_attr(docsrs, doc(cfg(all(feature = "unstable", not(feature = "send")))))]
+108 -78
View File
@@ -31,10 +31,10 @@ use crate::types::{
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};
@@ -226,6 +226,7 @@ pub(crate) static EXTRA_REGISTRY_KEY: u8 = 0;
const WRAPPED_FAILURE_POOL_SIZE: usize = 64;
const MULTIVALUE_POOL_SIZE: usize = 64;
const REF_STACK_RESERVE: c_int = 1;
/// Requires `feature = "send"`
#[cfg(feature = "send")]
@@ -499,6 +500,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(),
@@ -512,8 +520,8 @@ impl Lua {
#[cfg(feature = "module")]
skip_memory_check: false,
ref_thread,
// We need 1 extra stack space to move values in and out of the ref stack.
ref_stack_size: ffi::LUA_MINSTACK - 1,
// We need some reserved stack space to move values in and out of the ref stack.
ref_stack_size: ffi::LUA_MINSTACK - REF_STACK_RESERVE,
ref_stack_top: ffi::lua_gettop(ref_thread),
ref_free: Vec::new(),
wrapped_failure_pool: Vec::with_capacity(WRAPPED_FAILURE_POOL_SIZE),
@@ -701,18 +709,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)
@@ -722,12 +732,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.
@@ -1404,8 +1414,8 @@ impl Lua {
let protect = !self.unlikely_memory_error();
push_table(state, 0, lower_bound, protect)?;
for (k, v) in iter {
self.push_value(k.into_lua(self)?)?;
self.push_value(v.into_lua(self)?)?;
self.push(k)?;
self.push(v)?;
if protect {
protect_lua!(state, 3, 1, fn(state) ffi::lua_rawset(state, -3))?;
} else {
@@ -1433,7 +1443,7 @@ impl Lua {
let protect = !self.unlikely_memory_error();
push_table(state, lower_bound, 0, protect)?;
for (i, v) in iter.enumerate() {
self.push_value(v.into_lua(self)?)?;
self.push(v)?;
if protect {
protect_lua!(state, 2, 1, |state| {
ffi::lua_rawseti(state, -2, (i + 1) as Integer);
@@ -1968,12 +1978,11 @@ impl Lua {
T: IntoLua<'lua>,
{
let state = self.state();
let t = t.into_lua(self)?;
unsafe {
let _sg = StackGuard::new(state);
check_stack(state, 5)?;
self.push_value(t)?;
self.push(t)?;
rawset_field(state, ffi::LUA_REGISTRYINDEX, name)
}
}
@@ -2260,33 +2269,38 @@ impl Lua {
extra.app_data.remove()
}
/// Pushes a value onto the Lua stack.
/// Pushes a value that implements `IntoLua` onto the Lua stack.
///
/// Uses 2 stack spaces, does not call checkstack.
#[doc(hidden)]
#[inline(always)]
pub unsafe fn push<'lua>(&'lua self, value: impl IntoLua<'lua>) -> Result<()> {
value.push_into_stack(self)
}
/// Pushes a `Value` onto the Lua stack.
///
/// Uses 2 stack spaces, does not call checkstack.
#[doc(hidden)]
pub unsafe fn push_value(&self, value: Value) -> Result<()> {
if let Value::Error(err) = value {
let protect = !self.unlikely_memory_error();
return push_gc_userdata(self.state(), WrappedFailure::Error(err), protect);
}
self.push_value_ref(&value)
}
/// Pushes a `&Value` (by reference) onto the Lua stack.
///
/// Similar to [`Lua::push_value`], uses 2 stack spaces, does not call checkstack.
pub(crate) unsafe fn push_value_ref(&self, value: &Value) -> Result<()> {
let state = self.state();
match value {
Value::Nil => {
ffi::lua_pushnil(state);
}
Value::Boolean(b) => {
ffi::lua_pushboolean(state, b as c_int);
}
Value::LightUserData(ud) => {
ffi::lua_pushlightuserdata(state, ud.0);
}
Value::Integer(i) => {
ffi::lua_pushinteger(state, i);
}
Value::Number(n) => {
ffi::lua_pushnumber(state, n);
}
Value::Nil => ffi::lua_pushnil(state),
Value::Boolean(b) => ffi::lua_pushboolean(state, *b as c_int),
Value::LightUserData(ud) => ffi::lua_pushlightuserdata(state, ud.0),
Value::Integer(i) => ffi::lua_pushinteger(state, *i),
Value::Number(n) => ffi::lua_pushnumber(state, *n),
#[cfg(feature = "luau")]
Value::Vector(v) => {
#[cfg(not(feature = "luau-vector4"))]
@@ -2294,33 +2308,16 @@ impl Lua {
#[cfg(feature = "luau-vector4")]
ffi::lua_pushvector(state, v.x(), v.y(), v.z(), v.w());
}
Value::String(s) => {
self.push_ref(&s.0);
}
Value::Table(t) => {
self.push_ref(&t.0);
}
Value::Function(f) => {
self.push_ref(&f.0);
}
Value::Thread(t) => {
self.push_ref(&t.0);
}
Value::UserData(ud) => {
self.push_ref(&ud.0);
}
Value::String(s) => self.push_ref(&s.0),
Value::Table(t) => self.push_ref(&t.0),
Value::Function(f) => self.push_ref(&f.0),
Value::Thread(t) => self.push_ref(&t.0),
Value::UserData(ud) => self.push_ref(&ud.0),
Value::Error(err) => {
let protect = !self.unlikely_memory_error();
push_gc_userdata(state, WrappedFailure::Error(err), protect)?;
push_gc_userdata(state, WrappedFailure::Error(err.clone()), protect)?;
}
}
Ok(())
}
@@ -2549,6 +2546,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.
//
@@ -2601,6 +2607,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>,
@@ -2615,12 +2631,12 @@ impl Lua {
let metatable_nrec = metatable_nrec + registry.async_meta_methods.len();
push_table(state, 0, metatable_nrec, true)?;
for (k, m) in registry.meta_methods {
self.push_value(Value::Function(self.create_callback(m)?))?;
self.push(self.create_callback(m)?)?;
rawset_field(state, -2, MetaMethod::validate(&k)?)?;
}
#[cfg(feature = "async")]
for (k, m) in registry.async_meta_methods {
self.push_value(Value::Function(self.create_async_callback(m)?))?;
self.push(self.create_async_callback(m)?)?;
rawset_field(state, -2, MetaMethod::validate(&k)?)?;
}
let mut has_name = false;
@@ -2671,7 +2687,7 @@ impl Lua {
if field_getters_nrec > 0 {
push_table(state, 0, field_getters_nrec, true)?;
for (k, m) in registry.field_getters {
self.push_value(Value::Function(self.create_callback(m)?))?;
self.push(self.create_callback(m)?)?;
rawset_field(state, -2, &k)?;
}
field_getters_index = Some(ffi::lua_absindex(state, -1));
@@ -2683,7 +2699,7 @@ impl Lua {
if field_setters_nrec > 0 {
push_table(state, 0, field_setters_nrec, true)?;
for (k, m) in registry.field_setters {
self.push_value(Value::Function(self.create_callback(m)?))?;
self.push(self.create_callback(m)?)?;
rawset_field(state, -2, &k)?;
}
field_setters_index = Some(ffi::lua_absindex(state, -1));
@@ -2706,12 +2722,12 @@ impl Lua {
}
}
for (k, m) in registry.methods {
self.push_value(Value::Function(self.create_callback(m)?))?;
self.push(self.create_callback(m)?)?;
rawset_field(state, -2, &k)?;
}
#[cfg(feature = "async")]
for (k, m) in registry.async_methods {
self.push_value(Value::Function(self.create_async_callback(m)?))?;
self.push(self.create_async_callback(m)?)?;
rawset_field(state, -2, &k)?;
}
match index_type {
@@ -2944,11 +2960,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 {
@@ -2956,10 +2975,10 @@ 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)?))?;
lua.push(lua.create_sequence_from(results)?)?;
Ok(2)
}
}
@@ -3000,20 +3019,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
@@ -3027,7 +3043,7 @@ impl Lua {
return unpack(res, nres)
end
end
yield(pending)
yield(res) -- `res` is a "pending" value
end
"#,
)
@@ -3049,6 +3065,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,
@@ -3211,6 +3235,12 @@ impl LuaInner {
}
}
impl ExtraData {
// 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);
impl<'a> StateGuard<'a> {
+28
View File
@@ -23,6 +23,20 @@ impl<'lua, T: IntoLua<'lua>, E: IntoLua<'lua>> IntoLuaMulti<'lua> for StdResult<
}
Ok(result)
}
#[inline]
unsafe fn push_into_stack_multi(self, lua: &'lua Lua) -> Result<c_int> {
match self {
Ok(v) => v.push_into_stack(lua).map(|_| 1),
Err(e) => {
let state = lua.state();
check_stack(state, 3)?;
ffi::lua_pushnil(state);
e.push_into_stack(lua)?;
Ok(2)
}
}
}
}
impl<'lua, E: IntoLua<'lua>> IntoLuaMulti<'lua> for StdResult<(), E> {
@@ -38,6 +52,20 @@ impl<'lua, E: IntoLua<'lua>> IntoLuaMulti<'lua> for StdResult<(), E> {
}
}
}
#[inline]
unsafe fn push_into_stack_multi(self, lua: &'lua Lua) -> Result<c_int> {
match self {
Ok(_) => Ok(0),
Err(e) => {
let state = lua.state();
check_stack(state, 3)?;
ffi::lua_pushnil(state);
e.push_into_stack(lua)?;
Ok(2)
}
}
}
}
impl<'lua, T: IntoLua<'lua>> IntoLuaMulti<'lua> for T {
+5 -5
View File
@@ -19,7 +19,7 @@ use crate::util::{
self, assert_stack, check_stack, init_userdata_metatable, push_string, push_table,
rawset_field, short_type_name, take_userdata, StackGuard,
};
use crate::value::{FromLua, FromLuaMulti, IntoLua, IntoLuaMulti, Value};
use crate::value::{FromLua, FromLuaMulti, IntoLua, IntoLuaMulti};
#[cfg(feature = "lua54")]
use crate::userdata::USER_VALUE_MAXSLOT;
@@ -405,7 +405,7 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
let meta_methods_nrec = registry.meta_methods.len() + registry.meta_fields.len() + 1;
push_table(state, 0, meta_methods_nrec, true)?;
for (k, m) in registry.meta_methods {
lua.push_value(Value::Function(wrap_method(self, ud_ptr, &k, m)?))?;
lua.push(wrap_method(self, ud_ptr, &k, m)?)?;
rawset_field(state, -2, MetaMethod::validate(&k)?)?;
}
let mut has_name = false;
@@ -455,7 +455,7 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
if field_getters_nrec > 0 {
push_table(state, 0, field_getters_nrec, true)?;
for (k, m) in registry.field_getters {
lua.push_value(Value::Function(wrap_method(self, ud_ptr, &k, m)?))?;
lua.push(wrap_method(self, ud_ptr, &k, m)?)?;
rawset_field(state, -2, &k)?;
}
field_getters_index = Some(ffi::lua_absindex(state, -1));
@@ -466,7 +466,7 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
if field_setters_nrec > 0 {
push_table(state, 0, field_setters_nrec, true)?;
for (k, m) in registry.field_setters {
lua.push_value(Value::Function(wrap_method(self, ud_ptr, &k, m)?))?;
lua.push(wrap_method(self, ud_ptr, &k, m)?)?;
rawset_field(state, -2, &k)?;
}
field_setters_index = Some(ffi::lua_absindex(state, -1));
@@ -478,7 +478,7 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
// Create table used for methods lookup
push_table(state, 0, methods_nrec, true)?;
for (k, m) in registry.methods {
lua.push_value(Value::Function(wrap_method(self, ud_ptr, &k, m)?))?;
lua.push(wrap_method(self, ud_ptr, &k, m)?)?;
rawset_field(state, -2, &k)?;
}
methods_index = Some(ffi::lua_absindex(state, -1));
+1 -1
View File
@@ -132,7 +132,7 @@ impl<'lua> String<'lua> {
}
}
/// Converts the string to a generic C pointer.
/// Converts this string to a generic C pointer.
///
/// There is no way to convert the pointer back to its original value.
///
+42 -33
View File
@@ -591,7 +591,7 @@ impl<'lua> Table<'lua> {
unsafe { ffi::lua_getreadonly(ref_thread, self.0.index) != 0 }
}
/// Converts the table to a generic C pointer.
/// Converts this table to a generic C pointer.
///
/// Different tables will give different pointers.
/// There is no way to convert the pointer back to its original value.
@@ -721,7 +721,6 @@ impl<'lua> Table<'lua> {
TableSequence {
table: self.0,
index: 1,
len: None,
_phantom: PhantomData,
}
}
@@ -733,17 +732,25 @@ impl<'lua> Table<'lua> {
}
#[cfg(feature = "serialize")]
pub(crate) fn sequence_values_by_len<V: FromLua<'lua>>(
self,
len: Option<usize>,
) -> TableSequence<'lua, V> {
let len = len.unwrap_or_else(|| self.raw_len()) as Integer;
TableSequence {
table: self.0,
index: 1,
len: Some(len),
_phantom: PhantomData,
pub(crate) fn for_each_value<V>(&self, mut f: impl FnMut(V) -> Result<()>) -> Result<()>
where
V: FromLua<'lua>,
{
let lua = self.0.lua;
let state = lua.state();
unsafe {
let _sg = StackGuard::new(state);
check_stack(state, 4)?;
lua.push_ref(&self.0);
let len = ffi::lua_rawlen(state, -1);
for i in 1..=len {
ffi::lua_rawgeti(state, -1, i as _);
f(V::from_stack(-1, lua)?)?;
ffi::lua_pop(state, 1);
}
}
Ok(())
}
/// Sets element value at position `idx` without invoking metamethods.
@@ -1085,6 +1092,13 @@ impl<'a, 'lua> Serialize for SerializableTable<'a, 'lua> {
use crate::serde::de::{check_value_for_skip, MapPairs};
use crate::value::SerializableValue;
let convert_result = |res: Result<()>, serialize_err: Option<S::Error>| match res {
Ok(v) => Ok(v),
Err(Error::SerializeError(_)) if serialize_err.is_some() => Err(serialize_err.unwrap()),
Err(Error::SerializeError(msg)) => Err(serde::ser::Error::custom(msg)),
Err(err) => Err(serde::ser::Error::custom(err.to_string())),
};
let options = self.options;
let visited = &self.visited;
visited.borrow_mut().insert(self.table.to_pointer());
@@ -1093,15 +1107,21 @@ impl<'a, 'lua> Serialize for SerializableTable<'a, 'lua> {
let len = self.table.raw_len();
if len > 0 || self.table.is_array() {
let mut seq = serializer.serialize_seq(Some(len))?;
for value in self.table.clone().sequence_values_by_len::<Value>(None) {
let value = &value.map_err(serde::ser::Error::custom)?;
let skip = check_value_for_skip(value, self.options, &self.visited)
.map_err(serde::ser::Error::custom)?;
let mut serialize_err = None;
let res = self.table.for_each_value::<Value>(|value| {
let skip = check_value_for_skip(&value, self.options, &self.visited)
.map_err(|err| Error::SerializeError(err.to_string()))?;
if skip {
continue;
// continue iteration
return Ok(());
}
seq.serialize_element(&SerializableValue::new(value, options, Some(visited)))?;
}
seq.serialize_element(&SerializableValue::new(&value, options, Some(visited)))
.map_err(|err| {
serialize_err = Some(err);
Error::SerializeError(String::new())
})
});
convert_result(res, serialize_err)?;
return seq.end();
}
@@ -1138,18 +1158,7 @@ impl<'a, 'lua> Serialize for SerializableTable<'a, 'lua> {
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()));
}
}
convert_result(res, serialize_err)?;
map.end()
}
}
@@ -1219,9 +1228,9 @@ where
///
/// [`Table::sequence_values`]: crate::Table::sequence_values
pub struct TableSequence<'lua, V> {
// TODO: Use `&Table`
table: LuaRef<'lua>,
index: Integer,
len: Option<Integer>,
_phantom: PhantomData<V>,
}
@@ -1242,7 +1251,7 @@ where
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,
ffi::LUA_TNIL => None,
_ => {
self.index += 1;
Some(V::from_stack(-1, lua))
+14 -9
View File
@@ -1,4 +1,4 @@
use std::os::raw::c_int;
use std::os::raw::{c_int, c_void};
use crate::error::{Error, Result};
#[allow(unused)]
@@ -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();
@@ -375,6 +375,16 @@ impl<'lua> Thread<'lua> {
}
}
/// Converts this thread to a generic C pointer.
///
/// There is no way to convert the pointer back to its original value.
///
/// Typically this function is used only for hashing and debug information.
#[inline]
pub fn to_pointer(&self) -> *const c_void {
self.0.to_pointer()
}
/// Convert this handle to owned version.
#[cfg(all(feature = "unstable", any(not(feature = "send"), doc)))]
#[cfg_attr(docsrs, doc(cfg(all(feature = "unstable", not(feature = "send")))))]
@@ -530,12 +540,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")]
+15 -5
View File
@@ -5,7 +5,7 @@ use std::fmt;
use std::hash::Hash;
use std::mem;
use std::ops::{Deref, DerefMut};
use std::os::raw::{c_char, c_int};
use std::os::raw::{c_char, c_int, c_void};
use std::string::String as StdString;
#[cfg(feature = "async")]
@@ -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(())
@@ -919,7 +919,7 @@ impl<'lua> AnyUserData<'lua> {
check_stack(state, 5)?;
lua.push_userdata_ref(&self.0)?;
lua.push_value(v.into_lua(lua)?)?;
lua.push(v)?;
#[cfg(feature = "lua54")]
if n < USER_VALUE_MAXSLOT {
@@ -1014,7 +1014,7 @@ impl<'lua> AnyUserData<'lua> {
check_stack(state, 5)?;
lua.push_userdata_ref(&self.0)?;
lua.push_value(v.into_lua(lua)?)?;
lua.push(v)?;
// Multiple (extra) user values are emulated by storing them in a table
protect_lua!(state, 2, 0, |state| {
@@ -1096,6 +1096,16 @@ impl<'lua> AnyUserData<'lua> {
}
}
/// Converts this userdata to a generic C pointer.
///
/// There is no way to convert the pointer back to its original value.
///
/// Typically this function is used only for hashing and debug information.
#[inline]
pub fn to_pointer(&self) -> *const c_void {
self.0.to_pointer()
}
/// Convert this handle to owned version.
#[cfg(all(feature = "unstable", any(not(feature = "send"), doc)))]
#[cfg_attr(docsrs, doc(cfg(all(feature = "unstable", not(feature = "send")))))]
+2 -2
View File
@@ -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 {
+2
View File
@@ -1045,6 +1045,8 @@ pub(crate) unsafe fn to_string(state: *mut ffi::lua_State, index: c_int) -> Stri
ffi::LUA_TTHREAD => format!("<thread {:?}>", ffi::lua_topointer(state, index)),
#[cfg(feature = "luau")]
ffi::LUA_TBUFFER => format!("<buffer {:?}>", ffi::lua_topointer(state, index)),
#[cfg(feature = "luajit")]
ffi::LUA_TCDATA => format!("<cdata {:?}>", ffi::lua_topointer(state, index)),
_ => "<unknown>".to_string(),
}
}
+8 -1
View File
@@ -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#"
+6
View File
@@ -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 {
+40 -8
View File
@@ -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
+6 -3
View File
@@ -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`
+50 -10
View File
@@ -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();
| -------------------------------------------^^^^^--
| | |
+20 -24
View File
@@ -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
+264 -1
View File
@@ -3,7 +3,270 @@ 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, RegistryKey, Result, Table, Thread, UserDataRef,
Value,
};
#[test]
fn test_value_into_lua() -> Result<()> {
let lua = Lua::new();
// Direct conversion
let v = Value::Boolean(true);
let v2 = (&v).into_lua(&lua)?;
assert_eq!(v, v2);
// Push into stack
let table = lua.create_table()?;
table.set("v", &v)?;
assert_eq!(v, table.get::<_, Value>("v")?);
Ok(())
}
#[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_registry_key_from_lua() -> Result<()> {
let lua = Lua::new();
let fkey = lua.load("function() return 1 end").eval::<RegistryKey>()?;
let f = lua.registry_value::<Function>(&fkey)?;
assert_eq!(f.call::<_, i32>(())?, 1);
Ok(())
}
#[test]
fn test_conv_vec() -> Result<()> {
+13
View File
@@ -231,6 +231,19 @@ fn test_function_info() -> Result<()> {
Ok(())
}
#[test]
fn test_function_pointer() -> Result<()> {
let lua = Lua::new();
let func1 = lua.load("return function() end").into_function()?;
let func2 = func1.call::<_, Function>(())?;
assert_eq!(func1.to_pointer(), func1.clone().to_pointer());
assert_ne!(func1.to_pointer(), func2.to_pointer());
Ok(())
}
#[test]
fn test_function_wrap() -> Result<()> {
use mlua::Error;
+22
View File
@@ -28,6 +28,12 @@ fn test_require() -> Result<()> {
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();
@@ -269,6 +275,22 @@ fn test_sandbox() -> Result<()> {
Ok(())
}
#[test]
fn test_sandbox_nolibs() -> Result<()> {
let lua = Lua::new_with(StdLib::NONE, LuaOptions::default()).unwrap();
lua.sandbox(true)?;
lua.load("global = 123").exec()?;
let n: i32 = lua.load("return global").eval()?;
assert_eq!(n, 123);
assert_eq!(lua.globals().get::<_, Option<i32>>("global")?, Some(123));
lua.sandbox(false)?;
assert_eq!(lua.globals().get::<_, Option<i32>>("global")?, None);
Ok(())
}
#[test]
fn test_sandbox_threads() -> Result<()> {
let lua = Lua::new();
+25 -1
View File
@@ -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") {
+60
View File
@@ -0,0 +1,60 @@
use mlua::{Error, ExternalError, IntoLuaMulti, Lua, Result, String, Value};
#[test]
fn test_result_conversions() -> Result<()> {
let lua = Lua::new();
let globals = lua.globals();
let ok = lua.create_function(|_, ()| Ok(Ok::<(), Error>(())))?;
let err = lua.create_function(|_, ()| Ok(Err::<(), _>("failure1".into_lua_err())))?;
let ok2 = lua.create_function(|_, ()| Ok(Ok::<_, Error>("!".to_owned())))?;
let err2 = lua.create_function(|_, ()| Ok(Err::<String, _>("failure2".into_lua_err())))?;
globals.set("ok", ok)?;
globals.set("ok2", ok2)?;
globals.set("err", err)?;
globals.set("err2", err2)?;
lua.load(
r#"
local r, e = ok()
assert(r == nil and e == nil)
local r, e = err()
assert(r == nil)
assert(tostring(e):find("failure1") ~= nil)
local r, e = ok2()
assert(r == "!")
assert(e == nil)
local r, e = err2()
assert(r == nil)
assert(tostring(e):find("failure2") ~= nil)
"#,
)
.exec()?;
// Try to convert Result into MultiValue
let ok1 = Ok::<(), Error>(());
let multi_ok1 = ok1.into_lua_multi(&lua)?;
assert_eq!(multi_ok1.len(), 0);
let err1 = Err::<(), _>("failure1");
let multi_err1 = err1.into_lua_multi(&lua)?;
assert_eq!(multi_err1.len(), 2);
assert_eq!(multi_err1[0], Value::Nil);
assert_eq!(multi_err1[1].as_str().unwrap(), "failure1");
let ok2 = Ok::<_, Error>("!");
let multi_ok2 = ok2.into_lua_multi(&lua)?;
assert_eq!(multi_ok2.len(), 1);
assert_eq!(multi_ok2[0].as_str().unwrap(), "!");
let err2 = Err::<String, _>("failure2".into_lua_err());
let multi_err2 = err2.into_lua_multi(&lua)?;
assert_eq!(multi_err2.len(), 2);
assert_eq!(multi_err2[0], Value::Nil);
assert!(matches!(multi_err2[1], Value::Error(_)));
assert_eq!(multi_err2[1].to_string()?, "failure2");
Ok(())
}
+1 -1
View File
@@ -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
View File
@@ -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(())
+13
View File
@@ -99,6 +99,19 @@ fn test_string_debug() -> Result<()> {
Ok(())
}
#[test]
fn test_string_pointer() -> Result<()> {
let lua = Lua::new();
let str1 = lua.create_string("hello")?;
let str2 = lua.create_string("hello")?;
// Lua uses string interning, so these should be the same
assert_eq!(str1.to_pointer(), str2.to_pointer());
Ok(())
}
#[cfg(all(feature = "unstable", not(feature = "send")))]
#[test]
fn test_owned_string() -> Result<()> {
+13
View File
@@ -369,6 +369,19 @@ fn test_table_eq() -> Result<()> {
Ok(())
}
#[test]
fn test_table_pointer() -> Result<()> {
let lua = Lua::new();
let table1 = lua.create_table()?;
let table2 = lua.create_table()?;
assert_eq!(table1.to_pointer(), table1.clone().to_pointer());
assert_ne!(table1.to_pointer(), table2.to_pointer());
Ok(())
}
#[test]
fn test_table_error() -> Result<()> {
let lua = Lua::new();
+23 -49
View File
@@ -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(())
}
@@ -499,44 +504,6 @@ fn test_panic() -> Result<()> {
Ok(())
}
#[test]
fn test_result_conversions() -> Result<()> {
let lua = Lua::new();
let globals = lua.globals();
let ok = lua.create_function(|_, ()| Ok(Ok::<(), Error>(())))?;
let err = lua.create_function(|_, ()| Ok(Err::<(), _>("failure1".into_lua_err())))?;
let ok2 = lua.create_function(|_, ()| Ok(Ok::<_, Error>("!".to_owned())))?;
let err2 = lua.create_function(|_, ()| Ok(Err::<String, _>("failure2".into_lua_err())))?;
globals.set("ok", ok)?;
globals.set("ok2", ok2)?;
globals.set("err", err)?;
globals.set("err2", err2)?;
lua.load(
r#"
local r, e = ok()
assert(r == nil and e == nil)
local r, e = err()
assert(r == nil)
assert(tostring(e):find("failure1") ~= nil)
local r, e = ok2()
assert(r == "!")
assert(e == nil)
local r, e = err2()
assert(r == nil)
assert(tostring(e):find("failure2") ~= nil)
"#,
)
.exec()?;
Ok(())
}
#[test]
fn test_num_conversion() -> Result<()> {
let lua = Lua::new();
@@ -947,6 +914,7 @@ fn test_application_data() -> Result<()> {
}
#[test]
#[cfg(not(target_arch = "wasm32"))]
fn test_recursion() -> Result<()> {
let lua = Lua::new();
@@ -959,21 +927,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 +958,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 +994,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();
@@ -1351,6 +1324,7 @@ fn test_luajit_cdata() -> Result<()> {
#[test]
#[cfg(feature = "send")]
#[cfg(not(target_arch = "wasm32"))]
fn test_send() {
let lua = Lua::new();
std::thread::spawn(move || {
+14 -1
View File
@@ -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(())
}) {
@@ -191,6 +191,19 @@ fn test_coroutine_panic() {
}
}
#[test]
fn test_thread_pointer() -> Result<()> {
let lua = Lua::new();
let func = lua.load("return 123").into_function()?;
let thread = lua.create_thread(func.clone())?;
assert_eq!(thread.to_pointer(), thread.clone().to_pointer());
assert_ne!(thread.to_pointer(), lua.current_thread().to_pointer());
Ok(())
}
#[cfg(all(feature = "unstable", not(feature = "send")))]
#[test]
fn test_owned_thread() -> Result<()> {
+19 -5
View File
@@ -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()
@@ -951,6 +951,20 @@ fn test_userdata_method_errors() -> Result<()> {
Ok(())
}
#[test]
fn test_userdata_pointer() -> Result<()> {
let lua = Lua::new();
let ud1 = lua.create_any_userdata("hello")?;
let ud2 = lua.create_any_userdata("hello")?;
assert_eq!(ud1.to_pointer(), ud1.clone().to_pointer());
// Different userdata objects with the same value should have different pointers
assert_ne!(ud1.to_pointer(), ud2.to_pointer());
Ok(())
}
#[cfg(all(feature = "unstable", not(feature = "send")))]
#[test]
fn test_owned_userdata() -> Result<()> {
@@ -991,9 +1005,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));