Compare commits

..

27 Commits

Author SHA1 Message Date
Alex Orlenko c363fb9288 v0.5.3 2021-03-04 00:01:06 +00:00
Alex Orlenko 3900e23839 Fix compilation warnings on nightly 2021-03-03 23:36:28 +00:00
Alex Orlenko 726fde7e1f Optimise async callbacks (polling)
call async Rust callback [sum] 3 10
                        time:   [59.338 us 59.729 us 60.097 us]
                        change: [-10.336% -8.6212% -6.8003%] (p = 0.00 < 0.05)
                        Performance has improved.
2021-03-03 23:21:56 +00:00
Alex Orlenko 7cb9c4f39c Fix bug in returning nil-prefixed multi values from async function 2021-03-03 22:32:22 +00:00
Alex Orlenko b93ace0224 v0.5.2 2021-02-27 19:32:25 +00:00
Alex Orlenko 5f37bf812d Fix/whitelist some clippy warnings 2021-02-27 19:23:08 +00:00
Alex Orlenko 1f7e760d20 Add codecov coverage report 2021-02-27 18:03:53 +00:00
Alex Orlenko 90bea4aa34 Update README and keywords 2021-02-27 13:28:47 +00:00
Alex Orlenko 7775b4a99c Bump copyright year 2021-02-26 10:35:00 +00:00
Alex Orlenko 1d9cda10eb Add ToLua implementation for Cow<str> and Cow<CStr> 2021-02-26 10:23:36 +00:00
Alex Orlenko 7332c6a28c Remove registered_userdata_mt check 2021-02-22 20:38:36 +00:00
Alex Orlenko 94670e3fdb rustfmt 2021-02-22 20:13:56 +00:00
Alex Orlenko 335f433df4 Optimize callbacks 2021-02-21 23:52:20 +00:00
Alex Orlenko 2aed548747 Fix scoped async destruction of partially polled futures 2021-02-21 23:52:07 +00:00
Alex Orlenko 6a77b5f003 Update benchmarks:
- Refactor
- Add async benchmarks
2021-02-21 18:48:45 +00:00
Alex Orlenko aeb66115f7 v0.5.1 2021-01-20 11:04:03 +00:00
Alex Orlenko ce873a40bf Update CHANGELOG 2021-01-20 11:00:47 +00:00
Alex Orlenko 8de75d1c18 Update tokio to 1.0 for async examples 2021-01-20 10:47:27 +00:00
Alex Orlenko b6ff501b8c Fix numeric types conversion for 32bit lua. Fix #27 2021-01-20 10:46:23 +00:00
Alex Orlenko 0e73ae18f4 Update CI 2021-01-16 13:32:38 +00:00
Alex Orlenko e62fd400d7 Remove unused exports from glue.{c,rs} && Fix some clippy warnings 2021-01-16 13:31:45 +00:00
Alex Orlenko 1c79f646de Update README 2021-01-16 13:31:34 +00:00
Alex Orlenko 7f5fd36a2b Merge pull request #26 from wez/cross
Support cross compilation
2021-01-14 15:35:35 +00:00
Wez Furlong faf19e4a06 Allow luajit to build in the pointer size cross compilation case 2021-01-13 20:01:12 -08:00
Wez Furlong 24d9099ef7 install more bits for cross compilation jobs 2021-01-13 10:38:08 -08:00
Wez Furlong 84003f31e7 Add CI for cross compilation cases 2021-01-13 10:18:53 -08:00
Wez Furlong e0d9ec41e2 Support cross compilation
This commit teaches the build script to recognize when it is
cross-compiling and switch to an alternative approach for generating
the `glue.rs` module.

It defaults to the equivalent logic found in the lua headers to
set the default types and parameters.

Notably: it doesn't statically produce the default lua paths as we
cannot know these without either executing the code (not guaranteed
possible when cross compiling) or regexing out the paths from the
headers (a bit brittle).  An alternative approach might be to use
something like `lazy_static` to ask the library for its compiled in
values once at runtime.

I've tested this with:

