mirror of
https://github.com/mlua-rs/mlua
synced 2026-06-08 16:05:43 +00:00
Compare commits
11 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b93ace0224 | |||
| 5f37bf812d | |||
| 1f7e760d20 | |||
| 90bea4aa34 | |||
| 7775b4a99c | |||
| 1d9cda10eb | |||
| 7332c6a28c | |||
| 94670e3fdb | |||
| 335f433df4 | |||
| 2aed548747 | |||
| 6a77b5f003 |
@@ -0,0 +1,23 @@
|
||||
name: coverage
|
||||
on: [push]
|
||||
|
||||
jobs:
|
||||
test:
|
||||
name: coverage
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: xd009642/tarpaulin
|
||||
options: --security-opt seccomp=unconfined
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Generate code coverage
|
||||
run: |
|
||||
cargo tarpaulin --verbose --features lua53,vendored,async,send,serialize --out xml --exclude-files benches --exclude-files tests --exclude-files build --exclude-files src/ffi
|
||||
|
||||
- name: Upload to codecov.io
|
||||
uses: codecov/codecov-action@v1
|
||||
with:
|
||||
token: ${{secrets.CODECOV_TOKEN}}
|
||||
fail_ci_if_error: true
|
||||
@@ -1,3 +1,9 @@
|
||||
## v0.5.2
|
||||
|
||||
- Some performance optimisations (callbacks)
|
||||
- `ToLua` implementation for `Cow<str>` and `Cow<CStr>`
|
||||
- Fixed bug with `Scope` destruction of partially polled futures
|
||||
|
||||
## v0.5.1
|
||||
|
||||
- Support cross compilation that should work well for vendored builds (including LuaJIT with some restrictions)
|
||||
|
||||
+4
-3
@@ -1,12 +1,12 @@
|
||||
[package]
|
||||
name = "mlua"
|
||||
version = "0.5.1" # remember to update html_root_url and mlua_derive
|
||||
version = "0.5.2" # remember to update html_root_url and mlua_derive
|
||||
authors = ["Aleksandr Orlenko <zxteam@pm.me>", "kyren <catherine@chucklefish.org>"]
|
||||
edition = "2018"
|
||||
repository = "https://github.com/khvzak/mlua"
|
||||
documentation = "https://docs.rs/mlua"
|
||||
readme = "README.md"
|
||||
keywords = ["lua", "luajit", "async", "futures"]
|
||||
keywords = ["lua", "luajit", "async", "futures", "scripting"]
|
||||
categories = ["api-bindings", "asynchronous"]
|
||||
license = "MIT"
|
||||
links = "lua"
|
||||
@@ -58,7 +58,7 @@ luajit-src = { version = ">= 210.1.2, < 220.0.0", optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
rustyline = "7.0"
|
||||
criterion = "0.3"
|
||||
criterion = { version = "0.3.4", features = ["html_reports", "async_tokio"] }
|
||||
trybuild = "1.0"
|
||||
futures = "0.3.5"
|
||||
hyper = { version = "0.14", features = ["client", "server"] }
|
||||
@@ -70,6 +70,7 @@ serde_json = "1.0"
|
||||
[[bench]]
|
||||
name = "benchmark"
|
||||
harness = false
|
||||
required-features = ["async"]
|
||||
|
||||
[[example]]
|
||||
name = "async_http_client"
|
||||
|
||||
@@ -3,7 +3,7 @@ below:
|
||||
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2019 A. Orlenko
|
||||
Copyright (c) 2019-2021 A. Orlenko
|
||||
Copyright (c) 2017 rlua
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# mlua
|
||||
[![Build Status]][github-actions] [![Latest Version]][crates.io] [![API Documentation]][docs.rs]
|
||||
[![Build Status]][github-actions] [![Latest Version]][crates.io] [![API Documentation]][docs.rs] [![Coverage Status]][codecov.io]
|
||||
|
||||
[Build Status]: https://github.com/khvzak/mlua/workflows/CI/badge.svg
|
||||
[github-actions]: https://github.com/khvzak/mlua/actions
|
||||
@@ -7,29 +7,53 @@
|
||||
[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
|
||||
|
||||
[Guided Tour](examples/guided_tour.rs)
|
||||
|
||||
`mlua` is bindings to [Lua](https://www.lua.org) programming language for Rust with a goal to provide
|
||||
_safe_ (as far as it's possible), high level, easy to use, practical and flexible API.
|
||||
|
||||
Started as [rlua v0.15](https://github.com/amethyst/rlua/tree/0.15.3) fork, `mlua` supports *__all__* major Lua versions (including LuaJIT) and allows to write native Lua modules in Rust as well as use Lua in a standalone mode.
|
||||
Started as [rlua](https://github.com/amethyst/rlua/tree/0.15.3) fork, `mlua` supports Lua 5.4, 5.3, 5.2 and 5.1 including LuaJIT (2.0.5 and 2.1 beta) and allows to write native Lua modules in Rust as well as use Lua in a standalone mode.
|
||||
|
||||
`mlua` supports the following Lua versions (and tested on Windows/macOS/Linux):
|
||||
- Lua 5.4 (`feature = "lua54"`)
|
||||
- Lua 5.3 (`feature = "lua53"`)
|
||||
- Lua 5.2 (`feature = "lua52"`)
|
||||
- Lua 5.1 (`feature = "lua51"`)
|
||||
- LuaJIT 2.1.0 beta (`feature = "luajit"`)
|
||||
- LuaJIT 2.0.5 stable (`feature = "luajit"`)
|
||||
`mlua` tested on Windows/macOS/Linux including module mode in [GitHub Actions] on `x86_64` platform and cross-compilation to `aarch64` (other targes are also supported).
|
||||
|
||||
Additional `feature = "vendored"` enables building static Lua from sources during `mlua` compilation.
|
||||
[GitHub Actions]: https://github.com/khvzak/mlua/actions
|
||||
|
||||
## Usage
|
||||
|
||||
### Feature flags
|
||||
|
||||
`mlua` uses feature flags to reduce the amount of depenendies, compiled code and allow to choose only required set of features.
|
||||
Below is a list of the available feature flags. By default `mlua` does not enable any features.
|
||||
|
||||
* `lua54`: activate Lua [5.4] support
|
||||
* `lua53`: activate Lua [5.3] support
|
||||
* `lua52`: activate Lua [5.2] support
|
||||
* `lua51`: activate Lua [5.1] support
|
||||
* `luajit`: activate [LuaJIT] support
|
||||
* `vendored`: build static Lua(JIT) library from sources during `mlua` compilation using [lua-src] or [luajit-src] crates
|
||||
* `module`: enable module mode (building loadable `cdylib` library for Lua)
|
||||
* `async`: enable async/await support (any executor can be used, eg. [tokio] or [async-std])
|
||||
* `send`: make `mlua::Lua` transferable across thread boundaries (adds [`Send`] requirement to `mlua::Function` and `mlua::UserData`)
|
||||
* `serialize`: add serialization and deserialization support to `mlua` types usign [serde] framework
|
||||
|
||||
[5.4]: https://www.lua.org/manual/5.4/manual.html
|
||||
[5.3]: https://www.lua.org/manual/5.3/manual.html
|
||||
[5.2]: https://www.lua.org/manual/5.2/manual.html
|
||||
[5.1]: https://www.lua.org/manual/5.1/manual.html
|
||||
[LuaJIT]: https://luajit.org/
|
||||
[lua-src]: https://github.com/khvzak/lua-src-rs
|
||||
[luajit-src]: https://github.com/khvzak/luajit-src-rs
|
||||
[tokio]: https://github.com/tokio-rs/tokio
|
||||
[async-std]: https://github.com/async-rs/async-std
|
||||
[`Send`]: https://doc.rust-lang.org/std/marker/trait.Send.html
|
||||
[serde]: https://github.com/serde-rs/serde
|
||||
|
||||
### Async/await support
|
||||
|
||||
Starting from v0.3, `mlua` supports async/await for all Lua versions. This works using Lua [coroutines](https://www.lua.org/manual/5.3/manual.html#2.6) and require running [Thread](https://docs.rs/mlua/latest/mlua/struct.Thread.html) along with enabling `feature = "async"` in `Cargo.toml`.
|
||||
`mlua` supports async/await for all Lua versions. This works using Lua [coroutines](https://www.lua.org/manual/5.3/manual.html#2.6) and require running [Thread](https://docs.rs/mlua/latest/mlua/struct.Thread.html) along with enabling `feature = "async"` in `Cargo.toml`.
|
||||
|
||||
**Examples**:
|
||||
- [HTTP Client](examples/async_http_client.rs)
|
||||
@@ -39,7 +63,7 @@ Starting from v0.3, `mlua` supports async/await for all Lua versions. This works
|
||||
|
||||
### Serialization (serde) support
|
||||
|
||||
With `serialize` feature flag enabled, `mlua` allows you to serialize/deserialize any type that implements [`serde::Serialize`] and [`serde::Deserialize`] into/from [`mlua::Value`]. In addition `mlua` provides [`serde::Serialize`] trait implementation for it (including user data support).
|
||||
With `serialize` feature flag enabled, `mlua` allows you to serialize/deserialize any type that implements [`serde::Serialize`] and [`serde::Deserialize`] into/from [`mlua::Value`]. In addition `mlua` provides [`serde::Serialize`] trait implementation for it (including `UserData` support).
|
||||
|
||||
[Example](examples/serialize.rs)
|
||||
|
||||
@@ -66,11 +90,13 @@ my_project $ LUA_INC=$HOME/tmp/lua-5.2.4/src LUA_LIB=$HOME/tmp/lua-5.2.4/src LUA
|
||||
Just enable the `vendored` feature and cargo will automatically build and link specified lua/luajit version. This is the easiest way to get started with `mlua`.
|
||||
|
||||
### Standalone mode
|
||||
In a standalone mode `mlua` allows to add to your application scripting support with a gently configured Lua runtime to ensure safety and soundness.
|
||||
|
||||
Add to `Cargo.toml` :
|
||||
|
||||
``` toml
|
||||
[dependencies]
|
||||
mlua = { version = "0.5", features = ["lua53"] }
|
||||
mlua = { version = "0.5", features = ["lua53", "vendored"] }
|
||||
```
|
||||
|
||||
`main.rs`
|
||||
@@ -94,6 +120,7 @@ fn main() -> LuaResult<()> {
|
||||
```
|
||||
|
||||
### Module mode
|
||||
In a module mode `mlua` allows to create a compiled Lua module that can be loaded from Lua code using [`require`](https://www.lua.org/manual/5.3/manual.html#pdf-require). In this case `mlua` uses an external Lua runtime which could lead to potential unsafety due to unpredictability of the Lua environment and usage of libraries such as [`debug`](https://www.lua.org/manual/5.3/manual.html#6.10).
|
||||
|
||||
[Example](examples/module)
|
||||
|
||||
@@ -104,7 +131,7 @@ Add to `Cargo.toml` :
|
||||
crate-type = ["cdylib"]
|
||||
|
||||
[dependencies]
|
||||
mlua = { version = "0.5", features = ["lua53", "module"] }
|
||||
mlua = { version = "0.5", features = ["lua53", "vendored", "module"] }
|
||||
```
|
||||
|
||||
`lib.rs` :
|
||||
|
||||
+167
-99
@@ -11,13 +11,23 @@
|
||||
extern "system" {}
|
||||
|
||||
use criterion::{criterion_group, criterion_main, BatchSize, Criterion};
|
||||
use std::time::Duration;
|
||||
use tokio::runtime::Runtime;
|
||||
|
||||
use mlua::prelude::*;
|
||||
|
||||
fn collect_gc_twice(lua: &Lua) {
|
||||
lua.gc_collect().unwrap();
|
||||
lua.gc_collect().unwrap();
|
||||
}
|
||||
|
||||
fn create_table(c: &mut Criterion) {
|
||||
c.bench_function("create table", |b| {
|
||||
b.iter_batched_ref(
|
||||
|| Lua::new(),
|
||||
|lua| {
|
||||
let lua = Lua::new();
|
||||
|
||||
c.bench_function("create [table empty]", |b| {
|
||||
b.iter_batched(
|
||||
|| collect_gc_twice(&lua),
|
||||
|_| {
|
||||
lua.create_table().unwrap();
|
||||
},
|
||||
BatchSize::SmallInput,
|
||||
@@ -26,12 +36,14 @@ fn create_table(c: &mut Criterion) {
|
||||
}
|
||||
|
||||
fn create_array(c: &mut Criterion) {
|
||||
c.bench_function("create array 10", |b| {
|
||||
b.iter_batched_ref(
|
||||
|| Lua::new(),
|
||||
|lua| {
|
||||
let lua = Lua::new();
|
||||
|
||||
c.bench_function("create [array] 10", |b| {
|
||||
b.iter_batched(
|
||||
|| collect_gc_twice(&lua),
|
||||
|_| {
|
||||
let table = lua.create_table().unwrap();
|
||||
for i in 1..11 {
|
||||
for i in 1..=10 {
|
||||
table.set(i, i).unwrap();
|
||||
}
|
||||
},
|
||||
@@ -41,10 +53,12 @@ fn create_array(c: &mut Criterion) {
|
||||
}
|
||||
|
||||
fn create_string_table(c: &mut Criterion) {
|
||||
c.bench_function("create string table 10", |b| {
|
||||
b.iter_batched_ref(
|
||||
|| Lua::new(),
|
||||
|lua| {
|
||||
let lua = Lua::new();
|
||||
|
||||
c.bench_function("create [table string] 10", |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();
|
||||
@@ -56,30 +70,20 @@ fn create_string_table(c: &mut Criterion) {
|
||||
});
|
||||
}
|
||||
|
||||
fn call_add_function(c: &mut Criterion) {
|
||||
c.bench_function("call add function 3 10", |b| {
|
||||
fn call_lua_function(c: &mut Criterion) {
|
||||
let lua = Lua::new();
|
||||
|
||||
c.bench_function("call Lua function [sum] 3 10", |b| {
|
||||
b.iter_batched_ref(
|
||||
|| {
|
||||
let lua = Lua::new();
|
||||
let f = {
|
||||
let f: LuaFunction = lua
|
||||
.load(
|
||||
r#"
|
||||
function(a, b, c)
|
||||
return a + b + c
|
||||
end
|
||||
"#,
|
||||
)
|
||||
.eval()
|
||||
.unwrap();
|
||||
lua.create_registry_value(f).unwrap()
|
||||
};
|
||||
(lua, f)
|
||||
collect_gc_twice(&lua);
|
||||
lua.load("function(a, b, c) return a + b + c end")
|
||||
.eval::<LuaFunction>()
|
||||
.unwrap()
|
||||
},
|
||||
|(lua, f)| {
|
||||
let add_function: LuaFunction = lua.registry_value(f).unwrap();
|
||||
|function| {
|
||||
for i in 0..10 {
|
||||
let _result: i64 = add_function.call((i, i + 1, i + 2)).unwrap();
|
||||
let _result: i64 = function.call((i, i + 1, i + 2)).unwrap();
|
||||
}
|
||||
},
|
||||
BatchSize::SmallInput,
|
||||
@@ -87,72 +91,72 @@ fn call_add_function(c: &mut Criterion) {
|
||||
});
|
||||
}
|
||||
|
||||
fn call_add_callback(c: &mut Criterion) {
|
||||
c.bench_function("call callback add 2 10", |b| {
|
||||
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))
|
||||
.unwrap();
|
||||
lua.globals().set("callback", callback).unwrap();
|
||||
|
||||
c.bench_function("call Rust callback [sum] 3 10", |b| {
|
||||
b.iter_batched_ref(
|
||||
|| {
|
||||
let lua = Lua::new();
|
||||
let f = {
|
||||
let c: LuaFunction = lua
|
||||
.create_function(|_, (a, b, c): (i64, i64, i64)| Ok(a + b + c))
|
||||
.unwrap();
|
||||
lua.globals().set("callback", c).unwrap();
|
||||
let f: LuaFunction = lua
|
||||
.load(
|
||||
r#"
|
||||
function()
|
||||
for i = 1,10 do
|
||||
callback(i, i, i)
|
||||
end
|
||||
end
|
||||
"#,
|
||||
)
|
||||
.eval()
|
||||
.unwrap();
|
||||
lua.create_registry_value(f).unwrap()
|
||||
};
|
||||
(lua, f)
|
||||
collect_gc_twice(&lua);
|
||||
lua.load("function() for i = 1,10 do callback(i, i+1, i+2) end end")
|
||||
.eval::<LuaFunction>()
|
||||
.unwrap()
|
||||
},
|
||||
|(lua, f)| {
|
||||
let entry_function: LuaFunction = lua.registry_value(f).unwrap();
|
||||
entry_function.call::<_, ()>(()).unwrap();
|
||||
|function| {
|
||||
function.call::<_, ()>(()).unwrap();
|
||||
},
|
||||
BatchSize::SmallInput,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
fn call_append_callback(c: &mut Criterion) {
|
||||
c.bench_function("call callback append 10", |b| {
|
||||
fn call_async_sum_callback(c: &mut Criterion) {
|
||||
let lua = Lua::new();
|
||||
let callback = lua
|
||||
.create_async_function(|_, (a, b, c): (i64, i64, i64)| async move { 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) {
|
||||
let lua = Lua::new();
|
||||
let callback = lua
|
||||
.create_function(|_, (a, b): (LuaString, LuaString)| {
|
||||
Ok(format!("{}{}", a.to_str()?, b.to_str()?))
|
||||
})
|
||||
.unwrap();
|
||||
lua.globals().set("callback", callback).unwrap();
|
||||
|
||||
c.bench_function("call Rust callback [concat string] 10", |b| {
|
||||
b.iter_batched_ref(
|
||||
|| {
|
||||
let lua = Lua::new();
|
||||
let f = {
|
||||
let c: LuaFunction = lua
|
||||
.create_function(|_, (a, b): (LuaString, LuaString)| {
|
||||
Ok(format!("{}{}", a.to_str()?, b.to_str()?))
|
||||
})
|
||||
.unwrap();
|
||||
lua.globals().set("callback", c).unwrap();
|
||||
let f: LuaFunction = lua
|
||||
.load(
|
||||
r#"
|
||||
function()
|
||||
for _ = 1,10 do
|
||||
callback("a", "b")
|
||||
end
|
||||
end
|
||||
"#,
|
||||
)
|
||||
.eval()
|
||||
.unwrap();
|
||||
lua.create_registry_value(f).unwrap()
|
||||
};
|
||||
(lua, f)
|
||||
collect_gc_twice(&lua);
|
||||
lua.load("function() for i = 1,10 do callback('a', tostring(i)) end end")
|
||||
.eval::<LuaFunction>()
|
||||
.unwrap()
|
||||
},
|
||||
|(lua, f)| {
|
||||
let entry_function: LuaFunction = lua.registry_value(f).unwrap();
|
||||
entry_function.call::<_, ()>(()).unwrap();
|
||||
|function| {
|
||||
function.call::<_, ()>(()).unwrap();
|
||||
},
|
||||
BatchSize::SmallInput,
|
||||
);
|
||||
@@ -160,10 +164,12 @@ fn call_append_callback(c: &mut Criterion) {
|
||||
}
|
||||
|
||||
fn create_registry_values(c: &mut Criterion) {
|
||||
c.bench_function("create registry 10", |b| {
|
||||
b.iter_batched_ref(
|
||||
|| Lua::new(),
|
||||
|lua| {
|
||||
let lua = Lua::new();
|
||||
|
||||
c.bench_function("create [registry value] 10", |b| {
|
||||
b.iter_batched(
|
||||
|| collect_gc_twice(&lua),
|
||||
|_| {
|
||||
for _ in 0..10 {
|
||||
lua.create_registry_value(lua.pack(true).unwrap()).unwrap();
|
||||
}
|
||||
@@ -178,10 +184,12 @@ fn create_userdata(c: &mut Criterion) {
|
||||
struct UserData(i64);
|
||||
impl LuaUserData for UserData {}
|
||||
|
||||
c.bench_function("create userdata 10", |b| {
|
||||
b.iter_batched_ref(
|
||||
|| Lua::new(),
|
||||
|lua| {
|
||||
let lua = Lua::new();
|
||||
|
||||
c.bench_function("create [table userdata] 10", |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();
|
||||
@@ -192,20 +200,80 @@ fn create_userdata(c: &mut Criterion) {
|
||||
});
|
||||
}
|
||||
|
||||
fn call_userdata_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));
|
||||
}
|
||||
}
|
||||
|
||||
let lua = Lua::new();
|
||||
lua.globals().set("userdata", UserData(10)).unwrap();
|
||||
|
||||
c.bench_function("call [userdata method] 10", |b| {
|
||||
b.iter_batched_ref(
|
||||
|| {
|
||||
collect_gc_twice(&lua);
|
||||
lua.load("function() for i = 1,10 do userdata:method() end end")
|
||||
.eval::<LuaFunction>()
|
||||
.unwrap()
|
||||
},
|
||||
|function| {
|
||||
function.call::<_, ()>(()).unwrap();
|
||||
},
|
||||
BatchSize::SmallInput,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
fn call_async_userdata_method(c: &mut Criterion) {
|
||||
#[derive(Clone, Copy)]
|
||||
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) });
|
||||
}
|
||||
}
|
||||
|
||||
let lua = Lua::new();
|
||||
lua.globals().set("userdata", UserData(10)).unwrap();
|
||||
|
||||
c.bench_function("call async [userdata 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()
|
||||
},
|
||||
|function| async move {
|
||||
function.call_async::<_, ()>(()).await.unwrap();
|
||||
},
|
||||
BatchSize::SmallInput,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
criterion_group! {
|
||||
name = benches;
|
||||
config = Criterion::default()
|
||||
.sample_size(200)
|
||||
.sample_size(300)
|
||||
.measurement_time(Duration::from_secs(10))
|
||||
.noise_threshold(0.02);
|
||||
targets =
|
||||
create_table,
|
||||
create_array,
|
||||
create_string_table,
|
||||
call_add_function,
|
||||
call_add_callback,
|
||||
call_append_callback,
|
||||
call_lua_function,
|
||||
call_sum_callback,
|
||||
call_async_sum_callback,
|
||||
call_concat_callback,
|
||||
create_registry_values,
|
||||
create_userdata,
|
||||
call_userdata_method,
|
||||
call_async_userdata_method,
|
||||
}
|
||||
|
||||
criterion_main!(benches);
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
use std::borrow::Cow;
|
||||
use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet};
|
||||
use std::ffi::{CStr, CString};
|
||||
use std::hash::{BuildHasher, Hash};
|
||||
@@ -222,6 +223,12 @@ impl<'lua> ToLua<'lua> for &str {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> ToLua<'lua> for Cow<'_, str> {
|
||||
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
|
||||
Ok(Value::String(lua.create_string(self.as_bytes())?))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> ToLua<'lua> for CString {
|
||||
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
|
||||
Ok(Value::String(lua.create_string(self.as_bytes())?))
|
||||
@@ -256,6 +263,12 @@ impl<'lua> ToLua<'lua> for &CStr {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> ToLua<'lua> for Cow<'_, CStr> {
|
||||
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
|
||||
Ok(Value::String(lua.create_string(self.to_bytes())?))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> ToLua<'lua> for BString {
|
||||
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
|
||||
Ok(Value::String(lua.create_string(&self)?))
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
// The MIT License (MIT)
|
||||
//
|
||||
// Copyright (c) 2019 A. Orlenko
|
||||
// Copyright (c) 2019-2021 A. Orlenko
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
// The MIT License (MIT)
|
||||
//
|
||||
// Copyright (c) 2019-2020 A. Orlenko
|
||||
// Copyright (c) 2019-2021 A. Orlenko
|
||||
// Copyright (c) 2014 J.C. Moyer
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
// The MIT License (MIT)
|
||||
//
|
||||
// Copyright (c) 2019-2020 A. Orlenko
|
||||
// Copyright (c) 2019-2021 A. Orlenko
|
||||
// Copyright (c) 2014 J.C. Moyer
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
// The MIT License (MIT)
|
||||
//
|
||||
// Copyright (c) 2019-2020 A. Orlenko
|
||||
// Copyright (c) 2019-2021 A. Orlenko
|
||||
// Copyright (c) 2014 J.C. Moyer
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
// The MIT License (MIT)
|
||||
//
|
||||
// Copyright (c) 2019-2020 A. Orlenko
|
||||
// Copyright (c) 2019-2021 A. Orlenko
|
||||
// Copyright (c) 2014 J.C. Moyer
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
// The MIT License (MIT)
|
||||
//
|
||||
// Copyright (c) 2019-2020 A. Orlenko
|
||||
// Copyright (c) 2019-2021 A. Orlenko
|
||||
// Copyright (c) 2014 J.C. Moyer
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
// The MIT License (MIT)
|
||||
//
|
||||
// Copyright (c) 2019-2020 A. Orlenko
|
||||
// Copyright (c) 2019-2021 A. Orlenko
|
||||
// Copyright (c) 2014 J.C. Moyer
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
|
||||
+1
-1
@@ -70,7 +70,7 @@
|
||||
//! [`serde::Deserialize`]: https://docs.serde.rs/serde/de/trait.Deserialize.html
|
||||
|
||||
// mlua types in rustdoc of other crates get linked to here.
|
||||
#![doc(html_root_url = "https://docs.rs/mlua/0.5.1")]
|
||||
#![doc(html_root_url = "https://docs.rs/mlua/0.5.2")]
|
||||
// Deny warnings inside doc tests / examples. When this isn't present, rustdoc doesn't show *any*
|
||||
// warnings at all.
|
||||
#![doc(test(attr(deny(warnings))))]
|
||||
|
||||
+37
-43
@@ -1,6 +1,6 @@
|
||||
use std::any::TypeId;
|
||||
use std::cell::{RefCell, UnsafeCell};
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::collections::HashMap;
|
||||
use std::ffi::CString;
|
||||
use std::marker::PhantomData;
|
||||
use std::os::raw::{c_char, c_int, c_void};
|
||||
@@ -22,10 +22,10 @@ use crate::types::{
|
||||
};
|
||||
use crate::userdata::{AnyUserData, MetaMethod, UserData, UserDataMethods, UserDataWrapped};
|
||||
use crate::util::{
|
||||
assert_stack, callback_error, check_stack, get_gc_userdata, get_main_state,
|
||||
get_meta_gc_userdata, get_wrapped_error, init_error_registry, init_gc_metatable_for,
|
||||
init_userdata_metatable, pop_error, protect_lua, protect_lua_closure, push_gc_userdata,
|
||||
push_meta_gc_userdata, push_string, push_userdata, push_wrapped_error, StackGuard,
|
||||
assert_stack, callback_error, check_stack, get_gc_userdata, get_main_state, get_userdata,
|
||||
get_wrapped_error, init_error_registry, init_gc_metatable_for, init_userdata_metatable,
|
||||
pop_error, protect_lua, protect_lua_closure, push_gc_userdata, push_meta_gc_userdata,
|
||||
push_string, push_userdata, push_wrapped_error, StackGuard,
|
||||
};
|
||||
use crate::value::{FromLua, FromLuaMulti, MultiValue, Nil, ToLua, ToLuaMulti, Value};
|
||||
|
||||
@@ -57,7 +57,6 @@ pub struct Lua {
|
||||
// Data associated with the lua_State.
|
||||
struct ExtraData {
|
||||
registered_userdata: HashMap<TypeId, c_int>,
|
||||
registered_userdata_mt: HashSet<isize>,
|
||||
registry_unref_list: Arc<Mutex<Option<Vec<c_int>>>>,
|
||||
|
||||
libs: StdLib,
|
||||
@@ -322,7 +321,6 @@ impl Lua {
|
||||
|
||||
let extra = Arc::new(Mutex::new(ExtraData {
|
||||
registered_userdata: HashMap::new(),
|
||||
registered_userdata_mt: HashSet::new(),
|
||||
registry_unref_list: Arc::new(Mutex::new(Some(Vec::new()))),
|
||||
ref_thread,
|
||||
libs: StdLib::NONE,
|
||||
@@ -1504,9 +1502,10 @@ impl Lua {
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn userdata_metatable<T: 'static + UserData>(&self) -> Result<c_int> {
|
||||
let type_id = TypeId::of::<T>();
|
||||
if let Some(table_id) = mlua_expect!(self.extra.lock(), "extra is poisoned")
|
||||
.registered_userdata
|
||||
.get(&TypeId::of::<T>())
|
||||
.get(&type_id)
|
||||
{
|
||||
return Ok(*table_id);
|
||||
}
|
||||
@@ -1560,20 +1559,17 @@ impl Lua {
|
||||
ffi::lua_pop(self.state, 1);
|
||||
}
|
||||
|
||||
let (ptr, id) = protect_lua_closure(self.state, 1, 0, |state| {
|
||||
let ptr = ffi::lua_topointer(state, -1) as isize;
|
||||
let id = ffi::luaL_ref(state, ffi::LUA_REGISTRYINDEX);
|
||||
(ptr, id)
|
||||
let id = protect_lua_closure(self.state, 1, 0, |state| {
|
||||
ffi::luaL_ref(state, ffi::LUA_REGISTRYINDEX)
|
||||
})?;
|
||||
|
||||
let mut extra = mlua_expect!(self.extra.lock(), "extra is poisoned");
|
||||
extra.registered_userdata.insert(TypeId::of::<T>(), id);
|
||||
extra.registered_userdata_mt.insert(ptr);
|
||||
extra.registered_userdata.insert(type_id, id);
|
||||
|
||||
Ok(id)
|
||||
}
|
||||
|
||||
// Pushes a LuaRef value onto the stack, checking that it's any registered userdata
|
||||
// Pushes a LuaRef value onto the stack, checking that it's not destructed
|
||||
// Uses 2 stack spaces, does not call checkstack
|
||||
#[cfg(feature = "serialize")]
|
||||
pub(crate) unsafe fn push_userdata_ref(&self, lref: &LuaRef) -> Result<()> {
|
||||
@@ -1581,19 +1577,13 @@ impl Lua {
|
||||
if ffi::lua_getmetatable(self.state, -1) == 0 {
|
||||
Err(Error::UserDataTypeMismatch)
|
||||
} else {
|
||||
// Check that this is our metatable
|
||||
let ptr = ffi::lua_topointer(self.state, -1) as isize;
|
||||
let extra = mlua_expect!(self.extra.lock(), "extra is poisoned");
|
||||
if !extra.registered_userdata_mt.contains(&ptr) {
|
||||
// Maybe UserData destructed?
|
||||
get_destructed_userdata_metatable(self.state);
|
||||
if ffi::lua_rawequal(self.state, -1, -2) == 1 {
|
||||
Err(Error::UserDataDestructed)
|
||||
} else {
|
||||
Err(Error::UserDataTypeMismatch)
|
||||
}
|
||||
// Check that userdata is not destructed
|
||||
get_destructed_userdata_metatable(self.state);
|
||||
let eq = ffi::lua_rawequal(self.state, -1, -2) == 1;
|
||||
ffi::lua_pop(self.state, 2);
|
||||
if eq {
|
||||
Err(Error::UserDataDestructed)
|
||||
} else {
|
||||
ffi::lua_pop(self.state, 1);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -1616,12 +1606,13 @@ impl Lua {
|
||||
{
|
||||
unsafe extern "C" fn call_callback(state: *mut ffi::lua_State) -> c_int {
|
||||
callback_error(state, |nargs| {
|
||||
let func =
|
||||
get_meta_gc_userdata::<Callback, Callback>(state, ffi::lua_upvalueindex(1));
|
||||
let lua = get_gc_userdata::<Lua>(state, ffi::lua_upvalueindex(2));
|
||||
if func.is_null() || lua.is_null() {
|
||||
if ffi::lua_type(state, ffi::lua_upvalueindex(1)) == ffi::LUA_TNIL
|
||||
|| ffi::lua_type(state, ffi::lua_upvalueindex(2)) == ffi::LUA_TNIL
|
||||
{
|
||||
return Err(Error::CallbackDestructed);
|
||||
}
|
||||
let func = get_userdata::<Callback>(state, ffi::lua_upvalueindex(1));
|
||||
let lua = get_userdata::<Lua>(state, ffi::lua_upvalueindex(2));
|
||||
|
||||
if nargs < ffi::LUA_MINSTACK {
|
||||
check_stack(state, ffi::LUA_MINSTACK - nargs)?;
|
||||
@@ -1681,14 +1672,13 @@ impl Lua {
|
||||
|
||||
unsafe extern "C" fn call_callback(state: *mut ffi::lua_State) -> c_int {
|
||||
callback_error(state, |nargs| {
|
||||
let func = get_meta_gc_userdata::<AsyncCallback, AsyncCallback>(
|
||||
state,
|
||||
ffi::lua_upvalueindex(1),
|
||||
);
|
||||
let lua = get_gc_userdata::<Lua>(state, ffi::lua_upvalueindex(2));
|
||||
if func.is_null() || lua.is_null() {
|
||||
if ffi::lua_type(state, ffi::lua_upvalueindex(1)) == ffi::LUA_TNIL
|
||||
|| ffi::lua_type(state, ffi::lua_upvalueindex(2)) == ffi::LUA_TNIL
|
||||
{
|
||||
return Err(Error::CallbackDestructed);
|
||||
}
|
||||
let func = get_userdata::<AsyncCallback>(state, ffi::lua_upvalueindex(1));
|
||||
let lua = get_userdata::<Lua>(state, ffi::lua_upvalueindex(2));
|
||||
|
||||
if nargs < ffi::LUA_MINSTACK {
|
||||
check_stack(state, ffi::LUA_MINSTACK - nargs)?;
|
||||
@@ -1715,14 +1705,16 @@ impl Lua {
|
||||
|
||||
unsafe extern "C" fn poll_future(state: *mut ffi::lua_State) -> c_int {
|
||||
callback_error(state, |nargs| {
|
||||
let fut = get_gc_userdata::<LocalBoxFuture<Result<MultiValue>>>(
|
||||
if ffi::lua_type(state, ffi::lua_upvalueindex(1)) == ffi::LUA_TNIL
|
||||
|| ffi::lua_type(state, ffi::lua_upvalueindex(2)) == ffi::LUA_TNIL
|
||||
{
|
||||
return Err(Error::CallbackDestructed);
|
||||
}
|
||||
let fut = get_userdata::<LocalBoxFuture<Result<MultiValue>>>(
|
||||
state,
|
||||
ffi::lua_upvalueindex(1),
|
||||
);
|
||||
let lua = get_gc_userdata::<Lua>(state, ffi::lua_upvalueindex(2));
|
||||
if fut.is_null() || lua.is_null() {
|
||||
return Err(Error::CallbackDestructed);
|
||||
}
|
||||
let lua = get_userdata::<Lua>(state, ffi::lua_upvalueindex(2));
|
||||
|
||||
if nargs < ffi::LUA_MINSTACK {
|
||||
check_stack(state, ffi::LUA_MINSTACK - nargs)?;
|
||||
@@ -1787,9 +1779,11 @@ impl Lua {
|
||||
})?,
|
||||
)?;
|
||||
|
||||
// We set `poll` variable in the env table to be able to destroy upvalues
|
||||
self.load(
|
||||
r#"
|
||||
local poll = get_poll(...)
|
||||
poll = get_poll(...)
|
||||
local poll, yield, unpack = poll, yield, unpack
|
||||
while true do
|
||||
ready, res = poll()
|
||||
if ready then
|
||||
|
||||
+1
-1
@@ -34,7 +34,7 @@ impl<'lua, T: ToLua<'lua>> ToLuaMulti<'lua> for T {
|
||||
|
||||
impl<'lua, T: FromLua<'lua>> FromLuaMulti<'lua> for T {
|
||||
fn from_lua_multi(mut values: MultiValue<'lua>, lua: &'lua Lua) -> Result<Self> {
|
||||
Ok(T::from_lua(values.pop_front().unwrap_or(Nil), lua)?)
|
||||
T::from_lua(values.pop_front().unwrap_or(Nil), lua)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+22
-7
@@ -23,9 +23,8 @@ use crate::value::{FromLuaMulti, MultiValue, ToLuaMulti, Value};
|
||||
#[cfg(feature = "async")]
|
||||
use {
|
||||
crate::types::AsyncCallback,
|
||||
futures_core::future::Future,
|
||||
futures_core::future::{Future, LocalBoxFuture},
|
||||
futures_util::future::{self, TryFutureExt},
|
||||
std::os::raw::c_char,
|
||||
};
|
||||
|
||||
/// Constructed by the [`Lua::scope`] method, allows temporarily creating Lua userdata and
|
||||
@@ -420,12 +419,11 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
|
||||
#[cfg(any(feature = "lua51", feature = "luajit"))]
|
||||
ffi::lua_getfenv(state, -1);
|
||||
|
||||
// Then, get the get_poll() closure using the corresponding key
|
||||
let key = "get_poll";
|
||||
ffi::lua_pushlstring(state, key.as_ptr() as *const c_char, key.len());
|
||||
// Second, get the `get_poll()` closure using the corresponding key
|
||||
ffi::lua_pushstring(state, cstr!("get_poll"));
|
||||
ffi::lua_rawget(state, -2);
|
||||
|
||||
// Finally, destroy all upvalues
|
||||
// Destroy all upvalues
|
||||
ffi::lua_getupvalue(state, -1, 1);
|
||||
let ud1 = take_userdata::<AsyncCallback>(state);
|
||||
ffi::lua_pushnil(state);
|
||||
@@ -437,8 +435,25 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
|
||||
ffi::lua_setupvalue(state, -2, 2);
|
||||
|
||||
ffi::lua_pop(state, 1);
|
||||
let mut data: Vec<Box<dyn Any>> = vec![Box::new(ud1), Box::new(ud2)];
|
||||
|
||||
vec![Box::new(ud1), Box::new(ud2)]
|
||||
// Finally, get polled future and destroy it
|
||||
ffi::lua_pushstring(state, cstr!("poll"));
|
||||
if ffi::lua_rawget(state, -2) == ffi::LUA_TFUNCTION {
|
||||
ffi::lua_getupvalue(state, -1, 1);
|
||||
let ud3 = take_userdata::<LocalBoxFuture<Result<MultiValue>>>(state);
|
||||
ffi::lua_pushnil(state);
|
||||
ffi::lua_setupvalue(state, -2, 1);
|
||||
data.push(Box::new(ud3));
|
||||
|
||||
ffi::lua_getupvalue(state, -1, 2);
|
||||
let ud4 = take_userdata::<Lua>(state);
|
||||
ffi::lua_pushnil(state);
|
||||
ffi::lua_setupvalue(state, -2, 2);
|
||||
data.push(Box::new(ud4));
|
||||
}
|
||||
|
||||
data
|
||||
}));
|
||||
|
||||
Ok(f)
|
||||
|
||||
@@ -20,7 +20,9 @@ impl<'lua, 'de> serde::Deserializer<'de> for Deserializer<'lua> {
|
||||
match self.0 {
|
||||
Value::Nil => visitor.visit_unit(),
|
||||
Value::Boolean(b) => visitor.visit_bool(b),
|
||||
#[allow(clippy::useless_conversion)]
|
||||
Value::Integer(i) => visitor.visit_i64(i.into()),
|
||||
#[allow(clippy::useless_conversion)]
|
||||
Value::Number(n) => visitor.visit_f64(n.into()),
|
||||
Value::String(s) => match s.to_str() {
|
||||
Ok(s) => visitor.visit_str(s),
|
||||
|
||||
+1
-5
@@ -279,15 +279,11 @@ pub unsafe fn push_meta_gc_userdata<MT: Any, T>(state: *mut ffi::lua_State, t: T
|
||||
|
||||
// Uses 2 stack spaces, does not call checkstack
|
||||
pub unsafe fn get_gc_userdata<T: Any>(state: *mut ffi::lua_State, index: c_int) -> *mut T {
|
||||
get_meta_gc_userdata::<T, T>(state, index)
|
||||
}
|
||||
|
||||
pub unsafe fn get_meta_gc_userdata<MT: Any, T>(state: *mut ffi::lua_State, index: c_int) -> *mut T {
|
||||
let ud = ffi::lua_touserdata(state, index) as *mut T;
|
||||
if ud.is_null() || ffi::lua_getmetatable(state, index) == 0 {
|
||||
return ptr::null_mut();
|
||||
}
|
||||
get_gc_metatable_for::<MT>(state);
|
||||
get_gc_metatable_for::<T>(state);
|
||||
let res = ffi::lua_rawequal(state, -1, -2) != 0;
|
||||
ffi::lua_pop(state, 2);
|
||||
if !res {
|
||||
|
||||
@@ -125,7 +125,9 @@ impl<'lua> Serialize for Value<'lua> {
|
||||
match self {
|
||||
Value::Nil => serializer.serialize_unit(),
|
||||
Value::Boolean(b) => serializer.serialize_bool(*b),
|
||||
#[allow(clippy::useless_conversion)]
|
||||
Value::Integer(i) => serializer.serialize_i64((*i).into()),
|
||||
#[allow(clippy::useless_conversion)]
|
||||
Value::Number(n) => serializer.serialize_f64((*n).into()),
|
||||
Value::String(s) => s.serialize(serializer),
|
||||
Value::Table(t) => t.serialize(serializer),
|
||||
|
||||
+18
-1
@@ -22,7 +22,9 @@ use std::time::Duration;
|
||||
use futures_timer::Delay;
|
||||
use futures_util::stream::TryStreamExt;
|
||||
|
||||
use mlua::{Error, Function, Lua, Result, Table, TableExt, UserData, UserDataMethods};
|
||||
use mlua::{
|
||||
Error, Function, Lua, Result, Table, TableExt, Thread, UserData, UserDataMethods, Value,
|
||||
};
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_async_function() -> Result<()> {
|
||||
@@ -332,11 +334,18 @@ async fn test_async_scope() -> Result<()> {
|
||||
let _ = f.call_async::<u64, ()>(10).await?;
|
||||
assert_eq!(Rc::strong_count(rc), 1);
|
||||
|
||||
// Create future in partialy polled state (Poll::Pending)
|
||||
let g = lua.create_thread(f)?;
|
||||
g.resume::<u64, ()>(10)?;
|
||||
lua.globals().set("g", g)?;
|
||||
assert_eq!(Rc::strong_count(rc), 2);
|
||||
|
||||
Ok(())
|
||||
});
|
||||
|
||||
assert_eq!(Rc::strong_count(rc), 1);
|
||||
let _ = fut.await?;
|
||||
assert_eq!(Rc::strong_count(rc), 1);
|
||||
|
||||
match lua
|
||||
.globals()
|
||||
@@ -351,6 +360,14 @@ async fn test_async_scope() -> Result<()> {
|
||||
r => panic!("improper return for destructed function: {:?}", r),
|
||||
};
|
||||
|
||||
match lua.globals().get::<_, Thread>("g")?.resume::<_, Value>(()) {
|
||||
Err(Error::CallbackError { ref cause, .. }) => match cause.as_ref() {
|
||||
Error::CallbackDestructed => {}
|
||||
e => panic!("expected `CallbackDestructed` error cause, got {:?}", e),
|
||||
},
|
||||
r => panic!("improper return for destructed function: {:?}", r),
|
||||
};
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user