```
cargo build --target armv7-unknown-linux-gnueabihf --features lua51,vendored
cargo build --target armv7-unknown-linux-gnueabihf --features lua52,vendored
cargo build --target armv7-unknown-linux-gnueabihf --features lua53,vendored
cargo build --target armv7-unknown-linux-gnueabihf --features lua54,vendored
cargo build --target armv7-unknown-linux-gnueabihf --features luajit,vendored
```

All except luajit compile.  Luajit itself doesn't cross compile, so I
don't think we can ever reasonably get that to work.

I haven't tried to run any of this yet; my use case is actually for mac
(https://github.com/wez/wezterm/pull/426) so I need to commit this and
try patching it in over there before I can see if that truly worked
end-to-end.

refs: https://github.com/khvzak/mlua/issues/14
2021-01-13 09:55:53 -08:00
30 changed files with 628 additions and 260 deletions
+23
View File
@@ -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
+65 -2
View File
@@ -32,10 +32,73 @@ jobs:
- name: Build ${{ matrix.lua }} pkg-config
if: ${{ matrix.os == 'ubuntu-18.04' && matrix.lua != 'lua54' }}
run: |
sudo apt-get update -y
sudo apt-get update
sudo apt-get install -y --no-install-recommends liblua5.3-dev liblua5.2-dev liblua5.1-0-dev libluajit-5.1-dev
cargo build --release --features "${{ matrix.lua }}"
build_aarch64_cross_macos:
name: Cross-compile to aarch64-apple-darwin
runs-on: macos-11.0
needs: build
strategy:
matrix:
lua: [lua54, lua53, lua52, lua51, luajit]
steps:
- uses: actions/checkout@v2
- uses: actions-rs/toolchain@v1
with:
toolchain: nightly
target: aarch64-apple-darwin
override: true
- name: Cross-compile
run: cargo build --target aarch64-apple-darwin --features "${{ matrix.lua }} async send serialize vendored"
build_aarch64_cross_ubuntu:
name: Cross-compile to aarch64-unknown-linux-gnu
runs-on: ubuntu-18.04
needs: build
strategy:
matrix:
lua: [lua54, lua53, lua52, lua51, luajit]
steps:
- uses: actions/checkout@v2
- uses: actions-rs/toolchain@v1
with:
toolchain: nightly
target: aarch64-unknown-linux-gnu
override: true
- name: Install ARM compiler toolchain
run: |
sudo apt-get update
sudo apt-get install -y --no-install-recommends gcc-aarch64-linux-gnu libc6-dev-arm64-cross
shell: bash
- name: Cross-compile
run: cargo build --target aarch64-unknown-linux-gnu --features "${{ matrix.lua }} async send serialize vendored"
shell: bash
build_armv7_cross_ubuntu:
name: Cross-compile to armv7-unknown-linux-gnueabihf
runs-on: ubuntu-18.04
needs: build
strategy:
matrix:
lua: [lua54, lua53, lua52, lua51]
steps:
- uses: actions/checkout@v2
- uses: actions-rs/toolchain@v1
with:
toolchain: nightly
target: armv7-unknown-linux-gnueabihf
override: true
- name: Install ARM compiler toolchain
run: |
sudo apt-get update
sudo apt-get install -y --no-install-recommends gcc-arm-linux-gnueabihf libc-dev-armhf-cross
shell: bash
- name: Cross-compile
run: cargo build --target armv7-unknown-linux-gnueabihf --features "${{ matrix.lua }} async send serialize vendored"
shell: bash
test:
name: Test
runs-on: ${{ matrix.os }}
@@ -94,7 +157,7 @@ jobs:
shell: bash
test_modules:
name: Test modules on Linux and macOS
name: Test modules
runs-on: ${{ matrix.os }}
needs: build
strategy:
+18
View File
@@ -1,7 +1,25 @@
## v0.5.3
- Fixed bug when returning nil-prefixed multi values from async function (+ test)
- Performance optimisation for async callbacks (polling)
## 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)
- Fix numeric types conversion for 32bit Lua
- Update tokio to 1.0 for async examples
## v0.5.0
- Serde support under `serialize` feature flag.
- Re-export `mlua_derive`.
- impl `ToLua` and `FromLua` for `HashSet` and `BTreeSet`
## v0.4.2
+8 -7
View File
@@ -1,12 +1,12 @@
[package]
name = "mlua"
version = "0.5.0" # remember to update html_root_url and mlua_derive
version = "0.5.3" # 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"
@@ -54,22 +54,23 @@ erased-serde = { version = "0.3", optional = true }
cc = { version = "1.0" }
pkg-config = { version = "0.3.17" }
lua-src = { version = ">= 540.0.0, < 550.0.0", optional = true }
luajit-src = { version = ">= 210.1.0, < 220.0.0", optional = true }
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 = "0.13"
reqwest = { version = "0.10", features = ["json"] }
tokio = { version = "0.2", features = ["full"] }
hyper = { version = "0.14", features = ["client", "server"] }
reqwest = { version = "0.11", features = ["json"] }
tokio = { version = "1.0", features = ["full"] }
futures-timer = "3.0"
serde_json = "1.0"
[[bench]]
name = "benchmark"
harness = false
required-features = ["async"]
[[example]]
name = "async_http_client"
+1 -1
View File
@@ -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
+48 -15
View File
@@ -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` :
@@ -141,11 +168,17 @@ rustflags = [
"-C", "link-arg=-undefined",
"-C", "link-arg=dynamic_lookup",
]
[target.aarch64-apple-darwin]
rustflags = [
"-C", "link-arg=-undefined",
"-C", "link-arg=dynamic_lookup",
]
```
On Linux you can build modules normally with `cargo build --release`.
Vendored and non-vendored builds are supported for these OS.
On Windows `vendored` mode is not supported since you need to link to a Lua dll.
On Windows `vendored` mode for modules is not supported since you need to link to a Lua dll.
Easiest way is to use either MinGW64 (as part of [MSYS2](https://github.com/msys2/msys2) package) with `pkg-config` or
MSVC with `LUA_INC` / `LUA_LIB` / `LUA_LIB_NAME` environment variables.
+171 -99
View File
@@ -11,13 +11,24 @@
extern "system" {}
use criterion::{criterion_group, criterion_main, BatchSize, Criterion};
use std::time::Duration;
use tokio::runtime::Runtime;
use tokio::task;
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 +37,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 +54,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 +71,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 +92,75 @@ 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 {
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) {
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 +168,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 +188,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 +204,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);
+106 -2
View File
@@ -1,7 +1,8 @@
#![allow(unreachable_code)]
use std::env;
use std::io::{Error, ErrorKind, Result};
use std::fs::File;
use std::io::{Error, ErrorKind, Result, Write};
use std::path::{Path, PathBuf};
use std::process::Command;
@@ -91,6 +92,105 @@ fn build_glue<P: AsRef<Path> + std::fmt::Debug>(include_path: &P) {
.unwrap();
}
// When cross-compiling, we cannot use `build_glue` as we cannot run the generated
// executable. Instead, let's take a stab at synthesizing the likely values.
// If you're cross-compiling and using a non-vendored library then there is a chance
// that the values selected here may be incorrect, but we have no way to determine
// that here.
fn generate_glue() -> Result<()> {
let build_dir = PathBuf::from(env::var_os("OUT_DIR").unwrap());
let mut glue = File::create(build_dir.join("glue.rs"))?;
writeln!(
glue,
"/* This file was generated by build/main.rs; do not modify by hand */"
)?;
writeln!(glue, "use std::os::raw::*;")?;
writeln!(glue, "/* luaconf.h */")?;
let pointer_bit_width: usize = env::var("CARGO_CFG_TARGET_POINTER_WIDTH")
.unwrap()
.parse()
.unwrap();
writeln!(
glue,
"pub const LUA_EXTRASPACE: c_int = {} / 8;",
pointer_bit_width
)?;
// This is generally hardcoded to this size
writeln!(glue, "pub const LUA_IDSIZE: c_int = 60;")?;
// Unless the target is restricted, the defaults are 64 bit
writeln!(glue, "pub type LUA_NUMBER = c_double;")?;
writeln!(glue, "pub type LUA_INTEGER = i64;")?;
writeln!(glue, "pub type LUA_UNSIGNED = u64;")?;
writeln!(glue, "/* lua.h */")?;
let version = if cfg!(any(feature = "luajit", feature = "lua51")) {
(5, 1, 0)
} else if cfg!(feature = "lua52") {
(5, 2, 0)
} else if cfg!(feature = "lua53") {
(5, 3, 0)
} else if cfg!(feature = "lua54") {
(5, 4, 0)
} else {
unreachable!();
};
writeln!(
glue,
"pub const LUA_VERSION_NUM: c_int = {};",
(version.0 * 100) + version.1
)?;
let max_stack = if pointer_bit_width >= 32 {
1_000_000
} else {
15_000
};
writeln!(
glue,
"pub const LUA_REGISTRYINDEX: c_int = -{} - 1000;",
max_stack
)?;
// These two are only defined in lua 5.1
writeln!(glue, "pub const LUA_ENVIRONINDEX: c_int = -10001;")?;
writeln!(glue, "pub const LUA_GLOBALSINDEX: c_int = -10002;")?;
writeln!(glue, "/* lauxlib.h */")?;
// This is only defined in lua 5.3 and up, but we can always generate its value here,
// even if we don't use it.
// This matches the default definition in lauxlib.h
writeln!(glue, "pub const LUAL_NUMSIZES: c_int = std::mem::size_of::<LUA_INTEGER>() as c_int * 16 + std::mem::size_of::<LUA_NUMBER>() as c_int;")?;
writeln!(glue, "/* lualib.h */")?;
write!(
glue,
r#"
#[cfg(feature = "luajit")]
pub const LUA_BITLIBNAME: &str = "bit";
#[cfg(not(feature = "luajit"))]
pub const LUA_BITLIBNAME: &str = "bit32";
pub const LUA_COLIBNAME: &str = "coroutine";
pub const LUA_DBLIBNAME: &str = "debug";
pub const LUA_IOLIBNAME: &str = "io";
pub const LUA_LOADLIBNAME: &str = "package";
pub const LUA_MATHLIBNAME: &str = "math";
pub const LUA_OSLIBNAME: &str = "os";
pub const LUA_STRLIBNAME: &str = "string";
pub const LUA_TABLIBNAME: &str = "table";
pub const LUA_UTF8LIBNAME: &str = "utf8";
pub const LUA_JITLIBNAME: &str = "jit";
pub const LUA_FFILIBNAME: &str = "ffi";
"#
)?;
Ok(())
}
fn main() {
#[cfg(not(any(
feature = "lua54",
@@ -132,5 +232,9 @@ fn main() {
);
let include_dir = find::probe_lua();
build_glue(&include_dir);
if env::var("TARGET").unwrap() != env::var("HOST").unwrap() {
generate_glue().unwrap();
} else {
build_glue(&include_dir);
}
}
+5 -4
View File
@@ -2,8 +2,9 @@ use std::collections::HashMap;
use std::sync::Arc;
use bstr::BString;
use hyper::{body::Body as HyperBody, Client as HyperClient};
use tokio::{stream::StreamExt, sync::Mutex};
use hyper::body::{Body as HyperBody, HttpBody as _};
use hyper::Client as HyperClient;
use tokio::sync::Mutex;
use mlua::{Error, Lua, Result, UserData, UserDataMethods};
@@ -20,8 +21,8 @@ impl UserData for BodyReader {
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
methods.add_async_method("read", |_, reader, ()| async move {
let mut reader = reader.0.lock().await;
let bytes = reader.try_next().await.map_err(Error::external)?;
if let Some(bytes) = bytes {
if let Some(bytes) = reader.data().await {
let bytes = bytes.map_err(Error::external)?;
return Ok(Some(BString::from(bytes.as_ref())));
}
Ok(None)
+1 -2
View File
@@ -1,4 +1,3 @@
use std::net::Shutdown;
use std::sync::Arc;
use bstr::BString;
@@ -55,7 +54,7 @@ impl UserData for LuaTcpStream {
});
methods.add_async_method("close", |_, stream, ()| async move {
stream.0.lock().await.shutdown(Shutdown::Both)?;
stream.0.lock().await.shutdown().await?;
Ok(())
});
}
+21 -2
View File
@@ -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};
@@ -12,7 +13,7 @@ use crate::lua::Lua;
use crate::string::String;
use crate::table::Table;
use crate::thread::Thread;
use crate::types::{LightUserData, MaybeSend, Number};
use crate::types::{LightUserData, MaybeSend};
use crate::userdata::{AnyUserData, UserData};
use crate::value::{FromLua, Nil, ToLua, Value};
@@ -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)?))
@@ -345,7 +358,13 @@ macro_rules! lua_convert_float {
($x:ty) => {
impl<'lua> ToLua<'lua> for $x {
fn to_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
Ok(Value::Number(self as Number))
cast(self)
.ok_or_else(|| Error::ToLuaConversionError {
from: stringify!($x),
to: "number",
message: Some("out of range".to_string()),
})
.map(Value::Number)
}
}
+1 -1
View File
@@ -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 -7
View File
@@ -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
@@ -225,12 +225,8 @@ int main(int argc, const char **argv) {
// == luaconf.h ==========================================================
RS_COMMENT("luaconf.h"),
RS_STR("LUA_PATH_DEFAULT", LUA_PATH_DEFAULT),
RS_STR("LUA_CPATH_DEFAULT", LUA_CPATH_DEFAULT),
RS_STR("LUA_DIRSEP", LUA_DIRSEP),
RS_INT("LUA_EXTRASPACE", LUA_EXTRASPACE),
RS_INT("LUA_IDSIZE", LUA_IDSIZE),
RS_INT("LUAL_BUFFERSIZE", LUAL_BUFFERSIZE),
RS_TYPE("LUA_NUMBER",
sizeof(LUA_NUMBER) > sizeof(float) ? "c_double" : "c_float"),
RS_TYPE("LUA_INTEGER", rs_int_type(sizeof(LUA_INTEGER))),
@@ -244,8 +240,6 @@ int main(int argc, const char **argv) {
RS_COMMENT("lua.h"),
RS_INT("LUA_VERSION_NUM", LUA_VERSION_NUM),
RS_STR("LUA_VERSION", LUA_VERSION),
RS_STR("LUA_RELEASE", LUA_RELEASE),
RS_INT("LUA_REGISTRYINDEX", LUA_REGISTRYINDEX),
#if LUA_VERSION_NUM == 501
RS_INT("LUA_ENVIRONINDEX", LUA_ENVIRONINDEX),
+1 -1
View File
@@ -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
+2 -4
View File
@@ -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
@@ -32,11 +32,9 @@ use std::ptr;
use super::luaconf;
pub use super::glue::{LUA_RELEASE, LUA_VERSION, LUA_VERSION_NUM};
pub use super::glue::LUA_REGISTRYINDEX;
#[cfg(any(feature = "lua51", feature = "luajit"))]
pub use super::glue::{LUA_ENVIRONINDEX, LUA_GLOBALSINDEX};
pub use super::glue::{LUA_REGISTRYINDEX, LUA_VERSION_NUM};
#[cfg(not(feature = "luajit"))]
pub const LUA_SIGNATURE: &[u8] = b"\x1bLua";
+1 -2
View File
@@ -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
@@ -23,7 +23,6 @@
//! Contains definitions from `luaconf.h`.
pub use super::glue::LUAL_BUFFERSIZE;
pub use super::glue::LUA_INTEGER;
pub use super::glue::LUA_NUMBER;
pub use super::glue::LUA_UNSIGNED;
+1 -1
View File
@@ -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
View File
@@ -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
View File
@@ -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.0")]
#![doc(html_root_url = "https://docs.rs/mlua/0.5.3")]
// Deny warnings inside doc tests / examples. When this isn't present, rustdoc doesn't show *any*
// warnings at all.
#![doc(test(attr(deny(warnings))))]
+57 -54
View File
@@ -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)?;
@@ -1743,17 +1735,19 @@ impl Lua {
match (*fut).as_mut().poll(&mut ctx) {
Poll::Pending => {
check_stack(state, 6)?;
check_stack(state, 1)?;
ffi::lua_pushboolean(state, 0);
push_gc_userdata(state, AsyncPollPending)?;
Ok(2)
Ok(1)
}
Poll::Ready(results) => {
let results = lua.create_sequence_from(results?)?;
check_stack(state, 2)?;
let results = results?;
let nresults = results.len() as Integer;
let results = lua.create_sequence_from(results)?;
check_stack(state, 3)?;
ffi::lua_pushboolean(state, 1);
lua.push_value(Value::Table(results))?;
Ok(2)
lua.push_value(Value::Integer(nresults))?;
Ok(3)
}
}
})
@@ -1780,22 +1774,31 @@ impl Lua {
env.set("yield", coroutine.get::<_, Function>("yield")?)?;
env.set(
"unpack",
self.create_function(|_, tbl: Table| {
self.create_function(|_, (tbl, len): (Table, Integer)| {
Ok(MultiValue::from_vec(
tbl.sequence_values().collect::<Result<Vec<Value>>>()?,
tbl.raw_sequence_values_by_len(Some(len))
.collect::<Result<Vec<Value>>>()?,
))
})?,
)?;
env.set("pending", unsafe {
let _sg = StackGuard::new(self.state);
check_stack(self.state, 5)?;
push_gc_userdata(self.state, AsyncPollPending)?;
self.pop_value()
})?;
// 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, pending, yield, unpack = poll, pending, yield, unpack
while true do
ready, res = poll()
local ready, res, nres = poll()
if ready then
return unpack(res)
return unpack(res, nres)
end
yield(res)
yield(pending)
end
"#,
)
+1 -1
View File
@@ -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
View File
@@ -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)
+4 -2
View File
@@ -20,8 +20,10 @@ impl<'lua, 'de> serde::Deserializer<'de> for Deserializer<'lua> {
match self.0 {
Value::Nil => visitor.visit_unit(),
Value::Boolean(b) => visitor.visit_bool(b),
Value::Integer(i) => visitor.visit_i64(i),
Value::Number(n) => visitor.visit_f64(n),
#[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),
Err(_) => visitor.visit_bytes(s.as_bytes()),
+14 -25
View File
@@ -8,18 +8,18 @@ use crate::ffi;
use crate::lua::Lua;
use crate::string::String;
use crate::table::Table;
use crate::types::{Integer, Number};
use crate::types::Integer;
use crate::util::{assert_stack, protect_lua, StackGuard};
use crate::value::Value;
use crate::value::{ToLua, Value};
/// A struct for serializing Rust values into Lua values.
pub struct Serializer<'lua>(pub &'lua Lua);
macro_rules! lua_serialize_integer {
macro_rules! lua_serialize_number {
($name:ident, $t:ty) => {
#[inline]
fn $name(self, value: $t) -> Result<Value<'lua>> {
Ok(Value::Integer(value as Integer))
value.to_lua(self.0)
}
};
}
@@ -43,28 +43,17 @@ impl<'lua> ser::Serializer for Serializer<'lua> {
Ok(Value::Boolean(value))
}
lua_serialize_integer!(serialize_i8, i8);
lua_serialize_integer!(serialize_u8, u8);
lua_serialize_integer!(serialize_i16, i16);
lua_serialize_integer!(serialize_u16, u16);
lua_serialize_integer!(serialize_i32, i32);
lua_serialize_integer!(serialize_u32, u32);
lua_serialize_integer!(serialize_u64, u64);
lua_serialize_number!(serialize_i8, i8);
lua_serialize_number!(serialize_u8, u8);
lua_serialize_number!(serialize_i16, i16);
lua_serialize_number!(serialize_u16, u16);
lua_serialize_number!(serialize_i32, i32);
lua_serialize_number!(serialize_u32, u32);
lua_serialize_number!(serialize_i64, i64);
lua_serialize_number!(serialize_u64, u64);
#[inline]
fn serialize_i64(self, value: i64) -> Result<Value<'lua>> {
Ok(Value::Integer(value))
}
#[inline]
fn serialize_f32(self, value: f32) -> Result<Value<'lua>> {
Ok(Value::Number(value as Number))
}
#[inline]
fn serialize_f64(self, value: f64) -> Result<Value<'lua>> {
Ok(Value::Number(value))
}
lua_serialize_number!(serialize_f32, f32);
lua_serialize_number!(serialize_f64, f64);
#[inline]
fn serialize_char(self, value: char) -> Result<Value<'lua>> {
+6 -4
View File
@@ -474,9 +474,11 @@ impl<'lua> Table<'lua> {
}
}
#[cfg(feature = "serialize")]
pub(crate) fn raw_sequence_values_by_len<V: FromLua<'lua>>(self) -> TableSequence<'lua, V> {
let len = self.raw_len();
pub(crate) fn raw_sequence_values_by_len<V: FromLua<'lua>>(
self,
len: Option<Integer>,
) -> TableSequence<'lua, V> {
let len = len.unwrap_or_else(|| self.raw_len());
TableSequence {
table: self.0,
index: Some(1),
@@ -641,7 +643,7 @@ impl<'lua> Serialize for Table<'lua> {
let len = self.raw_len() as usize;
if len > 0 || self.is_array() {
let mut seq = serializer.serialize_seq(Some(len))?;
for v in self.clone().raw_sequence_values_by_len::<Value>() {
for v in self.clone().raw_sequence_values_by_len::<Value>(None) {
let v = v.map_err(serde::ser::Error::custom)?;
seq.serialize_element(&v)?;
}
+1 -5
View File
@@ -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 {
+4 -2
View File
@@ -125,8 +125,10 @@ impl<'lua> Serialize for Value<'lua> {
match self {
Value::Nil => serializer.serialize_unit(),
Value::Boolean(b) => serializer.serialize_bool(*b),
Value::Integer(i) => serializer.serialize_i64(*i),
Value::Number(n) => serializer.serialize_f64(*n),
#[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),
Value::UserData(ud) => ud.serialize(serializer),
+36 -1
View File
@@ -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<()> {
@@ -134,6 +136,24 @@ async fn test_async_handle_yield() -> Result<()> {
Ok(())
}
#[tokio::test]
async fn test_async_multi_return_nil() -> Result<()> {
let lua = Lua::new();
lua.globals().set(
"func",
lua.create_async_function(|_, _: ()| async { Ok((Option::<String>::None, "error")) })?,
)?;
lua.load(
r#"
local ok, err = func()
assert(err == "error")
"#,
)
.exec_async()
.await
}
#[tokio::test]
async fn test_async_return_async_closure() -> Result<()> {
let lua = Lua::new();
@@ -332,11 +352,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 +378,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(())
}
+5 -5
View File
@@ -19,7 +19,7 @@ fn test_serialize() -> Result<(), Box<dyn std::error::Error>> {
#[derive(Serialize)]
struct MyUserData(i64, String);
impl UserData for MyUserData {};
impl UserData for MyUserData {}
let lua = Lua::new();
let globals = lua.globals();
@@ -81,7 +81,7 @@ fn test_serialize_in_scope() -> LuaResult<()> {
#[derive(Serialize, Clone)]
struct MyUserData(i64, String);
impl UserData for MyUserData {};
impl UserData for MyUserData {}
let lua = Lua::new();
lua.scope(|scope| {
@@ -112,7 +112,7 @@ fn test_serialize_failure() -> Result<(), Box<dyn std::error::Error>> {
#[derive(Serialize)]
struct MyUserData(i64);
impl UserData for MyUserData {};
impl UserData for MyUserData {}
let lua = Lua::new();
@@ -148,7 +148,7 @@ fn test_to_value_struct() -> LuaResult<()> {
name: String,
key: i64,
data: Option<bool>,
};
}
let test = Test {
name: "alex".to_string(),
@@ -178,7 +178,7 @@ fn test_to_value_enum() -> LuaResult<()> {
name: String,
key: i64,
data: Option<bool>,
};
}
let test = Test {
name: "alex".to_string(),
+2 -2
View File
@@ -25,8 +25,8 @@ fn test_user_data() -> Result<()> {
struct UserData1(i64);
struct UserData2(Box<i64>);
impl UserData for UserData1 {};
impl UserData for UserData2 {};
impl UserData for UserData1 {}
impl UserData for UserData2 {}
let lua = Lua::new();
let userdata1 = lua.create_userdata(UserData1(1))?;