mirror of
https://github.com/mlua-rs/mlua
synced 2026-06-08 16:05:43 +00:00
Compare commits
81 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c85616137a | |||
| f52d106a82 | |||
| 10826a7e67 | |||
| 18c3255c90 | |||
| 6190427f37 | |||
| 9a5a341e44 | |||
| dd91ebfbe5 | |||
| f9fe869b76 | |||
| 6e4033abba | |||
| c213a85ef0 | |||
| 4963cc1b8b | |||
| 418e8fba32 | |||
| 08a7e30820 | |||
| 19676bda40 | |||
| 5a06778fbc | |||
| e33bdddc7a | |||
| cfb5d3fd45 | |||
| 84a174c94d | |||
| 888b2bbf8d | |||
| 386eb7a2b7 | |||
| 66c9c633b2 | |||
| f1e357ce52 | |||
| be09a1fabf | |||
| 30af045c6f | |||
| e809640b7e | |||
| cb3ef20ffc | |||
| 3b6564c02a | |||
| 89580dd863 | |||
| a0554a56d4 | |||
| 2fee3e7891 | |||
| 9f073ad879 | |||
| 1b74c89509 | |||
| 440862de38 | |||
| 066d28f5e5 | |||
| 4528f93345 | |||
| d49757c7db | |||
| 170818c469 | |||
| 55c8af1e6b | |||
| e2ebe65306 | |||
| f0f5a8a0af | |||
| 0e1be19cbb | |||
| 0ef709672d | |||
| 41503b4fb8 | |||
| 19bd254e1e | |||
| 50f20e0c2c | |||
| 7efe807199 | |||
| 63ea8c7662 | |||
| 863d36d5a1 | |||
| a8017c10b9 | |||
| 2c7d7117d2 | |||
| 6d689c35aa | |||
| 8af1304fd0 | |||
| 153502ec73 | |||
| 2ea2b1f4fb | |||
| ef8c1556e6 | |||
| b0da2fc439 | |||
| 204eedde3c | |||
| fbc2973aff | |||
| d0641d812f | |||
| ad70ba54a5 | |||
| 806f0bcef4 | |||
| 0741db7565 | |||
| d88a4282c7 | |||
| d7d987fa14 | |||
| 4d3ac6d8c5 | |||
| a9ca99349c | |||
| f71db80a74 | |||
| 09af3e021a | |||
| 9e3d495f91 | |||
| cab2e5a48e | |||
| 25a4879cde | |||
| ed48b11e7f | |||
| 559f9e6c6b | |||
| 458b06796c | |||
| 259eb09ae1 | |||
| a544e41b33 | |||
| 235fba821e | |||
| c8c64a1b5a | |||
| eff0bbb052 | |||
| d098c9ccf6 | |||
| c62b17a5c8 |
@@ -14,7 +14,7 @@ jobs:
|
||||
|
||||
- name: Generate code coverage
|
||||
run: |
|
||||
cargo tarpaulin --verbose --features lua53,vendored,async,send,serialize,macros --out xml --exclude-files benches --exclude-files build --exclude-files mlua_derive --exclude-files src/ffi --exclude-files tests
|
||||
cargo tarpaulin --verbose --features lua54,vendored,async,send,serialize,macros --out xml --exclude-files benches --exclude-files build --exclude-files mlua_derive --exclude-files src/ffi --exclude-files tests
|
||||
|
||||
- name: Upload to codecov.io
|
||||
uses: codecov/codecov-action@v1
|
||||
|
||||
+16
-16
@@ -7,11 +7,11 @@ jobs:
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-18.04, macos-latest, windows-latest]
|
||||
os: [ubuntu-20.04, macos-latest, windows-latest]
|
||||
rust: [stable]
|
||||
lua: [lua54, lua53, lua52, lua51, luajit]
|
||||
include:
|
||||
- os: ubuntu-18.04
|
||||
- os: ubuntu-20.04
|
||||
target: x86_64-unknown-linux-gnu
|
||||
- os: macos-latest
|
||||
target: x86_64-apple-darwin
|
||||
@@ -31,7 +31,7 @@ jobs:
|
||||
cargo build --features "${{ matrix.lua }},vendored,async,send,serialize,macros"
|
||||
shell: bash
|
||||
- name: Build ${{ matrix.lua }} pkg-config
|
||||
if: ${{ matrix.os == 'ubuntu-18.04' && matrix.lua != 'lua54' }}
|
||||
if: ${{ matrix.os == 'ubuntu-20.04' && matrix.lua != 'lua54' }}
|
||||
run: |
|
||||
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
|
||||
@@ -56,7 +56,7 @@ jobs:
|
||||
|
||||
build_aarch64_cross_ubuntu:
|
||||
name: Cross-compile to aarch64-unknown-linux-gnu
|
||||
runs-on: ubuntu-18.04
|
||||
runs-on: ubuntu-20.04
|
||||
needs: build
|
||||
strategy:
|
||||
matrix:
|
||||
@@ -79,7 +79,7 @@ jobs:
|
||||
|
||||
build_armv7_cross_ubuntu:
|
||||
name: Cross-compile to armv7-unknown-linux-gnueabihf
|
||||
runs-on: ubuntu-18.04
|
||||
runs-on: ubuntu-20.04
|
||||
needs: build
|
||||
strategy:
|
||||
matrix:
|
||||
@@ -106,11 +106,11 @@ jobs:
|
||||
needs: build
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-18.04, macos-latest, windows-latest]
|
||||
os: [ubuntu-20.04, macos-latest, windows-latest]
|
||||
rust: [stable, nightly]
|
||||
lua: [lua54, lua53, lua52, lua51, luajit]
|
||||
lua: [lua54, lua53, lua52, lua51, luajit, luajit52]
|
||||
include:
|
||||
- os: ubuntu-18.04
|
||||
- os: ubuntu-20.04
|
||||
target: x86_64-unknown-linux-gnu
|
||||
- os: macos-latest
|
||||
target: x86_64-apple-darwin
|
||||
@@ -129,8 +129,8 @@ jobs:
|
||||
cargo test --features "${{ matrix.lua }},vendored"
|
||||
cargo test --features "${{ matrix.lua }},vendored,async,send,serialize,macros"
|
||||
shell: bash
|
||||
- name: Run compile tests (macos lua53)
|
||||
if: ${{ matrix.os == 'macos-latest' && matrix.lua == 'lua53' }}
|
||||
- name: Run compile tests (macos lua54)
|
||||
if: ${{ matrix.os == 'macos-latest' && matrix.lua == 'lua54' }}
|
||||
run: |
|
||||
TRYBUILD=overwrite cargo test --features "${{ matrix.lua }},vendored" -- --ignored
|
||||
TRYBUILD=overwrite cargo test --features "${{ matrix.lua }},vendored,async,send,serialize,macros" -- --ignored
|
||||
@@ -142,11 +142,11 @@ jobs:
|
||||
needs: build
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-18.04]
|
||||
os: [ubuntu-20.04]
|
||||
rust: [nightly]
|
||||
lua: [lua54, lua53, lua52, lua51, luajit]
|
||||
include:
|
||||
- os: ubuntu-18.04
|
||||
- os: ubuntu-20.04
|
||||
target: x86_64-unknown-linux-gnu
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
@@ -168,11 +168,11 @@ jobs:
|
||||
needs: build
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-18.04, macos-latest]
|
||||
os: [ubuntu-20.04, macos-latest]
|
||||
rust: [stable]
|
||||
lua: [lua54, lua53, lua52, lua51, luajit]
|
||||
include:
|
||||
- os: ubuntu-18.04
|
||||
- os: ubuntu-20.04
|
||||
target: x86_64-unknown-linux-gnu
|
||||
- os: macos-latest
|
||||
target: x86_64-apple-darwin
|
||||
@@ -213,7 +213,7 @@ jobs:
|
||||
|
||||
rustfmt:
|
||||
name: Rustfmt
|
||||
runs-on: ubuntu-18.04
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions-rs/toolchain@v1
|
||||
@@ -225,7 +225,7 @@ jobs:
|
||||
|
||||
clippy:
|
||||
name: Clippy check
|
||||
runs-on: ubuntu-18.04
|
||||
runs-on: ubuntu-20.04
|
||||
strategy:
|
||||
matrix:
|
||||
lua: [lua54, lua53, lua52, lua51, luajit]
|
||||
|
||||
@@ -1,3 +1,55 @@
|
||||
## v0.7.4
|
||||
|
||||
- Improved `Lua::create_registry_value` to reuse previously expired registry keys.
|
||||
No need to call `Lua::expire_registry_values` when creating/dropping registry values.
|
||||
- Added `Lua::replace_registry_value` to change value of an existing Registry Key
|
||||
- Async calls optimization
|
||||
|
||||
## v0.7.3
|
||||
|
||||
- Fixed cross-compilation issue (introduced in 84a174c)
|
||||
|
||||
## v0.7.2
|
||||
|
||||
- Allow `pkg-config` to omit include paths if they equals to standard (#114).
|
||||
- Various bugfixes (eg. #121)
|
||||
|
||||
## v0.7.1
|
||||
|
||||
- Fixed traceback generation for errors (#112)
|
||||
- `Lua::into_static/from_static` methods have been removed from the docs and are discouraged for use
|
||||
|
||||
## v0.7.0
|
||||
|
||||
- New "application data" api to store arbitrary objects inside Lua
|
||||
- New feature flag `luajit52` to build/support LuaJIT with partial compatibility with Lua 5.2
|
||||
- Added async meta methods for all Lua (except 5.1)
|
||||
- Added `AnyUserData::take()` to take UserData objects from Lua
|
||||
- Added `set_nth_user_value`/`get_nth_user_value` to `AnyUserData` for all Lua versions
|
||||
- Added `set_named_user_value`/`get_named_user_value` to `AnyUserData` for all Lua versions
|
||||
- Added `Lua::inspect_stack()` to get information about the interpreter runtime stack
|
||||
- Added `set_warning_function`/`remove_warning_function`/`warning` functions to `Lua` for 5.4
|
||||
- Added `TableExt::call()` to call tables with `__call` metamethod as functions
|
||||
- Added `Lua::unload()` to unload modules
|
||||
- `ToLua` implementation for arrays changed to const generics
|
||||
- Added thread (coroutine) cache for async execution (disabled by default and works for Lua 5.4/JIT)
|
||||
- LuaOptions and (De)SerializeOptions marked as const
|
||||
- Fixed recursive tables serialization when using `serde::Serialize` for Lua Tables
|
||||
- Improved errors reporting. Now source included to `fmt::Display` implementation for `Error::CallbackError`
|
||||
- Major performance improvements
|
||||
|
||||
## v0.6.6
|
||||
|
||||
- Fixed calculating `LUA_REGISTRYINDEX` when cross-compiling for lua51/jit (#82)
|
||||
- Updated documentation & examples
|
||||
|
||||
## v0.6.5
|
||||
|
||||
- Fixed bug when polling async futures (#77)
|
||||
- Refactor Waker handling in async code (+10% performance gain when calling async functions)
|
||||
- Added `Location::caller()` information to `Lua::load()` if chunk's name is None (Rust 1.46+)
|
||||
- Added serialization of i128/u128 types (serde)
|
||||
|
||||
## v0.6.4
|
||||
|
||||
- Performance optimizations
|
||||
|
||||
+8
-6
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "mlua"
|
||||
version = "0.6.4" # remember to update html_root_url and mlua_derive
|
||||
version = "0.7.4" # 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"
|
||||
@@ -13,11 +13,11 @@ links = "lua"
|
||||
build = "build/main.rs"
|
||||
description = """
|
||||
High level bindings to Lua 5.4/5.3/5.2/5.1 (including LuaJIT)
|
||||
with async/await features and support of writing native lua modules in Rust.
|
||||
with async/await features and support of writing native Lua modules in Rust.
|
||||
"""
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
features = ["lua53", "async", "send", "serialize", "macros"]
|
||||
features = ["lua54", "vendored", "async", "send", "serialize", "macros"]
|
||||
rustdoc-args = ["--cfg", "docsrs"]
|
||||
|
||||
[workspace]
|
||||
@@ -33,6 +33,7 @@ lua53 = []
|
||||
lua52 = []
|
||||
lua51 = []
|
||||
luajit = []
|
||||
luajit52 = ["luajit"]
|
||||
vendored = ["lua-src", "luajit-src"]
|
||||
module = ["mlua_derive"]
|
||||
async = ["futures-core", "futures-task", "futures-util"]
|
||||
@@ -45,6 +46,7 @@ mlua_derive = { version = "=0.6.0", optional = true, path = "mlua_derive" }
|
||||
bstr = { version = "0.2", features = ["std"], default_features = false }
|
||||
once_cell = { version = "1.0" }
|
||||
num-traits = { version = "0.2.14" }
|
||||
rustc-hash = "1.0"
|
||||
futures-core = { version = "0.3.5", optional = true }
|
||||
futures-task = { version = "0.3.5", optional = true }
|
||||
futures-util = { version = "0.3.5", optional = true }
|
||||
@@ -55,10 +57,10 @@ 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.2, < 220.0.0", optional = true }
|
||||
luajit-src = { version = ">= 210.3.1, < 220.0.0", optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
rustyline = "8.0"
|
||||
rustyline = "9.0"
|
||||
criterion = { version = "0.3.4", features = ["html_reports", "async_tokio"] }
|
||||
trybuild = "1.0"
|
||||
futures = "0.3.5"
|
||||
@@ -84,7 +86,7 @@ required-features = ["async", "serialize", "macros"]
|
||||
|
||||
[[example]]
|
||||
name = "async_http_server"
|
||||
required-features = ["async", "send"]
|
||||
required-features = ["async", "macros"]
|
||||
|
||||
[[example]]
|
||||
name = "async_tcp_server"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# mlua
|
||||
[![Build Status]][github-actions] [![Latest Version]][crates.io] [![API Documentation]][docs.rs] [![Coverage Status]][codecov.io]
|
||||
[![Build Status]][github-actions] [![Latest Version]][crates.io] [![API Documentation]][docs.rs] [![Coverage Status]][codecov.io] ![MSRV]
|
||||
|
||||
[Build Status]: https://github.com/khvzak/mlua/workflows/CI/badge.svg
|
||||
[github-actions]: https://github.com/khvzak/mlua/actions
|
||||
@@ -9,13 +9,14 @@
|
||||
[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
|
||||
[MSRV]: https://img.shields.io/badge/rust-1.53+-brightgreen.svg?&logo=rust
|
||||
|
||||
[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](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.
|
||||
Started as `rlua` 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` 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).
|
||||
|
||||
@@ -33,6 +34,7 @@ Below is a list of the available feature flags. By default `mlua` does not enabl
|
||||
* `lua52`: activate Lua [5.2] support
|
||||
* `lua51`: activate Lua [5.1] support
|
||||
* `luajit`: activate [LuaJIT] support
|
||||
* `luajit52`: activate [LuaJIT] support with partial compatibility with Lua 5.2
|
||||
* `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])
|
||||
@@ -74,7 +76,7 @@ With `serialize` feature flag enabled, `mlua` allows you to serialize/deserializ
|
||||
|
||||
### Compiling
|
||||
|
||||
You have to enable one of the features `lua54`, `lua53`, `lua52`, `lua51` or `luajit`, according to the chosen Lua version.
|
||||
You have to enable one of the features `lua54`, `lua53`, `lua52`, `lua51` or `luajit(52)`, according to the chosen Lua version.
|
||||
|
||||
By default `mlua` uses `pkg-config` tool to find lua includes and libraries for the chosen Lua version.
|
||||
In most cases it works as desired, although sometimes could be more preferable to use a custom lua library.
|
||||
@@ -97,7 +99,7 @@ Add to `Cargo.toml` :
|
||||
|
||||
``` toml
|
||||
[dependencies]
|
||||
mlua = { version = "0.6", features = ["lua53", "vendored"] }
|
||||
mlua = { version = "0.7", features = ["lua54", "vendored"] }
|
||||
```
|
||||
|
||||
`main.rs`
|
||||
@@ -121,7 +123,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).
|
||||
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.4/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.4/manual.html#6.10).
|
||||
|
||||
[Example](examples/module)
|
||||
|
||||
@@ -132,7 +134,7 @@ Add to `Cargo.toml` :
|
||||
crate-type = ["cdylib"]
|
||||
|
||||
[dependencies]
|
||||
mlua = { version = "0.6", features = ["lua53", "vendored", "module"] }
|
||||
mlua = { version = "0.7", features = ["lua54", "vendored", "module"] }
|
||||
```
|
||||
|
||||
`lib.rs` :
|
||||
@@ -158,7 +160,7 @@ And then (**macOS** example):
|
||||
``` sh
|
||||
$ cargo rustc -- -C link-arg=-undefined -C link-arg=dynamic_lookup
|
||||
$ ln -s ./target/debug/libmy_module.dylib ./my_module.so
|
||||
$ lua5.3 -e 'require("my_module").hello("world")'
|
||||
$ lua5.4 -e 'require("my_module").hello("world")'
|
||||
hello, world!
|
||||
```
|
||||
|
||||
|
||||
@@ -120,7 +120,8 @@ fn call_sum_callback(c: &mut Criterion) {
|
||||
}
|
||||
|
||||
fn call_async_sum_callback(c: &mut Criterion) {
|
||||
let lua = Lua::new();
|
||||
let options = LuaOptions::new().thread_cache_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;
|
||||
@@ -244,7 +245,8 @@ fn call_async_userdata_method(c: &mut Criterion) {
|
||||
}
|
||||
}
|
||||
|
||||
let lua = Lua::new();
|
||||
let options = LuaOptions::new().thread_cache_size(1024);
|
||||
let lua = Lua::new_with(LuaStdLib::ALL_SAFE, options).unwrap();
|
||||
lua.globals().set("userdata", UserData(10)).unwrap();
|
||||
|
||||
c.bench_function("call async [userdata method] 10", |b| {
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
use std::path::PathBuf;
|
||||
|
||||
pub fn probe_lua() -> PathBuf {
|
||||
pub fn probe_lua() -> Option<PathBuf> {
|
||||
unreachable!()
|
||||
}
|
||||
|
||||
+23
-51
@@ -10,7 +10,7 @@ fn get_env_var(name: &str) -> String {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn probe_lua() -> PathBuf {
|
||||
pub fn probe_lua() -> Option<PathBuf> {
|
||||
let include_dir = get_env_var("LUA_INC");
|
||||
let lib_dir = get_env_var("LUA_LIB");
|
||||
let lua_lib = get_env_var("LUA_LIB_NAME");
|
||||
@@ -38,73 +38,42 @@ pub fn probe_lua() -> PathBuf {
|
||||
println!("cargo:rustc-link-search=native={}", lib_dir);
|
||||
println!("cargo:rustc-link-lib={}{}", link_lib, lua_lib);
|
||||
}
|
||||
return PathBuf::from(include_dir);
|
||||
return Some(PathBuf::from(include_dir));
|
||||
}
|
||||
|
||||
// Find using `pkg-config`
|
||||
|
||||
#[cfg(feature = "lua54")]
|
||||
{
|
||||
let mut lua = pkg_config::Config::new()
|
||||
.range_version((Bound::Included("5.4"), Bound::Excluded("5.5")))
|
||||
.cargo_metadata(need_lua_lib)
|
||||
.probe("lua");
|
||||
|
||||
if lua.is_err() {
|
||||
lua = pkg_config::Config::new()
|
||||
.cargo_metadata(need_lua_lib)
|
||||
.probe("lua5.4");
|
||||
}
|
||||
|
||||
lua.unwrap().include_paths[0].clone()
|
||||
}
|
||||
|
||||
let (incl_bound, excl_bound, alt_probe, ver) = ("5.4", "5.5", "lua5.4", "5.4");
|
||||
#[cfg(feature = "lua53")]
|
||||
{
|
||||
let mut lua = pkg_config::Config::new()
|
||||
.range_version((Bound::Included("5.3"), Bound::Excluded("5.4")))
|
||||
.cargo_metadata(need_lua_lib)
|
||||
.probe("lua");
|
||||
|
||||
if lua.is_err() {
|
||||
lua = pkg_config::Config::new()
|
||||
.cargo_metadata(need_lua_lib)
|
||||
.probe("lua5.3");
|
||||
}
|
||||
|
||||
lua.unwrap().include_paths[0].clone()
|
||||
}
|
||||
|
||||
let (incl_bound, excl_bound, alt_probe, ver) = ("5.3", "5.4", "lua5.3", "5.3");
|
||||
#[cfg(feature = "lua52")]
|
||||
{
|
||||
let mut lua = pkg_config::Config::new()
|
||||
.range_version((Bound::Included("5.2"), Bound::Excluded("5.3")))
|
||||
.cargo_metadata(need_lua_lib)
|
||||
.probe("lua");
|
||||
|
||||
if lua.is_err() {
|
||||
lua = pkg_config::Config::new()
|
||||
.cargo_metadata(need_lua_lib)
|
||||
.probe("lua5.2");
|
||||
}
|
||||
|
||||
lua.unwrap().include_paths[0].clone()
|
||||
}
|
||||
|
||||
let (incl_bound, excl_bound, alt_probe, ver) = ("5.2", "5.3", "lua5.2", "5.2");
|
||||
#[cfg(feature = "lua51")]
|
||||
let (incl_bound, excl_bound, alt_probe, ver) = ("5.1", "5.2", "lua5.1", "5.1");
|
||||
|
||||
#[cfg(any(
|
||||
feature = "lua54",
|
||||
feature = "lua53",
|
||||
feature = "lua52",
|
||||
feature = "lua51"
|
||||
))]
|
||||
{
|
||||
let mut lua = pkg_config::Config::new()
|
||||
.range_version((Bound::Included("5.1"), Bound::Excluded("5.2")))
|
||||
.range_version((Bound::Included(incl_bound), Bound::Excluded(excl_bound)))
|
||||
.cargo_metadata(need_lua_lib)
|
||||
.probe("lua");
|
||||
|
||||
if lua.is_err() {
|
||||
lua = pkg_config::Config::new()
|
||||
.cargo_metadata(need_lua_lib)
|
||||
.probe("lua5.1");
|
||||
.probe(alt_probe);
|
||||
}
|
||||
|
||||
lua.unwrap().include_paths[0].clone()
|
||||
lua.expect(&format!("cannot find Lua {} using `pkg-config`", ver))
|
||||
.include_paths
|
||||
.get(0)
|
||||
.cloned()
|
||||
}
|
||||
|
||||
#[cfg(feature = "luajit")]
|
||||
@@ -114,6 +83,9 @@ pub fn probe_lua() -> PathBuf {
|
||||
.cargo_metadata(need_lua_lib)
|
||||
.probe("luajit");
|
||||
|
||||
lua.unwrap().include_paths[0].clone()
|
||||
lua.expect("cannot find LuaJIT using `pkg-config`")
|
||||
.include_paths
|
||||
.get(0)
|
||||
.cloned()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use std::path::PathBuf;
|
||||
|
||||
pub fn probe_lua() -> PathBuf {
|
||||
pub fn probe_lua() -> Option<PathBuf> {
|
||||
#[cfg(feature = "lua54")]
|
||||
let artifacts = lua_src::Build::new().build(lua_src::Lua54);
|
||||
#[cfg(feature = "lua53")]
|
||||
@@ -10,10 +10,16 @@ pub fn probe_lua() -> PathBuf {
|
||||
#[cfg(feature = "lua51")]
|
||||
let artifacts = lua_src::Build::new().build(lua_src::Lua51);
|
||||
#[cfg(feature = "luajit")]
|
||||
let artifacts = luajit_src::Build::new().build();
|
||||
let artifacts = {
|
||||
let mut builder = luajit_src::Build::new();
|
||||
if cfg!(feature = "luajit52") {
|
||||
builder.lua52compat(true);
|
||||
}
|
||||
builder.build()
|
||||
};
|
||||
|
||||
#[cfg(not(feature = "module"))]
|
||||
artifacts.print_cargo_metadata();
|
||||
|
||||
artifacts.include_dir().to_owned()
|
||||
Some(artifacts.include_dir().to_owned())
|
||||
}
|
||||
|
||||
+30
-14
@@ -68,11 +68,14 @@ impl CommandExt for Command {
|
||||
}
|
||||
}
|
||||
|
||||
fn build_glue<P: AsRef<Path> + std::fmt::Debug>(include_path: &P) {
|
||||
// `include_path` is optional as Lua headers can be also found in compiler standard paths
|
||||
fn build_glue(include_path: Option<impl AsRef<Path>>) {
|
||||
let build_dir = PathBuf::from(env::var_os("OUT_DIR").unwrap());
|
||||
|
||||
let mut config = cc::Build::new();
|
||||
config.include(include_path);
|
||||
if let Some(include_path) = include_path {
|
||||
config.include(include_path.as_ref());
|
||||
}
|
||||
|
||||
// Compile and run glue.c
|
||||
let glue = build_dir.join("glue");
|
||||
@@ -143,16 +146,18 @@ fn generate_glue() -> Result<()> {
|
||||
(version.0 * 100) + version.1
|
||||
)?;
|
||||
|
||||
let max_stack = if pointer_bit_width >= 32 {
|
||||
1_000_000
|
||||
} else {
|
||||
15_000
|
||||
};
|
||||
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
|
||||
writeln!(
|
||||
glue,
|
||||
"pub const LUA_REGISTRYINDEX: c_int = -{} - 1000;",
|
||||
max_stack
|
||||
if pointer_bit_width >= 32 {
|
||||
1_000_000
|
||||
} else {
|
||||
15_000
|
||||
}
|
||||
)?;
|
||||
#[cfg(any(feature = "lua51", feature = "luajit"))]
|
||||
writeln!(glue, "pub const LUA_REGISTRYINDEX: c_int = -10000;")?;
|
||||
|
||||
// These two are only defined in lua 5.1
|
||||
writeln!(glue, "pub const LUA_ENVIRONINDEX: c_int = -10001;")?;
|
||||
@@ -199,7 +204,9 @@ fn main() {
|
||||
feature = "lua51",
|
||||
feature = "luajit"
|
||||
)))]
|
||||
compile_error!("You must enable one of the features: lua54, lua53, lua52, lua51, luajit");
|
||||
compile_error!(
|
||||
"You must enable one of the features: lua54, lua53, lua52, lua51, luajit, luajit52"
|
||||
);
|
||||
|
||||
#[cfg(all(
|
||||
feature = "lua54",
|
||||
@@ -210,19 +217,27 @@ fn main() {
|
||||
feature = "luajit"
|
||||
)
|
||||
))]
|
||||
compile_error!("You can enable only one of the features: lua54, lua53, lua52, lua51, luajit");
|
||||
compile_error!(
|
||||
"You can enable only one of the features: lua54, lua53, lua52, lua51, luajit, luajit52"
|
||||
);
|
||||
|
||||
#[cfg(all(
|
||||
feature = "lua53",
|
||||
any(feature = "lua52", feature = "lua51", feature = "luajit")
|
||||
))]
|
||||
compile_error!("You can enable only one of the features: lua54, lua53, lua52, lua51, luajit");
|
||||
compile_error!(
|
||||
"You can enable only one of the features: lua54, lua53, lua52, lua51, luajit, luajit52"
|
||||
);
|
||||
|
||||
#[cfg(all(feature = "lua52", any(feature = "lua51", feature = "luajit")))]
|
||||
compile_error!("You can enable only one of the features: lua54, lua53, lua52, lua51, luajit");
|
||||
compile_error!(
|
||||
"You can enable only one of the features: lua54, lua53, lua52, lua51, luajit, luajit52"
|
||||
);
|
||||
|
||||
#[cfg(all(feature = "lua51", feature = "luajit"))]
|
||||
compile_error!("You can enable only one of the features: lua54, lua53, lua52, lua51, luajit");
|
||||
compile_error!(
|
||||
"You can enable only one of the features: lua54, lua53, lua52, lua51, luajit, luajit52"
|
||||
);
|
||||
|
||||
// We don't support "vendored module" mode on windows
|
||||
#[cfg(all(feature = "vendored", feature = "module", target_os = "windows"))]
|
||||
@@ -233,9 +248,10 @@ fn main() {
|
||||
|
||||
let include_dir = find::probe_lua();
|
||||
if env::var("TARGET").unwrap() != env::var("HOST").unwrap() {
|
||||
// The `probe_lua` call above is still needed here
|
||||
generate_glue().unwrap();
|
||||
} else {
|
||||
build_glue(&include_dir);
|
||||
build_glue(include_dir);
|
||||
println!("cargo:rerun-if-changed=src/ffi/glue/glue.c");
|
||||
}
|
||||
|
||||
|
||||
@@ -1,26 +1,17 @@
|
||||
use std::collections::HashMap;
|
||||
use std::sync::Arc;
|
||||
|
||||
use hyper::body::{Body as HyperBody, HttpBody as _};
|
||||
use hyper::Client as HyperClient;
|
||||
use tokio::sync::Mutex;
|
||||
|
||||
use mlua::{chunk, ExternalResult, Lua, Result, UserData, UserDataMethods};
|
||||
use mlua::{chunk, AnyUserData, ExternalResult, Lua, Result, UserData, UserDataMethods};
|
||||
|
||||
#[derive(Clone)]
|
||||
struct BodyReader(Arc<Mutex<HyperBody>>);
|
||||
|
||||
impl BodyReader {
|
||||
fn new(body: HyperBody) -> Self {
|
||||
BodyReader(Arc::new(Mutex::new(body)))
|
||||
}
|
||||
}
|
||||
struct BodyReader(HyperBody);
|
||||
|
||||
impl UserData for BodyReader {
|
||||
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
|
||||
methods.add_async_method("read", |lua, reader, ()| async move {
|
||||
let mut reader = reader.0.lock().await;
|
||||
if let Some(bytes) = reader.data().await {
|
||||
methods.add_async_function("read", |lua, reader: AnyUserData| async move {
|
||||
let mut reader = reader.borrow_mut::<Self>()?;
|
||||
if let Some(bytes) = reader.0.data().await {
|
||||
let bytes = bytes.to_lua_err()?;
|
||||
return Some(lua.create_string(&bytes)).transpose();
|
||||
}
|
||||
@@ -50,7 +41,7 @@ async fn main() -> Result<()> {
|
||||
}
|
||||
|
||||
lua_resp.set("headers", headers)?;
|
||||
lua_resp.set("body", BodyReader::new(resp.into_body()))?;
|
||||
lua_resp.set("body", BodyReader(resp.into_body()))?;
|
||||
|
||||
Ok(lua_resp)
|
||||
})?;
|
||||
@@ -58,7 +49,7 @@ async fn main() -> Result<()> {
|
||||
let f = lua
|
||||
.load(chunk! {
|
||||
local res = $fetch_url(...)
|
||||
print(res.status)
|
||||
print("status: "..res.status)
|
||||
for key, vals in pairs(res.headers) do
|
||||
for _, val in ipairs(vals) do
|
||||
print(key..": "..val)
|
||||
|
||||
@@ -1,10 +1,16 @@
|
||||
use std::future::Future;
|
||||
use std::net::SocketAddr;
|
||||
use std::pin::Pin;
|
||||
use std::rc::Rc;
|
||||
use std::task::{Context, Poll};
|
||||
|
||||
use hyper::server::conn::AddrStream;
|
||||
use hyper::service::{make_service_fn, service_fn};
|
||||
use hyper::service::Service;
|
||||
use hyper::{Body, Request, Response, Server};
|
||||
|
||||
use mlua::{Error, Function, Lua, Result, Table, UserData, UserDataMethods};
|
||||
use mlua::{
|
||||
chunk, Error as LuaError, Function, Lua, String as LuaString, Table, UserData, UserDataMethods,
|
||||
};
|
||||
|
||||
struct LuaRequest(SocketAddr, Request<Body>);
|
||||
|
||||
@@ -15,75 +21,106 @@ impl UserData for LuaRequest {
|
||||
}
|
||||
}
|
||||
|
||||
async fn run_server(handler: Function<'static>) -> Result<()> {
|
||||
let make_svc = make_service_fn(|socket: &AddrStream| {
|
||||
let remote_addr = socket.remote_addr();
|
||||
let handler = handler.clone();
|
||||
async move {
|
||||
Ok::<_, Error>(service_fn(move |req: Request<Body>| {
|
||||
let handler = handler.clone();
|
||||
async move {
|
||||
let lua_req = LuaRequest(remote_addr, req);
|
||||
let lua_resp: Table = handler.call_async(lua_req).await?;
|
||||
let body = lua_resp
|
||||
.get::<_, Option<String>>("body")?
|
||||
.unwrap_or_default();
|
||||
pub struct Svc(Rc<Lua>, SocketAddr);
|
||||
|
||||
let mut resp = Response::builder()
|
||||
.status(lua_resp.get::<_, Option<u16>>("status")?.unwrap_or(200));
|
||||
impl Service<Request<Body>> for Svc {
|
||||
type Response = Response<Body>;
|
||||
type Error = LuaError;
|
||||
type Future = Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>>>>;
|
||||
|
||||
fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||
Poll::Ready(Ok(()))
|
||||
}
|
||||
|
||||
fn call(&mut self, req: Request<Body>) -> Self::Future {
|
||||
// If handler returns an error then generate 5xx response
|
||||
let lua = self.0.clone();
|
||||
let lua_req = LuaRequest(self.1, req);
|
||||
Box::pin(async move {
|
||||
let handler: Function = lua.named_registry_value("http_handler")?;
|
||||
match handler.call_async::<_, Table>(lua_req).await {
|
||||
Ok(lua_resp) => {
|
||||
let status = lua_resp.get::<_, Option<u16>>("status")?.unwrap_or(200);
|
||||
let mut resp = Response::builder().status(status);
|
||||
|
||||
// Set headers
|
||||
if let Some(headers) = lua_resp.get::<_, Option<Table>>("headers")? {
|
||||
for pair in headers.pairs::<String, String>() {
|
||||
for pair in headers.pairs::<String, LuaString>() {
|
||||
let (h, v) = pair?;
|
||||
resp = resp.header(&h, v);
|
||||
resp = resp.header(&h, v.as_bytes());
|
||||
}
|
||||
}
|
||||
|
||||
Ok::<_, Error>(resp.body(Body::from(body)).unwrap())
|
||||
let body = lua_resp
|
||||
.get::<_, Option<LuaString>>("body")?
|
||||
.map(|b| Body::from(b.as_bytes().to_vec()))
|
||||
.unwrap_or_else(Body::empty);
|
||||
|
||||
Ok(resp.body(body).unwrap())
|
||||
}
|
||||
}))
|
||||
}
|
||||
});
|
||||
Err(err) => {
|
||||
eprintln!("{}", err);
|
||||
Ok(Response::builder()
|
||||
.status(500)
|
||||
.body(Body::from("Internal Server Error"))
|
||||
.unwrap())
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::main(flavor = "current_thread")]
|
||||
async fn main() {
|
||||
let lua = Rc::new(Lua::new());
|
||||
|
||||
// Create Lua handler function
|
||||
let handler: Function = lua
|
||||
.load(chunk! {
|
||||
function(req)
|
||||
return {
|
||||
status = 200,
|
||||
headers = {
|
||||
["X-Req-Method"] = req:method(),
|
||||
["X-Remote-Addr"] = req:remote_addr(),
|
||||
},
|
||||
body = "Hello from Lua!\n"
|
||||
}
|
||||
end
|
||||
})
|
||||
.eval()
|
||||
.expect("cannot create Lua handler");
|
||||
|
||||
// Store it in the Registry
|
||||
lua.set_named_registry_value("http_handler", handler)
|
||||
.expect("cannot store Lua handler");
|
||||
|
||||
let addr = ([127, 0, 0, 1], 3000).into();
|
||||
let server = Server::bind(&addr).executor(LocalExec).serve(make_svc);
|
||||
let server = Server::bind(&addr).executor(LocalExec).serve(MakeSvc(lua));
|
||||
|
||||
println!("Listening on http://{}", addr);
|
||||
|
||||
tokio::task::LocalSet::new()
|
||||
.run_until(server)
|
||||
.await
|
||||
.map_err(Error::external)
|
||||
// Create `LocalSet` to spawn !Send futures
|
||||
let local = tokio::task::LocalSet::new();
|
||||
local.run_until(server).await.expect("cannot run server")
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<()> {
|
||||
let lua = Lua::new().into_static();
|
||||
struct MakeSvc(Rc<Lua>);
|
||||
|
||||
let handler: Function = lua
|
||||
.load(
|
||||
r#"
|
||||
function(req)
|
||||
return {
|
||||
status = 200,
|
||||
headers = {
|
||||
["X-Req-Method"] = req:method(),
|
||||
["X-Remote-Addr"] = req:remote_addr(),
|
||||
},
|
||||
body = "Hello, World!\n"
|
||||
}
|
||||
end
|
||||
"#,
|
||||
)
|
||||
.eval()?;
|
||||
impl Service<&AddrStream> for MakeSvc {
|
||||
type Response = Svc;
|
||||
type Error = hyper::Error;
|
||||
type Future = Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>>>>;
|
||||
|
||||
run_server(handler).await?;
|
||||
fn poll_ready(&mut self, _: &mut Context) -> Poll<Result<(), Self::Error>> {
|
||||
Poll::Ready(Ok(()))
|
||||
}
|
||||
|
||||
// Consume the static reference and drop it.
|
||||
// This is safe as long as we don't hold any other references to Lua
|
||||
// or alive resources.
|
||||
unsafe { Lua::from_static(lua) };
|
||||
Ok(())
|
||||
fn call(&mut self, stream: &AddrStream) -> Self::Future {
|
||||
let lua = self.0.clone();
|
||||
let remote_addr = stream.remote_addr();
|
||||
Box::pin(async move { Ok(Svc(lua, remote_addr)) })
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
|
||||
@@ -1,122 +1,121 @@
|
||||
use std::sync::Arc;
|
||||
use std::io;
|
||||
use std::net::SocketAddr;
|
||||
use std::rc::Rc;
|
||||
|
||||
use tokio::io::{AsyncReadExt, AsyncWriteExt};
|
||||
use tokio::net::{TcpListener, TcpStream};
|
||||
use tokio::sync::Mutex;
|
||||
use tokio::task;
|
||||
|
||||
use mlua::{chunk, Function, Lua, Result, String as LuaString, UserData, UserDataMethods};
|
||||
use mlua::{
|
||||
chunk, AnyUserData, Function, Lua, RegistryKey, String as LuaString, UserData, UserDataMethods,
|
||||
};
|
||||
|
||||
struct LuaTcp;
|
||||
|
||||
#[derive(Clone)]
|
||||
struct LuaTcpListener(Arc<Mutex<TcpListener>>);
|
||||
|
||||
#[derive(Clone)]
|
||||
struct LuaTcpStream(Arc<Mutex<TcpStream>>);
|
||||
|
||||
impl UserData for LuaTcp {
|
||||
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
|
||||
methods.add_async_function("bind", |_, addr: String| async move {
|
||||
let listener = TcpListener::bind(addr).await?;
|
||||
Ok(LuaTcpListener(Arc::new(Mutex::new(listener))))
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
impl UserData for LuaTcpListener {
|
||||
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
|
||||
methods.add_async_method("accept", |_, listener, ()| async move {
|
||||
let (stream, _) = listener.0.lock().await.accept().await?;
|
||||
Ok(LuaTcpStream(Arc::new(Mutex::new(stream))))
|
||||
});
|
||||
}
|
||||
}
|
||||
struct LuaTcpStream(TcpStream);
|
||||
|
||||
impl UserData for LuaTcpStream {
|
||||
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
|
||||
methods.add_async_method("peer_addr", |_, stream, ()| async move {
|
||||
Ok(stream.0.lock().await.peer_addr()?.to_string())
|
||||
methods.add_method("peer_addr", |_, this, ()| {
|
||||
Ok(this.0.peer_addr()?.to_string())
|
||||
});
|
||||
|
||||
methods.add_async_method("read", |lua, stream, size: usize| async move {
|
||||
let mut buf = vec![0; size];
|
||||
let n = stream.0.lock().await.read(&mut buf).await?;
|
||||
buf.truncate(n);
|
||||
lua.create_string(&buf)
|
||||
});
|
||||
methods.add_async_function(
|
||||
"read",
|
||||
|lua, (this, size): (AnyUserData, usize)| async move {
|
||||
let mut this = this.borrow_mut::<Self>()?;
|
||||
let mut buf = vec![0; size];
|
||||
let n = this.0.read(&mut buf).await?;
|
||||
buf.truncate(n);
|
||||
lua.create_string(&buf)
|
||||
},
|
||||
);
|
||||
|
||||
methods.add_async_method("write", |_, stream, data: LuaString| async move {
|
||||
let n = stream.0.lock().await.write(&data.as_bytes()).await?;
|
||||
Ok(n)
|
||||
});
|
||||
methods.add_async_function(
|
||||
"write",
|
||||
|_, (this, data): (AnyUserData, LuaString)| async move {
|
||||
let mut this = this.borrow_mut::<Self>()?;
|
||||
let n = this.0.write(&data.as_bytes()).await?;
|
||||
Ok(n)
|
||||
},
|
||||
);
|
||||
|
||||
methods.add_async_method("close", |_, stream, ()| async move {
|
||||
stream.0.lock().await.shutdown().await?;
|
||||
methods.add_async_function("close", |_, this: AnyUserData| async move {
|
||||
let mut this = this.borrow_mut::<Self>()?;
|
||||
this.0.shutdown().await?;
|
||||
Ok(())
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async fn run_server(lua: &'static Lua) -> Result<()> {
|
||||
let spawn = lua.create_function(move |_, func: Function| {
|
||||
task::spawn_local(async move { func.call_async::<_, ()>(()).await });
|
||||
Ok(())
|
||||
})?;
|
||||
async fn run_server(lua: Lua, handler: RegistryKey) -> io::Result<()> {
|
||||
let addr: SocketAddr = ([127, 0, 0, 1], 3000).into();
|
||||
let listener = TcpListener::bind(addr).await.expect("cannot bind addr");
|
||||
|
||||
let tcp = LuaTcp;
|
||||
println!("Listening on {}", addr);
|
||||
|
||||
let server = lua
|
||||
let lua = Rc::new(lua);
|
||||
let handler = Rc::new(handler);
|
||||
loop {
|
||||
let (stream, _) = match listener.accept().await {
|
||||
Ok(res) => res,
|
||||
Err(err) if is_transient_error(&err) => continue,
|
||||
Err(err) => return Err(err),
|
||||
};
|
||||
|
||||
let lua = lua.clone();
|
||||
let handler = handler.clone();
|
||||
task::spawn_local(async move {
|
||||
let handler: Function = lua
|
||||
.registry_value(&handler)
|
||||
.expect("cannot get Lua handler");
|
||||
|
||||
let stream = LuaTcpStream(stream);
|
||||
if let Err(err) = handler.call_async::<_, ()>(stream).await {
|
||||
eprintln!("{}", err);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::main(flavor = "current_thread")]
|
||||
async fn main() {
|
||||
let lua = Lua::new();
|
||||
|
||||
// Create Lua handler function
|
||||
let handler_fn = lua
|
||||
.load(chunk! {
|
||||
local addr = ...
|
||||
local listener = $tcp.bind(addr)
|
||||
print("listening on "..addr)
|
||||
|
||||
local accept_new = true
|
||||
while true do
|
||||
local stream = listener:accept()
|
||||
function(stream)
|
||||
local peer_addr = stream:peer_addr()
|
||||
print("connected from "..peer_addr)
|
||||
|
||||
if not accept_new then
|
||||
return
|
||||
end
|
||||
|
||||
$spawn(function()
|
||||
while true do
|
||||
local data = stream:read(100)
|
||||
data = data:match("^%s*(.-)%s*$") -- trim
|
||||
print("["..peer_addr.."] "..data)
|
||||
if data == "bye" then
|
||||
stream:write("bye bye\n")
|
||||
stream:close()
|
||||
return
|
||||
end
|
||||
if data == "exit" then
|
||||
stream:close()
|
||||
accept_new = false
|
||||
return
|
||||
end
|
||||
stream:write("echo: "..data.."\n")
|
||||
while true do
|
||||
local data = stream:read(100)
|
||||
data = data:match("^%s*(.-)%s*$") // trim
|
||||
print("["..peer_addr.."] "..data)
|
||||
if data == "bye" then
|
||||
stream:write("bye bye\n")
|
||||
stream:close()
|
||||
return
|
||||
end
|
||||
end)
|
||||
stream:write("echo: "..data.."\n")
|
||||
end
|
||||
end
|
||||
})
|
||||
.into_function()?;
|
||||
.eval::<Function>()
|
||||
.expect("cannot create Lua handler");
|
||||
|
||||
// Store it in the Registry
|
||||
let handler = lua
|
||||
.create_registry_value(handler_fn)
|
||||
.expect("cannot store Lua handler");
|
||||
|
||||
task::LocalSet::new()
|
||||
.run_until(server.call_async::<_, ()>("0.0.0.0:1234"))
|
||||
.run_until(run_server(lua, handler))
|
||||
.await
|
||||
.expect("cannot run server")
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
let lua = Lua::new().into_static();
|
||||
|
||||
run_server(lua).await.unwrap();
|
||||
|
||||
// Consume the static reference and drop it.
|
||||
// This is safe as long as we don't hold any other references to Lua
|
||||
// or alive resources.
|
||||
unsafe { Lua::from_static(lua) };
|
||||
fn is_transient_error(e: &io::Error) -> bool {
|
||||
e.kind() == io::ErrorKind::ConnectionRefused
|
||||
|| e.kind() == io::ErrorKind::ConnectionAborted
|
||||
|| e.kind() == io::ErrorKind::ConnectionReset
|
||||
}
|
||||
|
||||
@@ -3,3 +3,9 @@ rustflags = [
|
||||
"-C", "link-arg=-undefined",
|
||||
"-C", "link-arg=dynamic_lookup",
|
||||
]
|
||||
|
||||
[target.aarch64-apple-darwin]
|
||||
rustflags = [
|
||||
"-C", "link-arg=-undefined",
|
||||
"-C", "link-arg=dynamic_lookup",
|
||||
]
|
||||
|
||||
@@ -175,8 +175,7 @@ impl Tokens {
|
||||
pub(crate) fn retokenize(tt: TokenStream) -> Tokens {
|
||||
Tokens(
|
||||
tt.into_iter()
|
||||
.map(Tokens::from)
|
||||
.flatten()
|
||||
.flat_map(Tokens::from)
|
||||
.peekable()
|
||||
.batching(|iter| {
|
||||
// Find variable tokens
|
||||
@@ -217,7 +216,7 @@ impl From<TokenTree> for Tokens {
|
||||
|
||||
vec![Token::new_delim(b, tt.clone(), true)]
|
||||
.into_iter()
|
||||
.chain(g.stream().into_iter().map(Tokens::from).flatten())
|
||||
.chain(g.stream().into_iter().flat_map(Tokens::from))
|
||||
.chain(vec![Token::new_delim(e, tt, false)])
|
||||
.collect()
|
||||
}
|
||||
|
||||
+40
-40
@@ -2,6 +2,7 @@
|
||||
|
||||
use std::borrow::Cow;
|
||||
use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet};
|
||||
use std::convert::TryInto;
|
||||
use std::ffi::{CStr, CString};
|
||||
use std::hash::{BuildHasher, Hash};
|
||||
use std::string::String as StdString;
|
||||
@@ -348,24 +349,24 @@ macro_rules! lua_convert_int {
|
||||
($x:ty) => {
|
||||
impl<'lua> ToLua<'lua> for $x {
|
||||
fn to_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
|
||||
if let Some(i) = cast(self) {
|
||||
Ok(Value::Integer(i))
|
||||
} else {
|
||||
cast(self)
|
||||
.ok_or_else(|| Error::ToLuaConversionError {
|
||||
from: stringify!($x),
|
||||
to: "number",
|
||||
message: Some("out of range".to_owned()),
|
||||
})
|
||||
.map(Value::Number)
|
||||
}
|
||||
cast(self)
|
||||
.map(Value::Integer)
|
||||
.or_else(|| cast(self).map(Value::Number))
|
||||
// This is impossible error because conversion to Number never fails
|
||||
.ok_or_else(|| Error::ToLuaConversionError {
|
||||
from: stringify!($x),
|
||||
to: "number",
|
||||
message: Some("out of range".to_owned()),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> FromLua<'lua> for $x {
|
||||
fn from_lua(value: Value<'lua>, lua: &'lua Lua) -> Result<Self> {
|
||||
let ty = value.type_name();
|
||||
(if let Some(i) = lua.coerce_integer(value.clone())? {
|
||||
(if let Value::Integer(i) = value {
|
||||
cast(i)
|
||||
} else if let Some(i) = lua.coerce_integer(value.clone())? {
|
||||
cast(i)
|
||||
} else {
|
||||
cast(lua.coerce_number(value)?.ok_or_else(|| {
|
||||
@@ -450,37 +451,36 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! lua_convert_array {
|
||||
($($N:literal)+) => {
|
||||
$(
|
||||
impl<'lua, T> ToLua<'lua> for [T; $N]
|
||||
where
|
||||
T: Clone + ToLua<'lua>,
|
||||
{
|
||||
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
|
||||
(&self).to_lua(lua)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua, T> ToLua<'lua> for &[T; $N]
|
||||
where
|
||||
T: Clone + ToLua<'lua>,
|
||||
{
|
||||
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
|
||||
Ok(Value::Table(
|
||||
lua.create_sequence_from(self.iter().cloned())?,
|
||||
))
|
||||
}
|
||||
}
|
||||
)+
|
||||
impl<'lua, T, const N: usize> ToLua<'lua> for [T; N]
|
||||
where
|
||||
T: ToLua<'lua>,
|
||||
{
|
||||
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
|
||||
Ok(Value::Table(lua.create_sequence_from(self)?))
|
||||
}
|
||||
}
|
||||
|
||||
lua_convert_array! {
|
||||
0 1 2 3 4 5 6 7 8 9
|
||||
10 11 12 13 14 15 16 17 18 19
|
||||
20 21 22 23 24 25 26 27 28 29
|
||||
30 31 32
|
||||
impl<'lua, T, const N: usize> FromLua<'lua> for [T; N]
|
||||
where
|
||||
T: FromLua<'lua>,
|
||||
{
|
||||
fn from_lua(value: Value<'lua>, _: &'lua Lua) -> Result<Self> {
|
||||
if let Value::Table(table) = value {
|
||||
let vec = table.sequence_values().collect::<Result<Vec<_>>>()?;
|
||||
vec.try_into()
|
||||
.map_err(|vec: Vec<T>| Error::FromLuaConversionError {
|
||||
from: "Table",
|
||||
to: "Array",
|
||||
message: Some(format!("expected table of length {}, got {}", N, vec.len())),
|
||||
})
|
||||
} else {
|
||||
Err(Error::FromLuaConversionError {
|
||||
from: value.type_name(),
|
||||
to: "Array",
|
||||
message: Some("expected table".to_string()),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua, T: ToLua<'lua>> ToLua<'lua> for Box<[T]> {
|
||||
|
||||
+41
-15
@@ -96,8 +96,8 @@ pub enum Error {
|
||||
/// [`Thread::status`] can be used to check if the coroutine can be resumed without causing this
|
||||
/// error.
|
||||
///
|
||||
/// [`Thread::resume`]: struct.Thread.html#method.resume
|
||||
/// [`Thread::status`]: struct.Thread.html#method.status
|
||||
/// [`Thread::resume`]: crate::Thread::resume
|
||||
/// [`Thread::status`]: crate::Thread::status
|
||||
CoroutineInactive,
|
||||
/// An [`AnyUserData`] is not the expected type in a borrow.
|
||||
///
|
||||
@@ -105,15 +105,15 @@ pub enum Error {
|
||||
/// metamethods for binary operators. Refer to the documentation of [`UserDataMethods`] for
|
||||
/// details.
|
||||
///
|
||||
/// [`AnyUserData`]: struct.AnyUserData.html
|
||||
/// [`UserDataMethods`]: trait.UserDataMethods.html
|
||||
/// [`AnyUserData`]: crate::AnyUserData
|
||||
/// [`UserDataMethods`]: crate::UserDataMethods
|
||||
UserDataTypeMismatch,
|
||||
/// An [`AnyUserData`] borrow failed because it has been destructed.
|
||||
///
|
||||
/// This error can happen either due to to being destructed in a previous __gc, or due to being
|
||||
/// destructed from exiting a `Lua::scope` call.
|
||||
///
|
||||
/// [`AnyUserData`]: struct.AnyUserData.html
|
||||
/// [`AnyUserData`]: crate::AnyUserData
|
||||
UserDataDestructed,
|
||||
/// An [`AnyUserData`] immutable borrow failed because it is already borrowed mutably.
|
||||
///
|
||||
@@ -121,8 +121,8 @@ pub enum Error {
|
||||
/// tries to call a method on the same [`UserData`] type. Consider restructuring your API to
|
||||
/// prevent these errors.
|
||||
///
|
||||
/// [`AnyUserData`]: struct.AnyUserData.html
|
||||
/// [`UserData`]: trait.UserData.html
|
||||
/// [`AnyUserData`]: crate::AnyUserData
|
||||
/// [`UserData`]: crate::UserData
|
||||
UserDataBorrowError,
|
||||
/// An [`AnyUserData`] mutable borrow failed because it is already borrowed.
|
||||
///
|
||||
@@ -130,22 +130,24 @@ pub enum Error {
|
||||
/// tries to call a method on the same [`UserData`] type. Consider restructuring your API to
|
||||
/// prevent these errors.
|
||||
///
|
||||
/// [`AnyUserData`]: struct.AnyUserData.html
|
||||
/// [`UserData`]: trait.UserData.html
|
||||
/// [`AnyUserData`]: crate::AnyUserData
|
||||
/// [`UserData`]: crate::UserData
|
||||
UserDataBorrowMutError,
|
||||
/// A [`MetaMethod`] operation is restricted (typically for `__gc` or `__metatable`).
|
||||
///
|
||||
/// [`MetaMethod`]: enum.MetaMethod.html
|
||||
/// [`MetaMethod`]: crate::MetaMethod
|
||||
MetaMethodRestricted(StdString),
|
||||
/// A [`MetaMethod`] (eg. `__index` or `__newindex`) has invalid type.
|
||||
///
|
||||
/// [`MetaMethod`]: enum.MetaMethod.html
|
||||
/// [`MetaMethod`]: crate::MetaMethod
|
||||
MetaMethodTypeError {
|
||||
method: StdString,
|
||||
type_name: &'static str,
|
||||
message: Option<StdString>,
|
||||
},
|
||||
/// A `RegistryKey` produced from a different Lua state was used.
|
||||
/// A [`RegistryKey`] produced from a different Lua state was used.
|
||||
///
|
||||
/// [`RegistryKey`]: crate::RegistryKey
|
||||
MismatchedRegistryKey,
|
||||
/// A Rust callback returned `Err`, raising the contained `Error` as a Lua error.
|
||||
CallbackError {
|
||||
@@ -245,8 +247,28 @@ impl fmt::Display for Error {
|
||||
Error::MismatchedRegistryKey => {
|
||||
write!(fmt, "RegistryKey used from different Lua state")
|
||||
}
|
||||
Error::CallbackError { ref traceback, .. } => {
|
||||
write!(fmt, "callback error\n{}", traceback)
|
||||
Error::CallbackError { ref cause, ref traceback } => {
|
||||
writeln!(fmt, "callback error")?;
|
||||
// Trace errors down to the root
|
||||
let (mut cause, mut full_traceback) = (cause, None);
|
||||
while let Error::CallbackError { cause: ref cause2, traceback: ref traceback2 } = **cause {
|
||||
cause = cause2;
|
||||
full_traceback = Some(traceback2);
|
||||
}
|
||||
if let Some(full_traceback) = full_traceback {
|
||||
let traceback = traceback.trim_start_matches("stack traceback:");
|
||||
let traceback = traceback.trim_start().trim_end();
|
||||
// Try to find local traceback within the full traceback
|
||||
if let Some(pos) = full_traceback.find(traceback) {
|
||||
write!(fmt, "{}", &full_traceback[..pos])?;
|
||||
writeln!(fmt, ">{}", &full_traceback[pos..].trim_end())?;
|
||||
} else {
|
||||
writeln!(fmt, "{}", full_traceback.trim_end())?;
|
||||
}
|
||||
} else {
|
||||
writeln!(fmt, "{}", traceback.trim_end())?;
|
||||
}
|
||||
write!(fmt, "caused by: {}", cause)
|
||||
}
|
||||
Error::PreviouslyResumedPanic => {
|
||||
write!(fmt, "previously resumed panic returned again")
|
||||
@@ -267,7 +289,11 @@ impl fmt::Display for Error {
|
||||
impl StdError for Error {
|
||||
fn source(&self) -> Option<&(dyn StdError + 'static)> {
|
||||
match *self {
|
||||
Error::CallbackError { ref cause, .. } => Some(cause.as_ref()),
|
||||
// An error type with a source error should either return that error via source or
|
||||
// include that source's error message in its own Display output, but never both.
|
||||
// https://blog.rust-lang.org/inside-rust/2021/07/01/What-the-error-handling-project-group-is-working-towards.html
|
||||
// Given that we include source to fmt::Display implementation for `CallbackError`, this call returns nothing.
|
||||
Error::CallbackError { .. } => None,
|
||||
Error::ExternalError(ref err) => err.source(),
|
||||
_ => None,
|
||||
}
|
||||
|
||||
+13
-4
@@ -23,6 +23,7 @@
|
||||
|
||||
//! Contains definitions from `lua.h`.
|
||||
|
||||
use std::marker::{PhantomData, PhantomPinned};
|
||||
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
|
||||
use std::os::raw::c_uchar;
|
||||
use std::os::raw::{c_char, c_int, c_void};
|
||||
@@ -63,7 +64,7 @@ pub use super::compat53::lua_getglobal;
|
||||
|
||||
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
|
||||
#[inline(always)]
|
||||
pub fn lua_upvalueindex(i: c_int) -> c_int {
|
||||
pub const fn lua_upvalueindex(i: c_int) -> c_int {
|
||||
LUA_REGISTRYINDEX - i
|
||||
}
|
||||
|
||||
@@ -81,7 +82,11 @@ pub const LUA_ERRERR: c_int = 5;
|
||||
pub const LUA_ERRERR: c_int = 6;
|
||||
|
||||
/// A raw Lua state associated with a thread.
|
||||
pub type lua_State = c_void;
|
||||
#[repr(C)]
|
||||
pub struct lua_State {
|
||||
_data: [u8; 0],
|
||||
_marker: PhantomData<(*mut u8, PhantomPinned)>,
|
||||
}
|
||||
|
||||
// basic types
|
||||
pub const LUA_TNONE: c_int = -1;
|
||||
@@ -96,6 +101,10 @@ pub const LUA_TFUNCTION: c_int = 6;
|
||||
pub const LUA_TUSERDATA: c_int = 7;
|
||||
pub const LUA_TTHREAD: c_int = 8;
|
||||
|
||||
// Type produced by LuaJIT FFI module
|
||||
#[cfg(feature = "luajit")]
|
||||
pub const LUA_TCDATA: c_int = 10;
|
||||
|
||||
#[cfg(feature = "lua54")]
|
||||
pub const LUA_NUMTYPES: c_int = 9;
|
||||
#[cfg(any(feature = "lua53", feature = "lua52"))]
|
||||
@@ -569,7 +578,7 @@ pub unsafe fn lua_resume(
|
||||
// warning-related functions
|
||||
#[cfg(feature = "lua54")]
|
||||
extern "C" {
|
||||
pub fn lua_setwarnf(L: *mut lua_State, f: lua_WarnFunction, ud: *mut c_void);
|
||||
pub fn lua_setwarnf(L: *mut lua_State, f: Option<lua_WarnFunction>, ud: *mut c_void);
|
||||
pub fn lua_warning(L: *mut lua_State, msg: *const c_char, tocont: c_int);
|
||||
}
|
||||
|
||||
@@ -621,7 +630,7 @@ extern "C" {
|
||||
#[cfg(any(feature = "lua54", feature = "lua53"))]
|
||||
#[inline(always)]
|
||||
pub unsafe fn lua_getextraspace(L: *mut lua_State) -> *mut c_void {
|
||||
L.offset(-super::glue::LUA_EXTRASPACE as isize) as *mut c_void
|
||||
(L as *mut c_char).offset(-super::glue::LUA_EXTRASPACE as isize) as *mut c_void
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
|
||||
|
||||
@@ -236,6 +236,9 @@ pub use self::lua::LUA_ERRGCMM;
|
||||
#[cfg(any(feature = "lua51", feature = "luajit"))]
|
||||
pub use self::lua::{LUA_ENVIRONINDEX, LUA_GLOBALSINDEX};
|
||||
|
||||
#[cfg(feature = "luajit")]
|
||||
pub use self::lua::LUA_TCDATA;
|
||||
|
||||
// constants from lauxlib.h
|
||||
pub use self::lauxlib::{LUA_ERRFILE, LUA_NOREF, LUA_REFNIL};
|
||||
|
||||
|
||||
+11
-7
@@ -6,7 +6,7 @@ use crate::error::{Error, Result};
|
||||
use crate::ffi;
|
||||
use crate::types::LuaRef;
|
||||
use crate::util::{assert_stack, check_stack, error_traceback, pop_error, StackGuard};
|
||||
use crate::value::{FromLuaMulti, MultiValue, ToLuaMulti};
|
||||
use crate::value::{FromLuaMulti, ToLuaMulti};
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
use {futures_core::future::LocalBoxFuture, futures_util::future};
|
||||
@@ -59,7 +59,7 @@ impl<'lua> Function<'lua> {
|
||||
pub fn call<A: ToLuaMulti<'lua>, R: FromLuaMulti<'lua>>(&self, args: A) -> Result<R> {
|
||||
let lua = self.0.lua;
|
||||
|
||||
let args = args.to_lua_multi(lua)?;
|
||||
let mut args = args.to_lua_multi(lua)?;
|
||||
let nargs = args.len() as c_int;
|
||||
|
||||
let results = unsafe {
|
||||
@@ -69,7 +69,7 @@ impl<'lua> Function<'lua> {
|
||||
ffi::lua_pushcfunction(lua.state, error_traceback);
|
||||
let stack_start = ffi::lua_gettop(lua.state);
|
||||
lua.push_ref(&self.0);
|
||||
for arg in args {
|
||||
for arg in args.drain_all() {
|
||||
lua.push_value(arg)?;
|
||||
}
|
||||
let ret = ffi::lua_pcall(lua.state, nargs, ffi::LUA_MULTRET, stack_start);
|
||||
@@ -77,7 +77,7 @@ impl<'lua> Function<'lua> {
|
||||
return Err(pop_error(lua.state, ret));
|
||||
}
|
||||
let nresults = ffi::lua_gettop(lua.state) - stack_start;
|
||||
let mut results = MultiValue::new();
|
||||
let mut results = args; // Reuse MultiValue container
|
||||
assert_stack(lua.state, 2);
|
||||
for _ in 0..nresults {
|
||||
results.push_front(lua.pop_value());
|
||||
@@ -116,7 +116,7 @@ impl<'lua> Function<'lua> {
|
||||
/// # }
|
||||
/// ```
|
||||
///
|
||||
/// [`AsyncThread`]: struct.AsyncThread.html
|
||||
/// [`AsyncThread`]: crate::AsyncThread
|
||||
#[cfg(feature = "async")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
|
||||
pub fn call_async<'fut, A, R>(&self, args: A) -> LocalBoxFuture<'fut, Result<R>>
|
||||
@@ -126,8 +126,12 @@ impl<'lua> Function<'lua> {
|
||||
R: FromLuaMulti<'lua> + 'fut,
|
||||
{
|
||||
let lua = self.0.lua;
|
||||
match lua.create_thread(self.clone()) {
|
||||
Ok(t) => Box::pin(t.into_async(args)),
|
||||
match lua.create_recycled_thread(self.clone()) {
|
||||
Ok(t) => {
|
||||
let mut t = t.into_async(args);
|
||||
t.set_recyclable(true);
|
||||
Box::pin(t)
|
||||
}
|
||||
Err(e) => Box::pin(future::err(e)),
|
||||
}
|
||||
}
|
||||
|
||||
+62
-56
@@ -1,29 +1,40 @@
|
||||
use std::cell::UnsafeCell;
|
||||
use std::ffi::CStr;
|
||||
use std::marker::PhantomData;
|
||||
use std::ops::{BitOr, BitOrAssign};
|
||||
use std::os::raw::{c_char, c_int};
|
||||
|
||||
use crate::ffi::{self, lua_Debug, lua_State};
|
||||
use crate::ffi::{self, lua_Debug};
|
||||
use crate::lua::Lua;
|
||||
use crate::util::callback_error;
|
||||
|
||||
/// Contains information about currently executing Lua code.
|
||||
///
|
||||
/// The `Debug` structure is provided as a parameter to the hook function set with
|
||||
/// [`Lua::set_hook`]. You may call the methods on this structure to retrieve information about the
|
||||
/// Lua code executing at the time that the hook function was called. Further information can be
|
||||
/// found in the [Lua 5.3 documentation][lua_doc].
|
||||
/// found in the Lua [documentation][lua_doc].
|
||||
///
|
||||
/// [lua_doc]: https://www.lua.org/manual/5.3/manual.html#lua_Debug
|
||||
/// [`Lua::set_hook`]: struct.Lua.html#method.set_hook
|
||||
#[derive(Clone)]
|
||||
pub struct Debug<'a> {
|
||||
ar: *mut lua_Debug,
|
||||
state: *mut lua_State,
|
||||
_phantom: PhantomData<&'a ()>,
|
||||
/// [lua_doc]: https://www.lua.org/manual/5.4/manual.html#lua_Debug
|
||||
/// [`Lua::set_hook`]: crate::Lua::set_hook
|
||||
pub struct Debug<'lua> {
|
||||
lua: &'lua Lua,
|
||||
ar: ActivationRecord,
|
||||
}
|
||||
|
||||
impl<'a> Debug<'a> {
|
||||
impl<'lua> Debug<'lua> {
|
||||
pub(crate) fn new(lua: &'lua Lua, ar: *mut lua_Debug) -> Self {
|
||||
Debug {
|
||||
lua,
|
||||
ar: ActivationRecord::Borrowed(ar),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn new_owned(lua: &'lua Lua, ar: lua_Debug) -> Self {
|
||||
Debug {
|
||||
lua,
|
||||
ar: ActivationRecord::Owned(UnsafeCell::new(ar)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the specific event that triggered the hook.
|
||||
///
|
||||
/// For [Lua 5.1] `DebugEvent::TailCall` is used for return events to indicate a return
|
||||
@@ -32,44 +43,44 @@ impl<'a> Debug<'a> {
|
||||
/// [Lua 5.1]: https://www.lua.org/manual/5.1/manual.html#pdf-LUA_HOOKTAILRET
|
||||
pub fn event(&self) -> DebugEvent {
|
||||
unsafe {
|
||||
match (*self.ar).event {
|
||||
match (*self.ar.get()).event {
|
||||
ffi::LUA_HOOKCALL => DebugEvent::Call,
|
||||
ffi::LUA_HOOKRET => DebugEvent::Ret,
|
||||
ffi::LUA_HOOKTAILCALL => DebugEvent::TailCall,
|
||||
ffi::LUA_HOOKLINE => DebugEvent::Line,
|
||||
ffi::LUA_HOOKCOUNT => DebugEvent::Count,
|
||||
event => mlua_panic!("Unknown Lua event code: {}", event),
|
||||
event => DebugEvent::Unknown(event),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Corresponds to the `n` what mask.
|
||||
pub fn names(&self) -> DebugNames<'a> {
|
||||
pub fn names(&self) -> DebugNames<'lua> {
|
||||
unsafe {
|
||||
mlua_assert!(
|
||||
ffi::lua_getinfo(self.state, cstr!("n"), self.ar) != 0,
|
||||
ffi::lua_getinfo(self.lua.state, cstr!("n"), self.ar.get()) != 0,
|
||||
"lua_getinfo failed with `n`"
|
||||
);
|
||||
DebugNames {
|
||||
name: ptr_to_str((*self.ar).name),
|
||||
name_what: ptr_to_str((*self.ar).namewhat),
|
||||
name: ptr_to_str((*self.ar.get()).name),
|
||||
name_what: ptr_to_str((*self.ar.get()).namewhat),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Corresponds to the `S` what mask.
|
||||
pub fn source(&self) -> DebugSource<'a> {
|
||||
pub fn source(&self) -> DebugSource<'lua> {
|
||||
unsafe {
|
||||
mlua_assert!(
|
||||
ffi::lua_getinfo(self.state, cstr!("S"), self.ar) != 0,
|
||||
ffi::lua_getinfo(self.lua.state, cstr!("S"), self.ar.get()) != 0,
|
||||
"lua_getinfo failed with `S`"
|
||||
);
|
||||
DebugSource {
|
||||
source: ptr_to_str((*self.ar).source),
|
||||
short_src: ptr_to_str((*self.ar).short_src.as_ptr()),
|
||||
line_defined: (*self.ar).linedefined as i32,
|
||||
last_line_defined: (*self.ar).lastlinedefined as i32,
|
||||
what: ptr_to_str((*self.ar).what),
|
||||
source: ptr_to_str((*self.ar.get()).source),
|
||||
short_src: ptr_to_str((*self.ar.get()).short_src.as_ptr()),
|
||||
line_defined: (*self.ar.get()).linedefined as i32,
|
||||
last_line_defined: (*self.ar.get()).lastlinedefined as i32,
|
||||
what: ptr_to_str((*self.ar.get()).what),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -78,10 +89,10 @@ impl<'a> Debug<'a> {
|
||||
pub fn curr_line(&self) -> i32 {
|
||||
unsafe {
|
||||
mlua_assert!(
|
||||
ffi::lua_getinfo(self.state, cstr!("l"), self.ar) != 0,
|
||||
ffi::lua_getinfo(self.lua.state, cstr!("l"), self.ar.get()) != 0,
|
||||
"lua_getinfo failed with `l`"
|
||||
);
|
||||
(*self.ar).currentline as i32
|
||||
(*self.ar.get()).currentline as i32
|
||||
}
|
||||
}
|
||||
|
||||
@@ -90,10 +101,10 @@ impl<'a> Debug<'a> {
|
||||
pub fn is_tail_call(&self) -> bool {
|
||||
unsafe {
|
||||
mlua_assert!(
|
||||
ffi::lua_getinfo(self.state, cstr!("t"), self.ar) != 0,
|
||||
ffi::lua_getinfo(self.lua.state, cstr!("t"), self.ar.get()) != 0,
|
||||
"lua_getinfo failed with `t`"
|
||||
);
|
||||
(*self.ar).currentline != 0
|
||||
(*self.ar.get()).currentline != 0
|
||||
}
|
||||
}
|
||||
|
||||
@@ -101,20 +112,35 @@ impl<'a> Debug<'a> {
|
||||
pub fn stack(&self) -> DebugStack {
|
||||
unsafe {
|
||||
mlua_assert!(
|
||||
ffi::lua_getinfo(self.state, cstr!("u"), self.ar) != 0,
|
||||
ffi::lua_getinfo(self.lua.state, cstr!("u"), self.ar.get()) != 0,
|
||||
"lua_getinfo failed with `u`"
|
||||
);
|
||||
DebugStack {
|
||||
num_ups: (*self.ar).nups as i32,
|
||||
num_ups: (*self.ar.get()).nups as i32,
|
||||
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
|
||||
num_params: (*self.ar).nparams as i32,
|
||||
num_params: (*self.ar.get()).nparams as i32,
|
||||
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
|
||||
is_vararg: (*self.ar).isvararg != 0,
|
||||
is_vararg: (*self.ar.get()).isvararg != 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum ActivationRecord {
|
||||
Borrowed(*mut lua_Debug),
|
||||
Owned(UnsafeCell<lua_Debug>),
|
||||
}
|
||||
|
||||
impl ActivationRecord {
|
||||
#[inline]
|
||||
fn get(&self) -> *mut lua_Debug {
|
||||
match self {
|
||||
ActivationRecord::Borrowed(x) => *x,
|
||||
ActivationRecord::Owned(x) => x.get(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents a specific event that triggered the hook.
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub enum DebugEvent {
|
||||
@@ -123,6 +149,7 @@ pub enum DebugEvent {
|
||||
TailCall,
|
||||
Line,
|
||||
Count,
|
||||
Unknown(c_int),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
@@ -144,10 +171,10 @@ pub struct DebugSource<'a> {
|
||||
pub struct DebugStack {
|
||||
pub num_ups: i32,
|
||||
/// Requires `feature = "lua54/lua53/lua52"`
|
||||
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52", doc))]
|
||||
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
|
||||
pub num_params: i32,
|
||||
/// Requires `feature = "lua54/lua53/lua52"`
|
||||
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52", doc))]
|
||||
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
|
||||
pub is_vararg: bool,
|
||||
}
|
||||
|
||||
@@ -255,27 +282,6 @@ impl BitOrAssign for HookTriggers {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) unsafe extern "C" fn hook_proc(state: *mut lua_State, ar: *mut lua_Debug) {
|
||||
callback_error(state, |_| {
|
||||
let debug = Debug {
|
||||
ar,
|
||||
state,
|
||||
_phantom: PhantomData,
|
||||
};
|
||||
|
||||
let lua = mlua_expect!(Lua::make_from_ptr(state), "cannot make Lua instance");
|
||||
let hook_cb = mlua_expect!(lua.hook_callback(), "no hook callback set in hook_proc");
|
||||
|
||||
#[allow(clippy::match_wild_err_arm)]
|
||||
match hook_cb.try_borrow_mut() {
|
||||
Ok(mut b) => (&mut *b)(&lua, debug),
|
||||
Err(_) => mlua_panic!("Lua should not allow hooks to be called within another hook"),
|
||||
}?;
|
||||
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
unsafe fn ptr_to_str<'a>(input: *const c_char) -> Option<&'a [u8]> {
|
||||
if input.is_null() {
|
||||
None
|
||||
|
||||
+42
-25
@@ -50,29 +50,29 @@
|
||||
//! to [`Function`]s and [`UserData`].
|
||||
//!
|
||||
//! [Lua programming language]: https://www.lua.org/
|
||||
//! [`Lua`]: struct.Lua.html
|
||||
//! [executing]: struct.Chunk.html#method.exec
|
||||
//! [evaluating]: struct.Chunk.html#method.eval
|
||||
//! [globals]: struct.Lua.html#method.globals
|
||||
//! [`ToLua`]: trait.ToLua.html
|
||||
//! [`FromLua`]: trait.FromLua.html
|
||||
//! [`ToLuaMulti`]: trait.ToLuaMulti.html
|
||||
//! [`FromLuaMulti`]: trait.FromLuaMulti.html
|
||||
//! [`Function`]: struct.Function.html
|
||||
//! [`UserData`]: trait.UserData.html
|
||||
//! [`UserDataFields`]: trait.UserDataFields.html
|
||||
//! [`UserDataMethods`]: trait.UserDataMethods.html
|
||||
//! [`LuaSerdeExt`]: serde/trait.LuaSerdeExt.html
|
||||
//! [`Value`]: enum.Value.html
|
||||
//! [`create_async_function`]: struct.Lua.html#method.create_async_function
|
||||
//! [`call_async`]: struct.Function.html#method.call_async
|
||||
//! [`AsyncThread`]: struct.AsyncThread.html
|
||||
//! [`Future`]: ../futures_core/future/trait.Future.html
|
||||
//! [`Lua`]: crate::Lua
|
||||
//! [executing]: crate::Chunk::exec
|
||||
//! [evaluating]: crate::Chunk::eval
|
||||
//! [globals]: crate::Lua::globals
|
||||
//! [`ToLua`]: crate::ToLua
|
||||
//! [`FromLua`]: crate::FromLua
|
||||
//! [`ToLuaMulti`]: crate::ToLuaMulti
|
||||
//! [`FromLuaMulti`]: crate::FromLuaMulti
|
||||
//! [`Function`]: crate::Function
|
||||
//! [`UserData`]: crate::UserData
|
||||
//! [`UserDataFields`]: crate::UserDataFields
|
||||
//! [`UserDataMethods`]: crate::UserDataMethods
|
||||
//! [`LuaSerdeExt`]: crate::LuaSerdeExt
|
||||
//! [`Value`]: crate::Value
|
||||
//! [`create_async_function`]: crate::Lua::create_async_function
|
||||
//! [`call_async`]: crate::Function::call_async
|
||||
//! [`AsyncThread`]: crate::AsyncThread
|
||||
//! [`Future`]: std::future::Future
|
||||
//! [`serde::Serialize`]: https://docs.serde.rs/serde/ser/trait.Serialize.html
|
||||
//! [`serde::Deserialize`]: https://docs.serde.rs/serde/de/trait.Deserialize.html
|
||||
|
||||
// mlua types in rustdoc of other crates get linked to here.
|
||||
#![doc(html_root_url = "https://docs.rs/mlua/0.6.4")]
|
||||
#![doc(html_root_url = "https://docs.rs/mlua/0.7.4")]
|
||||
// Deny warnings inside doc tests / examples. When this isn't present, rustdoc doesn't show *any*
|
||||
// warnings at all.
|
||||
#![doc(test(attr(deny(warnings))))]
|
||||
@@ -98,6 +98,8 @@ mod userdata;
|
||||
mod util;
|
||||
mod value;
|
||||
|
||||
pub mod prelude;
|
||||
|
||||
pub use crate::{ffi::lua_CFunction, ffi::lua_State};
|
||||
|
||||
pub use crate::error::{Error, ExternalError, ExternalResult, Result};
|
||||
@@ -120,13 +122,11 @@ pub use crate::value::{FromLua, FromLuaMulti, MultiValue, Nil, ToLua, ToLuaMulti
|
||||
pub use crate::thread::AsyncThread;
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "serialize")))]
|
||||
#[doc(inline)]
|
||||
pub use crate::serde::{
|
||||
de::Options as DeserializeOptions, ser::Options as SerializeOptions, LuaSerdeExt,
|
||||
};
|
||||
|
||||
pub mod prelude;
|
||||
#[cfg(feature = "serialize")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "serialize")))]
|
||||
pub mod serde;
|
||||
@@ -185,13 +185,30 @@ extern crate mlua_derive;
|
||||
///
|
||||
/// Everything else should work.
|
||||
///
|
||||
/// [`AsChunk`]: trait.AsChunk.html
|
||||
/// [`UserData`]: trait.UserData.html
|
||||
/// [`ToLua`]: trait.ToLua.html
|
||||
/// [`AsChunk`]: crate::AsChunk
|
||||
/// [`UserData`]: crate::UserData
|
||||
/// [`ToLua`]: crate::ToLua
|
||||
#[cfg(any(feature = "macros"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "macros")))]
|
||||
pub use mlua_derive::chunk;
|
||||
|
||||
#[cfg(any(feature = "module"))]
|
||||
/// Registers Lua module entrypoint.
|
||||
///
|
||||
/// You can register multiple entrypoints as required.
|
||||
///
|
||||
/// ```
|
||||
/// use mlua::{Lua, Result, Table};
|
||||
///
|
||||
/// #[mlua::lua_module]
|
||||
/// fn my_module(lua: &Lua) -> Result<Table> {
|
||||
/// let exports = lua.create_table()?;
|
||||
/// exports.set("hello", "world")?;
|
||||
/// Ok(exports)
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Internally in the code above the compiler defines C function `luaopen_my_module`.
|
||||
///
|
||||
#[cfg(any(feature = "module", docsrs))]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "module")))]
|
||||
pub use mlua_derive::lua_module;
|
||||
|
||||
+638
-147
File diff suppressed because it is too large
Load Diff
+20
-15
@@ -12,8 +12,7 @@ use crate::value::{FromLua, FromLuaMulti, MultiValue, Nil, ToLua, ToLuaMulti};
|
||||
/// on success, or in the case of an error, returning `nil` and an error message.
|
||||
impl<'lua, T: ToLua<'lua>, E: ToLua<'lua>> ToLuaMulti<'lua> for StdResult<T, E> {
|
||||
fn to_lua_multi(self, lua: &'lua Lua) -> Result<MultiValue<'lua>> {
|
||||
let mut result = MultiValue::new();
|
||||
|
||||
let mut result = MultiValue::new_or_cached(lua);
|
||||
match self {
|
||||
Ok(v) => result.push_front(v.to_lua(lua)?),
|
||||
Err(e) => {
|
||||
@@ -21,14 +20,13 @@ impl<'lua, T: ToLua<'lua>, E: ToLua<'lua>> ToLuaMulti<'lua> for StdResult<T, E>
|
||||
result.push_front(Nil);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua, T: ToLua<'lua>> ToLuaMulti<'lua> for T {
|
||||
fn to_lua_multi(self, lua: &'lua Lua) -> Result<MultiValue<'lua>> {
|
||||
let mut v = MultiValue::new();
|
||||
let mut v = MultiValue::new_or_cached(lua);
|
||||
v.push_front(self.to_lua(lua)?);
|
||||
Ok(v)
|
||||
}
|
||||
@@ -36,7 +34,9 @@ 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> {
|
||||
T::from_lua(values.pop_front().unwrap_or(Nil), lua)
|
||||
let res = T::from_lua(values.pop_front().unwrap_or(Nil), lua);
|
||||
lua.cache_multivalue(values);
|
||||
res
|
||||
}
|
||||
}
|
||||
|
||||
@@ -76,8 +76,8 @@ impl<'lua> FromLuaMulti<'lua> for MultiValue<'lua> {
|
||||
/// # }
|
||||
/// ```
|
||||
///
|
||||
/// [`FromLua`]: trait.FromLua.html
|
||||
/// [`MultiValue`]: struct.MultiValue.html
|
||||
/// [`FromLua`]: crate::FromLua
|
||||
/// [`MultiValue`]: crate::MultiValue
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Variadic<T>(Vec<T>);
|
||||
|
||||
@@ -125,30 +125,35 @@ impl<T> DerefMut for Variadic<T> {
|
||||
|
||||
impl<'lua, T: ToLua<'lua>> ToLuaMulti<'lua> for Variadic<T> {
|
||||
fn to_lua_multi(self, lua: &'lua Lua) -> Result<MultiValue<'lua>> {
|
||||
self.0.into_iter().map(|e| e.to_lua(lua)).collect()
|
||||
let mut values = MultiValue::new_or_cached(lua);
|
||||
values.refill(self.0.into_iter().map(|e| e.to_lua(lua)))?;
|
||||
Ok(values)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua, T: FromLua<'lua>> FromLuaMulti<'lua> for Variadic<T> {
|
||||
fn from_lua_multi(values: MultiValue<'lua>, lua: &'lua Lua) -> Result<Self> {
|
||||
values
|
||||
.into_iter()
|
||||
fn from_lua_multi(mut values: MultiValue<'lua>, lua: &'lua Lua) -> Result<Self> {
|
||||
let res = values
|
||||
.drain_all()
|
||||
.map(|e| T::from_lua(e, lua))
|
||||
.collect::<Result<Vec<T>>>()
|
||||
.map(Variadic)
|
||||
.map(Variadic);
|
||||
lua.cache_multivalue(values);
|
||||
res
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! impl_tuple {
|
||||
() => (
|
||||
impl<'lua> ToLuaMulti<'lua> for () {
|
||||
fn to_lua_multi(self, _: &'lua Lua) -> Result<MultiValue<'lua>> {
|
||||
Ok(MultiValue::new())
|
||||
fn to_lua_multi(self, lua: &'lua Lua) -> Result<MultiValue<'lua>> {
|
||||
Ok(MultiValue::new_or_cached(lua))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> FromLuaMulti<'lua> for () {
|
||||
fn from_lua_multi(_: MultiValue<'lua>, _: &'lua Lua) -> Result<Self> {
|
||||
fn from_lua_multi(values: MultiValue<'lua>, lua: &'lua Lua) -> Result<Self> {
|
||||
lua.cache_multivalue(values);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
+9
-6
@@ -1,23 +1,26 @@
|
||||
//! Re-exports most types with an extra `Lua*` prefix to prevent name clashes.
|
||||
|
||||
#[doc(no_inline)]
|
||||
pub use crate::{
|
||||
AnyUserData as LuaAnyUserData, Chunk as LuaChunk, Error as LuaError,
|
||||
ExternalError as LuaExternalError, ExternalResult as LuaExternalResult, FromLua, FromLuaMulti,
|
||||
Function as LuaFunction, GCMode as LuaGCMode, Integer as LuaInteger,
|
||||
LightUserData as LuaLightUserData, Lua, LuaOptions, MetaMethod as LuaMetaMethod,
|
||||
MultiValue as LuaMultiValue, Nil as LuaNil, Number as LuaNumber, RegistryKey as LuaRegistryKey,
|
||||
Result as LuaResult, String as LuaString, Table as LuaTable, TableExt as LuaTableExt,
|
||||
TablePairs as LuaTablePairs, TableSequence as LuaTableSequence, Thread as LuaThread,
|
||||
ThreadStatus as LuaThreadStatus, ToLua, ToLuaMulti, UserData as LuaUserData,
|
||||
UserDataFields as LuaUserDataFields, UserDataMetatable as LuaUserDataMetatable,
|
||||
UserDataMethods as LuaUserDataMethods, Value as LuaValue,
|
||||
Result as LuaResult, StdLib as LuaStdLib, String as LuaString, Table as LuaTable,
|
||||
TableExt as LuaTableExt, TablePairs as LuaTablePairs, TableSequence as LuaTableSequence,
|
||||
Thread as LuaThread, ThreadStatus as LuaThreadStatus, ToLua, ToLuaMulti,
|
||||
UserData as LuaUserData, UserDataFields as LuaUserDataFields,
|
||||
UserDataMetatable as LuaUserDataMetatable, UserDataMethods as LuaUserDataMethods,
|
||||
Value as LuaValue,
|
||||
};
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
#[doc(no_inline)]
|
||||
pub use crate::AsyncThread as LuaAsyncThread;
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
#[doc(inline)]
|
||||
#[doc(no_inline)]
|
||||
pub use crate::{
|
||||
DeserializeOptions as LuaDeserializeOptions, LuaSerdeExt,
|
||||
SerializeOptions as LuaSerializeOptions,
|
||||
|
||||
+97
-32
@@ -23,6 +23,9 @@ use crate::util::{
|
||||
};
|
||||
use crate::value::{FromLua, FromLuaMulti, MultiValue, ToLua, ToLuaMulti, Value};
|
||||
|
||||
#[cfg(feature = "lua54")]
|
||||
use crate::userdata::USER_VALUE_MAXSLOT;
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
use {
|
||||
crate::types::{AsyncCallback, AsyncCallbackUpvalue, AsyncPollUpvalue},
|
||||
@@ -35,7 +38,7 @@ use {
|
||||
///
|
||||
/// See [`Lua::scope`] for more details.
|
||||
///
|
||||
/// [`Lua::scope`]: struct.Lua.html#method.scope
|
||||
/// [`Lua::scope`]: crate::Lua.html::scope
|
||||
pub struct Scope<'lua, 'scope> {
|
||||
lua: &'lua Lua,
|
||||
destructors: RefCell<Vec<(LuaRef<'lua>, DestructorCallback<'lua>)>>,
|
||||
@@ -58,8 +61,8 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
|
||||
/// This is a version of [`Lua::create_function`] that creates a callback which expires on
|
||||
/// scope drop. See [`Lua::scope`] for more details.
|
||||
///
|
||||
/// [`Lua::create_function`]: struct.Lua.html#method.create_function
|
||||
/// [`Lua::scope`]: struct.Lua.html#method.scope
|
||||
/// [`Lua::create_function`]: crate::Lua::create_function
|
||||
/// [`Lua::scope`]: crate::Lua::scope
|
||||
pub fn create_function<'callback, A, R, F>(&'callback self, func: F) -> Result<Function<'lua>>
|
||||
where
|
||||
A: FromLuaMulti<'callback>,
|
||||
@@ -87,8 +90,8 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
|
||||
/// This is a version of [`Lua::create_function_mut`] that creates a callback which expires
|
||||
/// on scope drop. See [`Lua::scope`] and [`Scope::create_function`] for more details.
|
||||
///
|
||||
/// [`Lua::create_function_mut`]: struct.Lua.html#method.create_function_mut
|
||||
/// [`Lua::scope`]: struct.Lua.html#method.scope
|
||||
/// [`Lua::create_function_mut`]: crate::Lua::create_function_mut
|
||||
/// [`Lua::scope`]: crate::Lua::scope
|
||||
/// [`Scope::create_function`]: #method.create_function
|
||||
pub fn create_function_mut<'callback, A, R, F>(
|
||||
&'callback self,
|
||||
@@ -114,9 +117,9 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
|
||||
///
|
||||
/// Requires `feature = "async"`
|
||||
///
|
||||
/// [`Lua::create_async_function`]: struct.Lua.html#method.create_async_function
|
||||
/// [`Lua::scope`]: struct.Lua.html#method.scope
|
||||
/// [`Lua::async_scope`]: struct.Lua.html#method.async_scope
|
||||
/// [`Lua::create_async_function`]: crate::Lua::create_async_function
|
||||
/// [`Lua::scope`]: crate::Lua::scope
|
||||
/// [`Lua::async_scope`]: crate::Lua::async_scope
|
||||
#[cfg(feature = "async")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
|
||||
pub fn create_async_function<'callback, A, R, F, FR>(
|
||||
@@ -147,8 +150,8 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
|
||||
/// UserData be 'static).
|
||||
/// See [`Lua::scope`] for more details.
|
||||
///
|
||||
/// [`Lua::create_userdata`]: struct.Lua.html#method.create_userdata
|
||||
/// [`Lua::scope`]: struct.Lua.html#method.scope
|
||||
/// [`Lua::create_userdata`]: crate::Lua::create_userdata
|
||||
/// [`Lua::scope`]: crate::Lua::scope
|
||||
pub fn create_userdata<T>(&self, data: T) -> Result<AnyUserData<'lua>>
|
||||
where
|
||||
T: 'static + UserData,
|
||||
@@ -165,8 +168,8 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
|
||||
///
|
||||
/// Requires `feature = "serialize"`
|
||||
///
|
||||
/// [`Lua::create_ser_userdata`]: struct.Lua.html#method.create_ser_userdata
|
||||
/// [`Lua::scope`]: struct.Lua.html#method.scope
|
||||
/// [`Lua::create_ser_userdata`]: crate::Lua::create_ser_userdata
|
||||
/// [`Lua::scope`]: crate::Lua::scope
|
||||
#[cfg(feature = "serialize")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "serialize")))]
|
||||
pub fn create_ser_userdata<T>(&self, data: T) -> Result<AnyUserData<'lua>>
|
||||
@@ -192,16 +195,27 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
|
||||
let _sg = StackGuard::new(state);
|
||||
assert_stack(state, 2);
|
||||
|
||||
ud.lua.push_ref(&ud);
|
||||
// Check that userdata is not destructed (via `take()` call)
|
||||
if ud.lua.push_userdata_ref(&ud).is_err() {
|
||||
return vec![];
|
||||
}
|
||||
|
||||
// We know the destructor has not run yet because we hold a reference to the userdata.
|
||||
|
||||
// Clear uservalue
|
||||
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
|
||||
ffi::lua_pushnil(state);
|
||||
// Clear associated user values
|
||||
#[cfg(feature = "lua54")]
|
||||
for i in 1..=USER_VALUE_MAXSLOT {
|
||||
ffi::lua_pushnil(state);
|
||||
ffi::lua_setiuservalue(state, -2, i as c_int);
|
||||
}
|
||||
#[cfg(any(feature = "lua53", feature = "lua52"))]
|
||||
{
|
||||
ffi::lua_pushnil(state);
|
||||
ffi::lua_setuservalue(state, -2);
|
||||
}
|
||||
#[cfg(any(feature = "lua51", feature = "luajit"))]
|
||||
ud.lua.push_ref(&newtable.0);
|
||||
ffi::lua_setuservalue(state, -2);
|
||||
{
|
||||
ud.lua.push_ref(&newtable.0);
|
||||
ffi::lua_setuservalue(state, -2);
|
||||
}
|
||||
|
||||
vec![Box::new(take_userdata::<UserDataCell<T>>(state))]
|
||||
});
|
||||
@@ -233,9 +247,9 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
|
||||
/// creating the userdata metatable each time a new userdata is created.
|
||||
///
|
||||
/// [`Scope::create_userdata`]: #method.create_userdata
|
||||
/// [`Lua::create_userdata`]: struct.Lua.html#method.create_userdata
|
||||
/// [`Lua::scope`]: struct.Lua.html#method.scope
|
||||
/// [`UserDataMethods`]: trait.UserDataMethods.html
|
||||
/// [`Lua::create_userdata`]: crate::Lua::create_userdata
|
||||
/// [`Lua::scope`]:crate::Lua::scope
|
||||
/// [`UserDataMethods`]: crate::UserDataMethods
|
||||
pub fn create_nonstatic_userdata<T>(&self, data: T) -> Result<AnyUserData<'lua>>
|
||||
where
|
||||
T: 'scope + UserData,
|
||||
@@ -322,8 +336,19 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
|
||||
let _sg = StackGuard::new(lua.state);
|
||||
check_stack(lua.state, 13)?;
|
||||
|
||||
#[allow(clippy::let_and_return)]
|
||||
let data_ptr = protect_lua!(lua.state, 0, 1, |state| {
|
||||
ffi::lua_newuserdata(state, mem::size_of::<UserDataCell<Rc<RefCell<T>>>>())
|
||||
let ud =
|
||||
ffi::lua_newuserdata(state, mem::size_of::<UserDataCell<Rc<RefCell<T>>>>());
|
||||
|
||||
// Set empty environment for Lua 5.1
|
||||
#[cfg(any(feature = "lua51", feature = "luajit"))]
|
||||
{
|
||||
ffi::lua_newtable(state);
|
||||
ffi::lua_setuservalue(state, -2);
|
||||
}
|
||||
|
||||
ud
|
||||
})?;
|
||||
// Prepare metatable, add meta methods first and then meta fields
|
||||
let meta_methods_nrec = ud_methods.meta_methods.len() + ud_fields.meta_fields.len() + 1;
|
||||
@@ -404,9 +429,10 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
|
||||
let _sg = StackGuard::new(state);
|
||||
assert_stack(state, 2);
|
||||
|
||||
ud.lua.push_ref(&ud);
|
||||
|
||||
// We know the destructor has not run yet because we hold a reference to the userdata.
|
||||
// Check that userdata is valid (very likely)
|
||||
if ud.lua.push_userdata_ref(&ud).is_err() {
|
||||
return vec![];
|
||||
}
|
||||
|
||||
// Deregister metatable
|
||||
ffi::lua_getmetatable(state, -1);
|
||||
@@ -414,12 +440,22 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
|
||||
ffi::lua_pop(state, 1);
|
||||
ud.lua.deregister_userdata_metatable(mt_ptr);
|
||||
|
||||
// Clear uservalue
|
||||
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
|
||||
ffi::lua_pushnil(state);
|
||||
// Clear associated user values
|
||||
#[cfg(feature = "lua54")]
|
||||
for i in 1..=USER_VALUE_MAXSLOT {
|
||||
ffi::lua_pushnil(state);
|
||||
ffi::lua_setiuservalue(state, -2, i as c_int);
|
||||
}
|
||||
#[cfg(any(feature = "lua53", feature = "lua52"))]
|
||||
{
|
||||
ffi::lua_pushnil(state);
|
||||
ffi::lua_setuservalue(state, -2);
|
||||
}
|
||||
#[cfg(any(feature = "lua51", feature = "luajit"))]
|
||||
ud.lua.push_ref(&newtable.0);
|
||||
ffi::lua_setuservalue(state, -2);
|
||||
{
|
||||
ud.lua.push_ref(&newtable.0);
|
||||
ffi::lua_setuservalue(state, -2);
|
||||
}
|
||||
|
||||
// A hack to drop non-static `T`
|
||||
unsafe fn seal<T>(t: T) -> Box<dyn FnOnce() + 'static> {
|
||||
@@ -691,6 +727,21 @@ impl<'lua, T: UserData> UserDataMethods<'lua, T> for NonStaticUserDataMethods<'l
|
||||
));
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "async", not(feature = "lua51")))]
|
||||
fn add_async_meta_method<S, A, R, M, MR>(&mut self, _meta: S, _method: M)
|
||||
where
|
||||
T: Clone,
|
||||
S: Into<MetaMethod>,
|
||||
A: FromLuaMulti<'lua>,
|
||||
R: ToLuaMulti<'lua>,
|
||||
M: 'static + MaybeSend + Fn(&'lua Lua, T, A) -> MR,
|
||||
MR: 'lua + Future<Output = Result<R>>,
|
||||
{
|
||||
// The panic should never happen as async non-static code wouldn't compile
|
||||
// Non-static lifetime must be bounded to 'lua lifetime
|
||||
mlua_panic!("asynchronous meta methods are not supported for non-static userdata")
|
||||
}
|
||||
|
||||
fn add_meta_function<S, A, R, F>(&mut self, meta: S, function: F)
|
||||
where
|
||||
S: Into<MetaMethod>,
|
||||
@@ -720,6 +771,20 @@ impl<'lua, T: UserData> UserDataMethods<'lua, T> for NonStaticUserDataMethods<'l
|
||||
})),
|
||||
));
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "async", not(feature = "lua51")))]
|
||||
fn add_async_meta_function<S, A, R, F, FR>(&mut self, _meta: S, _function: F)
|
||||
where
|
||||
S: Into<MetaMethod>,
|
||||
A: FromLuaMulti<'lua>,
|
||||
R: ToLuaMulti<'lua>,
|
||||
F: 'static + MaybeSend + Fn(&'lua Lua, A) -> FR,
|
||||
FR: 'lua + Future<Output = Result<R>>,
|
||||
{
|
||||
// The panic should never happen as async non-static code wouldn't compile
|
||||
// Non-static lifetime must be bounded to 'lua lifetime
|
||||
mlua_panic!("asynchronous meta functions are not supported for non-static userdata")
|
||||
}
|
||||
}
|
||||
|
||||
struct NonStaticUserDataFields<'lua, T: UserData> {
|
||||
|
||||
+34
-23
@@ -1,12 +1,13 @@
|
||||
use std::cell::RefCell;
|
||||
use std::collections::HashSet;
|
||||
use std::os::raw::c_void;
|
||||
use std::rc::Rc;
|
||||
use std::string::String as StdString;
|
||||
|
||||
use rustc_hash::FxHashSet;
|
||||
use serde::de::{self, IntoDeserializer};
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
use crate::ffi;
|
||||
use crate::table::{Table, TablePairs, TableSequence};
|
||||
use crate::value::Value;
|
||||
|
||||
@@ -15,18 +16,23 @@ use crate::value::Value;
|
||||
pub struct Deserializer<'lua> {
|
||||
value: Value<'lua>,
|
||||
options: Options,
|
||||
visited: Rc<RefCell<HashSet<*const c_void>>>,
|
||||
visited: Rc<RefCell<FxHashSet<*const c_void>>>,
|
||||
}
|
||||
|
||||
/// A struct with options to change default deserializer behavior.
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
#[non_exhaustive]
|
||||
pub struct Options {
|
||||
/// If true, an attempt to serialize types such as `Thread`, `UserData`, `LightUserData`
|
||||
/// and `Error` will cause an error.
|
||||
/// If true, an attempt to serialize types such as [`Thread`], [`UserData`], [`LightUserData`]
|
||||
/// and [`Error`] will cause an error.
|
||||
/// Otherwise these types skipped when iterating or serialized as unit type.
|
||||
///
|
||||
/// Default: **true**
|
||||
///
|
||||
/// [`Thread`]: crate::Thread
|
||||
/// [`UserData`]: crate::UserData
|
||||
/// [`LightUserData`]: crate::LightUserData
|
||||
/// [`Error`]: crate::Error
|
||||
pub deny_unsupported_types: bool,
|
||||
|
||||
/// If true, an attempt to serialize a recursive table (table that refers to itself)
|
||||
@@ -39,23 +45,24 @@ pub struct Options {
|
||||
|
||||
impl Default for Options {
|
||||
fn default() -> Self {
|
||||
Options {
|
||||
deny_unsupported_types: true,
|
||||
deny_recursive_tables: true,
|
||||
}
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl Options {
|
||||
/// Returns a new instance of `Options` with default parameters.
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
pub const fn new() -> Self {
|
||||
Options {
|
||||
deny_unsupported_types: true,
|
||||
deny_recursive_tables: true,
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets [`deny_unsupported_types`] option.
|
||||
///
|
||||
/// [`deny_unsupported_types`]: #structfield.deny_unsupported_types
|
||||
pub fn deny_unsupported_types(mut self, enabled: bool) -> Self {
|
||||
#[must_use]
|
||||
pub const fn deny_unsupported_types(mut self, enabled: bool) -> Self {
|
||||
self.deny_unsupported_types = enabled;
|
||||
self
|
||||
}
|
||||
@@ -63,6 +70,7 @@ impl Options {
|
||||
/// Sets [`deny_recursive_tables`] option.
|
||||
///
|
||||
/// [`deny_recursive_tables`]: #structfield.deny_recursive_tables
|
||||
#[must_use]
|
||||
pub fn deny_recursive_tables(mut self, enabled: bool) -> Self {
|
||||
self.deny_recursive_tables = enabled;
|
||||
self
|
||||
@@ -80,14 +88,14 @@ impl<'lua> Deserializer<'lua> {
|
||||
Deserializer {
|
||||
value,
|
||||
options,
|
||||
visited: Rc::new(RefCell::new(HashSet::new())),
|
||||
visited: Rc::new(RefCell::new(FxHashSet::default())),
|
||||
}
|
||||
}
|
||||
|
||||
fn from_parts(
|
||||
value: Value<'lua>,
|
||||
options: Options,
|
||||
visited: Rc<RefCell<HashSet<*const c_void>>>,
|
||||
visited: Rc<RefCell<FxHashSet<*const c_void>>>,
|
||||
) -> Self {
|
||||
Deserializer {
|
||||
value,
|
||||
@@ -298,7 +306,7 @@ impl<'lua, 'de> serde::Deserializer<'de> for Deserializer<'lua> {
|
||||
}
|
||||
|
||||
serde::forward_to_deserialize_any! {
|
||||
bool i8 i16 i32 i64 u8 u16 u32 u64 f32 f64 char str string bytes
|
||||
bool i8 i16 i32 i64 i128 u8 u16 u32 u64 u128 f32 f64 char str string bytes
|
||||
byte_buf unit unit_struct newtype_struct
|
||||
identifier ignored_any
|
||||
}
|
||||
@@ -307,7 +315,7 @@ impl<'lua, 'de> serde::Deserializer<'de> for Deserializer<'lua> {
|
||||
struct SeqDeserializer<'lua> {
|
||||
seq: TableSequence<'lua, Value<'lua>>,
|
||||
options: Options,
|
||||
visited: Rc<RefCell<HashSet<*const c_void>>>,
|
||||
visited: Rc<RefCell<FxHashSet<*const c_void>>>,
|
||||
}
|
||||
|
||||
impl<'lua, 'de> de::SeqAccess<'de> for SeqDeserializer<'lua> {
|
||||
@@ -345,7 +353,7 @@ struct MapDeserializer<'lua> {
|
||||
pairs: TablePairs<'lua, Value<'lua>, Value<'lua>>,
|
||||
value: Option<Value<'lua>>,
|
||||
options: Options,
|
||||
visited: Rc<RefCell<HashSet<*const c_void>>>,
|
||||
visited: Rc<RefCell<FxHashSet<*const c_void>>>,
|
||||
processed: usize,
|
||||
}
|
||||
|
||||
@@ -401,7 +409,7 @@ struct EnumDeserializer<'lua> {
|
||||
variant: StdString,
|
||||
value: Option<Value<'lua>>,
|
||||
options: Options,
|
||||
visited: Rc<RefCell<HashSet<*const c_void>>>,
|
||||
visited: Rc<RefCell<FxHashSet<*const c_void>>>,
|
||||
}
|
||||
|
||||
impl<'lua, 'de> de::EnumAccess<'de> for EnumDeserializer<'lua> {
|
||||
@@ -425,7 +433,7 @@ impl<'lua, 'de> de::EnumAccess<'de> for EnumDeserializer<'lua> {
|
||||
struct VariantDeserializer<'lua> {
|
||||
value: Option<Value<'lua>>,
|
||||
options: Options,
|
||||
visited: Rc<RefCell<HashSet<*const c_void>>>,
|
||||
visited: Rc<RefCell<FxHashSet<*const c_void>>>,
|
||||
}
|
||||
|
||||
impl<'lua, 'de> de::VariantAccess<'de> for VariantDeserializer<'lua> {
|
||||
@@ -493,14 +501,16 @@ impl<'lua, 'de> de::VariantAccess<'de> for VariantDeserializer<'lua> {
|
||||
// Used to track recursive tables but allow to traverse same tables multiple times
|
||||
struct RecursionGuard {
|
||||
ptr: *const c_void,
|
||||
visited: Rc<RefCell<HashSet<*const c_void>>>,
|
||||
visited: Rc<RefCell<FxHashSet<*const c_void>>>,
|
||||
}
|
||||
|
||||
impl RecursionGuard {
|
||||
#[inline]
|
||||
fn new(table: &Table, visited: &Rc<RefCell<HashSet<*const c_void>>>) -> Self {
|
||||
fn new(table: &Table, visited: &Rc<RefCell<FxHashSet<*const c_void>>>) -> Self {
|
||||
let visited = Rc::clone(visited);
|
||||
let ptr = unsafe { table.0.lua.get_ref_ptr(&table.0) };
|
||||
let lua = table.0.lua;
|
||||
let ptr =
|
||||
unsafe { lua.ref_thread_exec(|refthr| ffi::lua_topointer(refthr, table.0.index)) };
|
||||
visited.borrow_mut().insert(ptr);
|
||||
RecursionGuard { ptr, visited }
|
||||
}
|
||||
@@ -516,12 +526,13 @@ impl Drop for RecursionGuard {
|
||||
fn check_value_if_skip(
|
||||
value: &Value,
|
||||
options: Options,
|
||||
visited: &RefCell<HashSet<*const c_void>>,
|
||||
visited: &RefCell<FxHashSet<*const c_void>>,
|
||||
) -> Result<bool> {
|
||||
match value {
|
||||
Value::Table(table) => {
|
||||
let lua = table.0.lua;
|
||||
let ptr = unsafe { lua.get_ref_ptr(&table.0) };
|
||||
let ptr =
|
||||
unsafe { lua.ref_thread_exec(|refthr| ffi::lua_topointer(refthr, table.0.index)) };
|
||||
if visited.borrow().contains(&ptr) {
|
||||
if options.deny_recursive_tables {
|
||||
return Err(de::Error::custom("recursive table detected"));
|
||||
|
||||
+8
-8
@@ -69,11 +69,11 @@ pub trait LuaSerdeExt<'lua> {
|
||||
/// ```
|
||||
fn array_metatable(&'lua self) -> Table<'lua>;
|
||||
|
||||
/// Converts `T` into a `Value` instance.
|
||||
/// Converts `T` into a [`Value`] instance.
|
||||
///
|
||||
/// Requires `feature = "serialize"`
|
||||
///
|
||||
/// [`Value`]: enum.Value.html
|
||||
/// [`Value`]: crate::Value
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
@@ -102,11 +102,11 @@ pub trait LuaSerdeExt<'lua> {
|
||||
/// ```
|
||||
fn to_value<T: Serialize + ?Sized>(&'lua self, t: &T) -> Result<Value<'lua>>;
|
||||
|
||||
/// Converts `T` into a `Value` instance with options.
|
||||
/// Converts `T` into a [`Value`] instance with options.
|
||||
///
|
||||
/// Requires `feature = "serialize"`
|
||||
///
|
||||
/// [`Value`]: enum.Value.html
|
||||
/// [`Value`]: crate::Value
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
@@ -129,11 +129,11 @@ pub trait LuaSerdeExt<'lua> {
|
||||
where
|
||||
T: Serialize + ?Sized;
|
||||
|
||||
/// Deserializes a `Value` into any serde deserializable object.
|
||||
/// Deserializes a [`Value`] into any serde deserializable object.
|
||||
///
|
||||
/// Requires `feature = "serialize"`
|
||||
///
|
||||
/// [`Value`]: enum.Value.html
|
||||
/// [`Value`]: crate::Value
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
@@ -159,11 +159,11 @@ pub trait LuaSerdeExt<'lua> {
|
||||
/// ```
|
||||
fn from_value<T: Deserialize<'lua>>(&'lua self, value: Value<'lua>) -> Result<T>;
|
||||
|
||||
/// Deserializes a `Value` into any serde deserializable object with options.
|
||||
/// Deserializes a [`Value`] into any serde deserializable object with options.
|
||||
///
|
||||
/// Requires `feature = "serialize"`
|
||||
///
|
||||
/// [`Value`]: enum.Value.html
|
||||
/// [`Value`]: crate::Value
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
|
||||
+21
-16
@@ -28,16 +28,16 @@ pub struct Options {
|
||||
///
|
||||
/// Default: **true**
|
||||
///
|
||||
/// [`array_metatable`]: ../trait.LuaSerdeExt.html#tymethod.array_metatable
|
||||
/// [`array_metatable`]: crate::LuaSerdeExt::array_metatable
|
||||
pub set_array_metatable: bool,
|
||||
|
||||
/// If true, serialize `None` (part of `Option` type) to [`null`].
|
||||
/// If true, serialize `None` (part of the `Option` type) to [`null`].
|
||||
/// Otherwise it will be set to Lua [`Nil`].
|
||||
///
|
||||
/// Default: **true**
|
||||
///
|
||||
/// [`null`]: ../trait.LuaSerdeExt.html#tymethod.null
|
||||
/// [`Nil`]: ../../enum.Value.html#variant.Nil
|
||||
/// [`null`]: crate::LuaSerdeExt::null
|
||||
/// [`Nil`]: crate::Value::Nil
|
||||
pub serialize_none_to_null: bool,
|
||||
|
||||
/// If true, serialize `Unit` (type of `()` in Rust) and Unit structs to [`null`].
|
||||
@@ -45,31 +45,32 @@ pub struct Options {
|
||||
///
|
||||
/// Default: **true**
|
||||
///
|
||||
/// [`null`]: ../trait.LuaSerdeExt.html#tymethod.null
|
||||
/// [`Nil`]: ../../enum.Value.html#variant.Nil
|
||||
/// [`null`]: crate::LuaSerdeExt::null
|
||||
/// [`Nil`]: crate::Value::Nil
|
||||
pub serialize_unit_to_null: bool,
|
||||
}
|
||||
|
||||
impl Default for Options {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl Options {
|
||||
/// Returns a new instance of [`Options`] with default parameters.
|
||||
pub const fn new() -> Self {
|
||||
Options {
|
||||
set_array_metatable: true,
|
||||
serialize_none_to_null: true,
|
||||
serialize_unit_to_null: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Options {
|
||||
/// Returns a new instance of `Options` with default parameters.
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
/// Sets [`set_array_metatable`] option.
|
||||
///
|
||||
/// [`set_array_metatable`]: #structfield.set_array_metatable
|
||||
pub fn set_array_metatable(mut self, enabled: bool) -> Self {
|
||||
#[must_use]
|
||||
pub const fn set_array_metatable(mut self, enabled: bool) -> Self {
|
||||
self.set_array_metatable = enabled;
|
||||
self
|
||||
}
|
||||
@@ -77,7 +78,8 @@ impl Options {
|
||||
/// Sets [`serialize_none_to_null`] option.
|
||||
///
|
||||
/// [`serialize_none_to_null`]: #structfield.serialize_none_to_null
|
||||
pub fn serialize_none_to_null(mut self, enabled: bool) -> Self {
|
||||
#[must_use]
|
||||
pub const fn serialize_none_to_null(mut self, enabled: bool) -> Self {
|
||||
self.serialize_none_to_null = enabled;
|
||||
self
|
||||
}
|
||||
@@ -85,7 +87,8 @@ impl Options {
|
||||
/// Sets [`serialize_unit_to_null`] option.
|
||||
///
|
||||
/// [`serialize_unit_to_null`]: #structfield.serialize_unit_to_null
|
||||
pub fn serialize_unit_to_null(mut self, enabled: bool) -> Self {
|
||||
#[must_use]
|
||||
pub const fn serialize_unit_to_null(mut self, enabled: bool) -> Self {
|
||||
self.serialize_unit_to_null = enabled;
|
||||
self
|
||||
}
|
||||
@@ -139,6 +142,8 @@ impl<'lua> ser::Serializer for Serializer<'lua> {
|
||||
lua_serialize_number!(serialize_u32, u32);
|
||||
lua_serialize_number!(serialize_i64, i64);
|
||||
lua_serialize_number!(serialize_u64, u64);
|
||||
lua_serialize_number!(serialize_i128, i128);
|
||||
lua_serialize_number!(serialize_u128, u128);
|
||||
|
||||
lua_serialize_number!(serialize_f32, f32);
|
||||
lua_serialize_number!(serialize_f64, f64);
|
||||
|
||||
+11
-11
@@ -6,32 +6,32 @@ use std::u32;
|
||||
pub struct StdLib(u32);
|
||||
|
||||
impl StdLib {
|
||||
/// [`coroutine`](https://www.lua.org/manual/5.3/manual.html#6.2) library
|
||||
/// [`coroutine`](https://www.lua.org/manual/5.4/manual.html#6.2) library
|
||||
///
|
||||
/// Requires `feature = "lua54/lua53/lua52"`
|
||||
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52", doc))]
|
||||
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
|
||||
pub const COROUTINE: StdLib = StdLib(1);
|
||||
/// [`table`](https://www.lua.org/manual/5.3/manual.html#6.6) library
|
||||
/// [`table`](https://www.lua.org/manual/5.4/manual.html#6.6) library
|
||||
pub const TABLE: StdLib = StdLib(1 << 1);
|
||||
/// [`io`](https://www.lua.org/manual/5.3/manual.html#6.8) library
|
||||
/// [`io`](https://www.lua.org/manual/5.4/manual.html#6.8) library
|
||||
pub const IO: StdLib = StdLib(1 << 2);
|
||||
/// [`os`](https://www.lua.org/manual/5.3/manual.html#6.9) library
|
||||
/// [`os`](https://www.lua.org/manual/5.4/manual.html#6.9) library
|
||||
pub const OS: StdLib = StdLib(1 << 3);
|
||||
/// [`string`](https://www.lua.org/manual/5.3/manual.html#6.4) library
|
||||
/// [`string`](https://www.lua.org/manual/5.4/manual.html#6.4) library
|
||||
pub const STRING: StdLib = StdLib(1 << 4);
|
||||
/// [`utf8`](https://www.lua.org/manual/5.3/manual.html#6.5) library
|
||||
/// [`utf8`](https://www.lua.org/manual/5.4/manual.html#6.5) library
|
||||
///
|
||||
/// Requires `feature = "lua54/lua53"`
|
||||
#[cfg(any(feature = "lua54", feature = "lua53", doc))]
|
||||
#[cfg(any(feature = "lua54", feature = "lua53"))]
|
||||
pub const UTF8: StdLib = StdLib(1 << 5);
|
||||
/// [`bit`](https://www.lua.org/manual/5.2/manual.html#6.7) library
|
||||
///
|
||||
/// Requires `feature = "lua52/luajit"`
|
||||
#[cfg(any(feature = "lua52", feature = "luajit", doc))]
|
||||
pub const BIT: StdLib = StdLib(1 << 6);
|
||||
/// [`math`](https://www.lua.org/manual/5.3/manual.html#6.7) library
|
||||
/// [`math`](https://www.lua.org/manual/5.4/manual.html#6.7) library
|
||||
pub const MATH: StdLib = StdLib(1 << 7);
|
||||
/// [`package`](https://www.lua.org/manual/5.3/manual.html#6.3) library
|
||||
/// [`package`](https://www.lua.org/manual/5.4/manual.html#6.3) library
|
||||
pub const PACKAGE: StdLib = StdLib(1 << 8);
|
||||
/// [`jit`](http://luajit.org/ext_jit.html) library
|
||||
///
|
||||
@@ -44,7 +44,7 @@ impl StdLib {
|
||||
/// Requires `feature = "luajit"`
|
||||
#[cfg(any(feature = "luajit", doc))]
|
||||
pub const FFI: StdLib = StdLib(1 << 30);
|
||||
/// (**unsafe**) [`debug`](https://www.lua.org/manual/5.3/manual.html#6.10) library
|
||||
/// (**unsafe**) [`debug`](https://www.lua.org/manual/5.4/manual.html#6.10) library
|
||||
pub const DEBUG: StdLib = StdLib(1 << 31);
|
||||
|
||||
/// No libraries
|
||||
|
||||
+82
-23
@@ -2,8 +2,9 @@ use std::marker::PhantomData;
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
use {
|
||||
serde::ser::{Serialize, SerializeMap, SerializeSeq, Serializer},
|
||||
std::result::Result as StdResult,
|
||||
rustc_hash::FxHashSet,
|
||||
serde::ser::{self, Serialize, SerializeMap, SerializeSeq, Serializer},
|
||||
std::{cell::RefCell, os::raw::c_void, result::Result as StdResult},
|
||||
};
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
@@ -378,8 +379,8 @@ impl<'lua> Table<'lua> {
|
||||
/// # }
|
||||
/// ```
|
||||
///
|
||||
/// [`Result`]: type.Result.html
|
||||
/// [Lua manual]: http://www.lua.org/manual/5.3/manual.html#pdf-next
|
||||
/// [`Result`]: crate::Result
|
||||
/// [Lua manual]: http://www.lua.org/manual/5.4/manual.html#pdf-next
|
||||
pub fn pairs<K: FromLua<'lua>, V: FromLua<'lua>>(self) -> TablePairs<'lua, K, V> {
|
||||
TablePairs {
|
||||
table: self.0,
|
||||
@@ -427,8 +428,8 @@ impl<'lua> Table<'lua> {
|
||||
/// ```
|
||||
///
|
||||
/// [`pairs`]: #method.pairs
|
||||
/// [`Result`]: type.Result.html
|
||||
/// [Lua manual]: http://www.lua.org/manual/5.3/manual.html#pdf-next
|
||||
/// [`Result`]: crate::Result
|
||||
/// [Lua manual]: http://www.lua.org/manual/5.4/manual.html#pdf-next
|
||||
pub fn sequence_values<V: FromLua<'lua>>(self) -> TableSequence<'lua, V> {
|
||||
TableSequence {
|
||||
table: self.0,
|
||||
@@ -454,7 +455,7 @@ impl<'lua> Table<'lua> {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "async", feature = "serialize"))]
|
||||
#[cfg(any(feature = "serialize"))]
|
||||
pub(crate) fn raw_sequence_values_by_len<V: FromLua<'lua>>(
|
||||
self,
|
||||
len: Option<Integer>,
|
||||
@@ -501,6 +502,25 @@ impl<'lua> AsRef<Table<'lua>> for Table<'lua> {
|
||||
|
||||
/// An extension trait for `Table`s that provides a variety of convenient functionality.
|
||||
pub trait TableExt<'lua> {
|
||||
/// Calls the table as function assuming it has `__call` metamethod.
|
||||
///
|
||||
/// The metamethod is called with the table as its first argument, followed by the passed arguments.
|
||||
fn call<A, R>(&self, args: A) -> Result<R>
|
||||
where
|
||||
A: ToLuaMulti<'lua>,
|
||||
R: FromLuaMulti<'lua>;
|
||||
|
||||
/// Asynchronously calls the table as function assuming it has `__call` metamethod.
|
||||
///
|
||||
/// The metamethod is called with the table as its first argument, followed by the passed arguments.
|
||||
#[cfg(feature = "async")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
|
||||
fn call_async<'fut, A, R>(&self, args: A) -> LocalBoxFuture<'fut, Result<R>>
|
||||
where
|
||||
'lua: 'fut,
|
||||
A: ToLuaMulti<'lua>,
|
||||
R: FromLuaMulti<'lua> + 'fut;
|
||||
|
||||
/// Gets the function associated to `key` from the table and executes it,
|
||||
/// passing the table itself along with `args` as function arguments.
|
||||
///
|
||||
@@ -563,6 +583,25 @@ pub trait TableExt<'lua> {
|
||||
}
|
||||
|
||||
impl<'lua> TableExt<'lua> for Table<'lua> {
|
||||
fn call<A, R>(&self, args: A) -> Result<R>
|
||||
where
|
||||
A: ToLuaMulti<'lua>,
|
||||
R: FromLuaMulti<'lua>,
|
||||
{
|
||||
// Convert table to a function and call via pcall that respects the `__call` metamethod.
|
||||
Function(self.0.clone()).call(args)
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
fn call_async<'fut, A, R>(&self, args: A) -> LocalBoxFuture<'fut, Result<R>>
|
||||
where
|
||||
'lua: 'fut,
|
||||
A: ToLuaMulti<'lua>,
|
||||
R: FromLuaMulti<'lua> + 'fut,
|
||||
{
|
||||
Function(self.0.clone()).call_async(args)
|
||||
}
|
||||
|
||||
fn call_method<K, A, R>(&self, key: K, args: A) -> Result<R>
|
||||
where
|
||||
K: ToLua<'lua>,
|
||||
@@ -622,22 +661,42 @@ impl<'lua> Serialize for Table<'lua> {
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
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>(None) {
|
||||
let v = v.map_err(serde::ser::Error::custom)?;
|
||||
seq.serialize_element(&v)?;
|
||||
}
|
||||
return seq.end();
|
||||
thread_local! {
|
||||
static VISITED: RefCell<FxHashSet<*const c_void>> = RefCell::new(FxHashSet::default());
|
||||
}
|
||||
|
||||
let mut map = serializer.serialize_map(None)?;
|
||||
for kv in self.clone().pairs::<Value, Value>() {
|
||||
let (k, v) = kv.map_err(serde::ser::Error::custom)?;
|
||||
map.serialize_entry(&k, &v)?;
|
||||
}
|
||||
map.end()
|
||||
let lua = self.0.lua;
|
||||
let ptr = unsafe { lua.ref_thread_exec(|refthr| ffi::lua_topointer(refthr, self.0.index)) };
|
||||
let res = VISITED.with(|visited| {
|
||||
{
|
||||
let mut visited = visited.borrow_mut();
|
||||
if visited.contains(&ptr) {
|
||||
return Err(ser::Error::custom("recursive table detected"));
|
||||
}
|
||||
visited.insert(ptr);
|
||||
}
|
||||
|
||||
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>(None) {
|
||||
let v = v.map_err(serde::ser::Error::custom)?;
|
||||
seq.serialize_element(&v)?;
|
||||
}
|
||||
return seq.end();
|
||||
}
|
||||
|
||||
let mut map = serializer.serialize_map(None)?;
|
||||
for kv in self.clone().pairs::<Value, Value>() {
|
||||
let (k, v) = kv.map_err(serde::ser::Error::custom)?;
|
||||
map.serialize_entry(&k, &v)?;
|
||||
}
|
||||
map.end()
|
||||
});
|
||||
VISITED.with(|visited| {
|
||||
visited.borrow_mut().remove(&ptr);
|
||||
});
|
||||
res
|
||||
}
|
||||
}
|
||||
|
||||
@@ -645,7 +704,7 @@ impl<'lua> Serialize for Table<'lua> {
|
||||
///
|
||||
/// This struct is created by the [`Table::pairs`] method.
|
||||
///
|
||||
/// [`Table::pairs`]: struct.Table.html#method.pairs
|
||||
/// [`Table::pairs`]: crate::Table::pairs
|
||||
pub struct TablePairs<'lua, K, V> {
|
||||
table: LuaRef<'lua>,
|
||||
key: Option<Value<'lua>>,
|
||||
@@ -704,7 +763,7 @@ where
|
||||
///
|
||||
/// This struct is created by the [`Table::sequence_values`] method.
|
||||
///
|
||||
/// [`Table::sequence_values`]: struct.Table.html#method.sequence_values
|
||||
/// [`Table::sequence_values`]: crate::Table::sequence_values
|
||||
pub struct TableSequence<'lua, V> {
|
||||
table: LuaRef<'lua>,
|
||||
index: Option<Integer>,
|
||||
|
||||
+57
-50
@@ -4,24 +4,22 @@ use std::os::raw::c_int;
|
||||
use crate::error::{Error, Result};
|
||||
use crate::ffi;
|
||||
use crate::types::LuaRef;
|
||||
use crate::util::{assert_stack, check_stack, error_traceback, pop_error, StackGuard};
|
||||
use crate::value::{FromLuaMulti, MultiValue, ToLuaMulti};
|
||||
use crate::util::{check_stack, error_traceback, pop_error, StackGuard};
|
||||
use crate::value::{FromLuaMulti, ToLuaMulti};
|
||||
|
||||
#[cfg(any(feature = "lua54", all(feature = "luajit", feature = "vendored"), doc))]
|
||||
#[cfg(any(feature = "lua54", all(feature = "luajit", feature = "vendored")))]
|
||||
use crate::function::Function;
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
use {
|
||||
crate::{
|
||||
lua::{ASYNC_POLL_PENDING, WAKER_REGISTRY_KEY},
|
||||
util::get_gc_userdata,
|
||||
value::Value,
|
||||
lua::{Lua, ASYNC_POLL_PENDING},
|
||||
value::{MultiValue, Value},
|
||||
},
|
||||
futures_core::{future::Future, stream::Stream},
|
||||
std::{
|
||||
cell::RefCell,
|
||||
marker::PhantomData,
|
||||
mem,
|
||||
os::raw::c_void,
|
||||
pin::Pin,
|
||||
task::{Context, Poll, Waker},
|
||||
@@ -35,7 +33,7 @@ pub enum ThreadStatus {
|
||||
///
|
||||
/// If a thread is in this state, it can be resumed by calling [`Thread::resume`].
|
||||
///
|
||||
/// [`Thread::resume`]: struct.Thread.html#method.resume
|
||||
/// [`Thread::resume`]: crate::Thread::resume
|
||||
Resumable,
|
||||
/// Either the thread has finished executing, or the thread is currently running.
|
||||
Unresumable,
|
||||
@@ -51,8 +49,8 @@ pub struct Thread<'lua>(pub(crate) LuaRef<'lua>);
|
||||
///
|
||||
/// Requires `feature = "async"`
|
||||
///
|
||||
/// [`Future`]: ../futures_core/future/trait.Future.html
|
||||
/// [`Stream`]: ../futures_core/stream/trait.Stream.html
|
||||
/// [`Future`]: futures_core::future::Future
|
||||
/// [`Stream`]: futures_core::stream::Stream
|
||||
#[cfg(feature = "async")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
|
||||
#[derive(Debug)]
|
||||
@@ -60,6 +58,7 @@ pub struct AsyncThread<'lua, R> {
|
||||
thread: Thread<'lua>,
|
||||
args0: RefCell<Option<Result<MultiValue<'lua>>>>,
|
||||
ret: PhantomData<R>,
|
||||
recycle: bool,
|
||||
}
|
||||
|
||||
impl<'lua> Thread<'lua> {
|
||||
@@ -110,15 +109,14 @@ impl<'lua> Thread<'lua> {
|
||||
R: FromLuaMulti<'lua>,
|
||||
{
|
||||
let lua = self.0.lua;
|
||||
let args = args.to_lua_multi(lua)?;
|
||||
let mut args = args.to_lua_multi(lua)?;
|
||||
let nargs = args.len() as c_int;
|
||||
let results = unsafe {
|
||||
let _sg = StackGuard::new(lua.state);
|
||||
check_stack(lua.state, cmp::min(nargs + 1, 3))?;
|
||||
check_stack(lua.state, cmp::max(nargs + 1, 3))?;
|
||||
|
||||
lua.push_ref(&self.0);
|
||||
let thread_state = ffi::lua_tothread(lua.state, -1);
|
||||
ffi::lua_pop(lua.state, 1);
|
||||
let thread_state =
|
||||
lua.ref_thread_exec(|ref_thread| ffi::lua_tothread(ref_thread, self.0.index));
|
||||
|
||||
let status = ffi::lua_status(thread_state);
|
||||
if status != ffi::LUA_YIELD && ffi::lua_gettop(thread_state) == 0 {
|
||||
@@ -126,7 +124,7 @@ impl<'lua> Thread<'lua> {
|
||||
}
|
||||
|
||||
check_stack(thread_state, nargs)?;
|
||||
for arg in args {
|
||||
for arg in args.drain_all() {
|
||||
lua.push_value(arg)?;
|
||||
}
|
||||
ffi::lua_xmove(lua.state, thread_state, nargs);
|
||||
@@ -139,7 +137,7 @@ impl<'lua> Thread<'lua> {
|
||||
return Err(pop_error(thread_state, ret));
|
||||
}
|
||||
|
||||
let mut results = MultiValue::new();
|
||||
let mut results = args; // Reuse MultiValue container
|
||||
check_stack(lua.state, nresults + 2)?; // 2 is extra for `lua.pop_value()` below
|
||||
ffi::lua_xmove(thread_state, lua.state, nresults);
|
||||
|
||||
@@ -155,12 +153,8 @@ impl<'lua> Thread<'lua> {
|
||||
pub fn status(&self) -> ThreadStatus {
|
||||
let lua = self.0.lua;
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(lua.state);
|
||||
assert_stack(lua.state, 1);
|
||||
|
||||
lua.push_ref(&self.0);
|
||||
let thread_state = ffi::lua_tothread(lua.state, -1);
|
||||
ffi::lua_pop(lua.state, 1);
|
||||
let thread_state =
|
||||
lua.ref_thread_exec(|ref_thread| ffi::lua_tothread(ref_thread, self.0.index));
|
||||
|
||||
let status = ffi::lua_status(thread_state);
|
||||
if status != ffi::LUA_OK && status != ffi::LUA_YIELD {
|
||||
@@ -188,7 +182,7 @@ impl<'lua> Thread<'lua> {
|
||||
///
|
||||
/// [Lua 5.4]: https://www.lua.org/manual/5.4/manual.html#lua_resetthread
|
||||
/// [LuaJIT]: https://github.com/openresty/luajit2#lua_resetthread
|
||||
#[cfg(any(feature = "lua54", all(feature = "luajit", feature = "vendored"), doc))]
|
||||
#[cfg(any(feature = "lua54", all(feature = "luajit", feature = "vendored")))]
|
||||
pub fn reset(&self, func: Function<'lua>) -> Result<()> {
|
||||
let lua = self.0.lua;
|
||||
unsafe {
|
||||
@@ -210,10 +204,10 @@ impl<'lua> Thread<'lua> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Converts Thread to an AsyncThread which implements Future and Stream traits.
|
||||
/// Converts Thread to an AsyncThread which implements [`Future`] and [`Stream`] traits.
|
||||
///
|
||||
/// `args` are passed as arguments to the thread function for first call.
|
||||
/// The object call `resume()` while polling and also allows to run rust futures
|
||||
/// The object calls [`resume()`] while polling and also allows to run rust futures
|
||||
/// to completion using an executor.
|
||||
///
|
||||
/// Using AsyncThread as a Stream allows to iterate through `coroutine.yield()`
|
||||
@@ -222,6 +216,10 @@ impl<'lua> Thread<'lua> {
|
||||
///
|
||||
/// Requires `feature = "async"`
|
||||
///
|
||||
/// [`Future`]: futures_core::future::Future
|
||||
/// [`Stream`]: futures_core::stream::Stream
|
||||
/// [`resume()`]: https://www.lua.org/manual/5.4/manual.html#lua_resume
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
@@ -263,6 +261,7 @@ impl<'lua> Thread<'lua> {
|
||||
thread: self,
|
||||
args0: RefCell::new(Some(args)),
|
||||
ret: PhantomData,
|
||||
recycle: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -273,6 +272,24 @@ impl<'lua> PartialEq for Thread<'lua> {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
impl<'lua, R> AsyncThread<'lua, R> {
|
||||
#[inline]
|
||||
pub(crate) fn set_recyclable(&mut self, recyclable: bool) {
|
||||
self.recycle = recyclable;
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
#[cfg(any(feature = "lua54", all(feature = "luajit", feature = "vendored")))]
|
||||
impl<'lua, R> Drop for AsyncThread<'lua, R> {
|
||||
fn drop(&mut self) {
|
||||
if self.recycle {
|
||||
self.thread.0.lua.recycle_thread(&mut self.thread);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
impl<'lua, R> Stream for AsyncThread<'lua, R>
|
||||
where
|
||||
@@ -288,7 +305,7 @@ where
|
||||
_ => return Poll::Ready(None),
|
||||
};
|
||||
|
||||
let _wg = WakerGuard::new(lua.state, cx.waker().clone());
|
||||
let _wg = WakerGuard::new(lua, cx.waker().clone());
|
||||
let ret: MultiValue = if let Some(args) = self.args0.borrow_mut().take() {
|
||||
self.thread.resume(args?)?
|
||||
} else {
|
||||
@@ -319,7 +336,7 @@ where
|
||||
_ => return Poll::Ready(Err(Error::CoroutineInactive)),
|
||||
};
|
||||
|
||||
let _wg = WakerGuard::new(lua.state, cx.waker().clone());
|
||||
let _wg = WakerGuard::new(lua, cx.waker().clone());
|
||||
let ret: MultiValue = if let Some(args) = self.args0.borrow_mut().take() {
|
||||
self.thread.resume(args?)?
|
||||
} else {
|
||||
@@ -344,7 +361,7 @@ where
|
||||
#[inline(always)]
|
||||
fn is_poll_pending(val: &MultiValue) -> bool {
|
||||
match val.iter().enumerate().last() {
|
||||
Some((1, Value::LightUserData(ud))) => {
|
||||
Some((0, Value::LightUserData(ud))) => {
|
||||
ud.0 == &ASYNC_POLL_PENDING as *const u8 as *mut c_void
|
||||
}
|
||||
_ => false,
|
||||
@@ -352,37 +369,27 @@ fn is_poll_pending(val: &MultiValue) -> bool {
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
struct WakerGuard(*mut ffi::lua_State, Option<Waker>);
|
||||
struct WakerGuard<'lua> {
|
||||
lua: &'lua Lua,
|
||||
prev: Option<Waker>,
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
impl WakerGuard {
|
||||
pub fn new(state: *mut ffi::lua_State, waker: Waker) -> Result<WakerGuard> {
|
||||
impl<'lua> WakerGuard<'lua> {
|
||||
#[inline]
|
||||
pub fn new(lua: &Lua, waker: Waker) -> Result<WakerGuard> {
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(state);
|
||||
check_stack(state, 3)?;
|
||||
|
||||
let waker_key = &WAKER_REGISTRY_KEY as *const u8 as *const c_void;
|
||||
ffi::lua_rawgetp(state, ffi::LUA_REGISTRYINDEX, waker_key);
|
||||
let waker_slot = get_gc_userdata::<Option<Waker>>(state, -1).as_mut();
|
||||
let old = mlua_expect!(waker_slot, "Waker is destroyed").replace(waker);
|
||||
|
||||
Ok(WakerGuard(state, old))
|
||||
let prev = lua.set_waker(Some(waker));
|
||||
Ok(WakerGuard { lua, prev })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
impl Drop for WakerGuard {
|
||||
impl<'lua> Drop for WakerGuard<'lua> {
|
||||
fn drop(&mut self) {
|
||||
let state = self.0;
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(state);
|
||||
assert_stack(state, 3);
|
||||
|
||||
let waker_key = &WAKER_REGISTRY_KEY as *const u8 as *const c_void;
|
||||
ffi::lua_rawgetp(state, ffi::LUA_REGISTRYINDEX, waker_key);
|
||||
let waker_slot = get_gc_userdata::<Option<Waker>>(state, -1).as_mut();
|
||||
mem::swap(mlua_expect!(waker_slot, "Waker is destroyed"), &mut self.1);
|
||||
self.lua.set_waker(self.prev.take());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+23
-11
@@ -1,9 +1,11 @@
|
||||
use std::cell::RefCell;
|
||||
use std::hash::{Hash, Hasher};
|
||||
use std::os::raw::{c_int, c_void};
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::{fmt, mem, ptr};
|
||||
|
||||
#[cfg(feature = "lua54")]
|
||||
use std::ffi::CStr;
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
use futures_core::future::LocalBoxFuture;
|
||||
|
||||
@@ -48,10 +50,16 @@ pub(crate) struct AsyncPollUpvalue<'lua> {
|
||||
}
|
||||
|
||||
#[cfg(feature = "send")]
|
||||
pub(crate) type HookCallback = Arc<RefCell<dyn FnMut(&Lua, Debug) -> Result<()> + Send>>;
|
||||
pub(crate) type HookCallback = Arc<Mutex<dyn FnMut(&Lua, Debug) -> Result<()> + Send>>;
|
||||
|
||||
#[cfg(not(feature = "send"))]
|
||||
pub(crate) type HookCallback = Arc<RefCell<dyn FnMut(&Lua, Debug) -> Result<()>>>;
|
||||
pub(crate) type HookCallback = Arc<Mutex<dyn FnMut(&Lua, Debug) -> Result<()>>>;
|
||||
|
||||
#[cfg(all(feature = "send", feature = "lua54"))]
|
||||
pub(crate) type WarnCallback = Box<dyn Fn(&Lua, &CStr, bool) -> Result<()> + Send>;
|
||||
|
||||
#[cfg(all(not(feature = "send"), feature = "lua54"))]
|
||||
pub(crate) type WarnCallback = Box<dyn Fn(&Lua, &CStr, bool) -> Result<()>>;
|
||||
|
||||
#[cfg(feature = "send")]
|
||||
pub trait MaybeSend: Send {}
|
||||
@@ -71,15 +79,17 @@ pub(crate) struct DestructedUserdataMT;
|
||||
/// garbage collected on Drop, but it can be removed with [`Lua::remove_registry_value`],
|
||||
/// and instances not manually removed can be garbage collected with [`Lua::expire_registry_values`].
|
||||
///
|
||||
/// Be warned, If you place this into Lua via a `UserData` type or a rust callback, it is *very
|
||||
/// Be warned, If you place this into Lua via a [`UserData`] type or a rust callback, it is *very
|
||||
/// easy* to accidentally cause reference cycles that the Lua garbage collector cannot resolve.
|
||||
/// Instead of placing a `RegistryKey` into a `UserData` type, prefer instead to use
|
||||
/// [`UserData::set_user_value`] / [`UserData::get_user_value`].
|
||||
/// Instead of placing a [`RegistryKey`] into a [`UserData`] type, prefer instead to use
|
||||
/// [`AnyUserData::set_user_value`] / [`AnyUserData::get_user_value`].
|
||||
///
|
||||
/// [`Lua::remove_registry_value`]: struct.Lua.html#method.remove_registry_value
|
||||
/// [`Lua::expire_registry_values`]: struct.Lua.html#method.expire_registry_values
|
||||
/// [`UserData::set_user_value`]: struct.UserData.html#method.set_user_value
|
||||
/// [`UserData::get_user_value`]: struct.UserData.html#method.get_user_value
|
||||
/// [`UserData`]: crate::UserData
|
||||
/// [`RegistryKey`]: crate::RegistryKey
|
||||
/// [`Lua::remove_registry_value`]: crate::Lua::remove_registry_value
|
||||
/// [`Lua::expire_registry_values`]: crate::Lua::expire_registry_values
|
||||
/// [`AnyUserData::set_user_value`]: crate::AnyUserData::set_user_value
|
||||
/// [`AnyUserData::get_user_value`]: crate::AnyUserData::get_user_value
|
||||
pub struct RegistryKey {
|
||||
pub(crate) registry_id: c_int,
|
||||
pub(crate) unref_list: Arc<Mutex<Option<Vec<c_int>>>>,
|
||||
@@ -145,7 +155,9 @@ impl<'lua> Clone for LuaRef<'lua> {
|
||||
|
||||
impl<'lua> Drop for LuaRef<'lua> {
|
||||
fn drop(&mut self) {
|
||||
self.lua.drop_ref(self)
|
||||
if self.index > 0 {
|
||||
self.lua.drop_ref(self);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+349
-92
@@ -3,6 +3,7 @@ use std::cell::{Ref, RefCell, RefMut};
|
||||
use std::fmt;
|
||||
use std::hash::{Hash, Hasher};
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use std::os::raw::{c_char, c_int};
|
||||
use std::string::String as StdString;
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
@@ -11,7 +12,6 @@ use std::future::Future;
|
||||
#[cfg(feature = "serialize")]
|
||||
use {
|
||||
serde::ser::{self, Serialize, Serializer},
|
||||
std::os::raw::c_void,
|
||||
std::result::Result as StdResult,
|
||||
};
|
||||
|
||||
@@ -21,21 +21,21 @@ use crate::function::Function;
|
||||
use crate::lua::Lua;
|
||||
use crate::table::{Table, TablePairs};
|
||||
use crate::types::{Callback, LuaRef, MaybeSend};
|
||||
use crate::util::{check_stack, get_userdata, StackGuard};
|
||||
use crate::util::{check_stack, get_userdata, take_userdata, StackGuard};
|
||||
use crate::value::{FromLua, FromLuaMulti, ToLua, ToLuaMulti};
|
||||
|
||||
#[cfg(any(feature = "lua52", feature = "lua51", feature = "luajit"))]
|
||||
use crate::value::Value;
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
use crate::types::AsyncCallback;
|
||||
|
||||
#[cfg(feature = "lua54")]
|
||||
pub(crate) const USER_VALUE_MAXSLOT: usize = 8;
|
||||
|
||||
/// Kinds of metamethods that can be overridden.
|
||||
///
|
||||
/// Currently, this mechanism does not allow overriding the `__gc` metamethod, since there is
|
||||
/// generally no need to do so: [`UserData`] implementors can instead just implement `Drop`.
|
||||
///
|
||||
/// [`UserData`]: trait.UserData.html
|
||||
/// [`UserData`]: crate::UserData
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum MetaMethod {
|
||||
/// The `+` operator.
|
||||
@@ -54,29 +54,29 @@ pub enum MetaMethod {
|
||||
Unm,
|
||||
/// The floor division (//) operator.
|
||||
/// Requires `feature = "lua54/lua53"`
|
||||
#[cfg(any(feature = "lua54", feature = "lua53", doc))]
|
||||
#[cfg(any(feature = "lua54", feature = "lua53"))]
|
||||
IDiv,
|
||||
/// The bitwise AND (&) operator.
|
||||
/// Requires `feature = "lua54/lua53"`
|
||||
#[cfg(any(feature = "lua54", feature = "lua53", doc))]
|
||||
#[cfg(any(feature = "lua54", feature = "lua53"))]
|
||||
BAnd,
|
||||
/// The bitwise OR (|) operator.
|
||||
/// Requires `feature = "lua54/lua53"`
|
||||
#[cfg(any(feature = "lua54", feature = "lua53", doc))]
|
||||
#[cfg(any(feature = "lua54", feature = "lua53"))]
|
||||
BOr,
|
||||
/// The bitwise XOR (binary ~) operator.
|
||||
/// Requires `feature = "lua54/lua53"`
|
||||
#[cfg(any(feature = "lua54", feature = "lua53", doc))]
|
||||
#[cfg(any(feature = "lua54", feature = "lua53"))]
|
||||
BXor,
|
||||
/// The bitwise NOT (unary ~) operator.
|
||||
/// Requires `feature = "lua54/lua53"`
|
||||
#[cfg(any(feature = "lua54", feature = "lua53", doc))]
|
||||
#[cfg(any(feature = "lua54", feature = "lua53"))]
|
||||
BNot,
|
||||
/// The bitwise left shift (<<) operator.
|
||||
#[cfg(any(feature = "lua54", feature = "lua53", doc))]
|
||||
#[cfg(any(feature = "lua54", feature = "lua53"))]
|
||||
Shl,
|
||||
/// The bitwise right shift (>>) operator.
|
||||
#[cfg(any(feature = "lua54", feature = "lua53", doc))]
|
||||
#[cfg(any(feature = "lua54", feature = "lua53"))]
|
||||
Shr,
|
||||
/// The string concatenation operator `..`.
|
||||
Concat,
|
||||
@@ -103,7 +103,12 @@ pub enum MetaMethod {
|
||||
/// This is not an operator, but it will be called by the built-in `pairs` function.
|
||||
///
|
||||
/// Requires `feature = "lua54/lua53/lua52"`
|
||||
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52", doc))]
|
||||
#[cfg(any(
|
||||
feature = "lua54",
|
||||
feature = "lua53",
|
||||
feature = "lua52",
|
||||
feature = "luajit52",
|
||||
))]
|
||||
Pairs,
|
||||
/// The `__ipairs` metamethod.
|
||||
///
|
||||
@@ -112,7 +117,7 @@ pub enum MetaMethod {
|
||||
/// Requires `feature = "lua52"`
|
||||
///
|
||||
/// [`ipairs`]: https://www.lua.org/manual/5.2/manual.html#pdf-ipairs
|
||||
#[cfg(any(feature = "lua52", doc))]
|
||||
#[cfg(any(feature = "lua52", feature = "luajit52", doc))]
|
||||
IPairs,
|
||||
/// The `__close` metamethod.
|
||||
///
|
||||
@@ -124,7 +129,7 @@ pub enum MetaMethod {
|
||||
/// Requires `feature = "lua54"`
|
||||
///
|
||||
/// [lua_doc]: https://www.lua.org/manual/5.4/manual.html#3.3.8
|
||||
#[cfg(any(feature = "lua54", doc))]
|
||||
#[cfg(any(feature = "lua54"))]
|
||||
Close,
|
||||
/// A custom metamethod.
|
||||
///
|
||||
@@ -189,9 +194,14 @@ impl MetaMethod {
|
||||
MetaMethod::Call => "__call",
|
||||
MetaMethod::ToString => "__tostring",
|
||||
|
||||
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
|
||||
#[cfg(any(
|
||||
feature = "lua54",
|
||||
feature = "lua53",
|
||||
feature = "lua52",
|
||||
feature = "luajit52"
|
||||
))]
|
||||
MetaMethod::Pairs => "__pairs",
|
||||
#[cfg(feature = "lua52")]
|
||||
#[cfg(any(feature = "lua52", feature = "luajit52"))]
|
||||
MetaMethod::IPairs => "__ipairs",
|
||||
|
||||
#[cfg(feature = "lua54")]
|
||||
@@ -251,9 +261,14 @@ impl From<StdString> for MetaMethod {
|
||||
"__call" => MetaMethod::Call,
|
||||
"__tostring" => MetaMethod::ToString,
|
||||
|
||||
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
|
||||
#[cfg(any(
|
||||
feature = "lua54",
|
||||
feature = "lua53",
|
||||
feature = "lua52",
|
||||
feature = "luajit52"
|
||||
))]
|
||||
"__pairs" => MetaMethod::Pairs,
|
||||
#[cfg(feature = "lua52")]
|
||||
#[cfg(any(feature = "lua52", feature = "luajit52"))]
|
||||
"__ipairs" => MetaMethod::IPairs,
|
||||
|
||||
#[cfg(feature = "lua54")]
|
||||
@@ -272,7 +287,7 @@ impl From<&str> for MetaMethod {
|
||||
|
||||
/// Method registry for [`UserData`] implementors.
|
||||
///
|
||||
/// [`UserData`]: trait.UserData.html
|
||||
/// [`UserData`]: crate::UserData
|
||||
pub trait UserDataMethods<'lua, T: UserData> {
|
||||
/// Add a regular method which accepts a `&T` as the first parameter.
|
||||
///
|
||||
@@ -326,7 +341,7 @@ pub trait UserDataMethods<'lua, T: UserData> {
|
||||
///
|
||||
/// Prefer to use [`add_method`] or [`add_method_mut`] as they are easier to use.
|
||||
///
|
||||
/// [`AnyUserData`]: struct.AnyUserData.html
|
||||
/// [`AnyUserData`]: crate::AnyUserData
|
||||
/// [`add_method`]: #method.add_method
|
||||
/// [`add_method_mut`]: #method.add_method_mut
|
||||
fn add_function<S, A, R, F>(&mut self, name: &S, function: F)
|
||||
@@ -396,6 +411,25 @@ pub trait UserDataMethods<'lua, T: UserData> {
|
||||
R: ToLuaMulti<'lua>,
|
||||
M: 'static + MaybeSend + FnMut(&'lua Lua, &mut T, A) -> Result<R>;
|
||||
|
||||
/// Add an async metamethod which accepts a `T` as the first parameter and returns Future.
|
||||
/// The passed `T` is cloned from the original value.
|
||||
///
|
||||
/// This is an async version of [`add_meta_method`].
|
||||
///
|
||||
/// Requires `feature = "async"`
|
||||
///
|
||||
/// [`add_meta_method`]: #method.add_meta_method
|
||||
#[cfg(all(feature = "async", not(feature = "lua51")))]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
|
||||
fn add_async_meta_method<S, A, R, M, MR>(&mut self, name: S, method: M)
|
||||
where
|
||||
T: Clone,
|
||||
S: Into<MetaMethod>,
|
||||
A: FromLuaMulti<'lua>,
|
||||
R: ToLuaMulti<'lua>,
|
||||
M: 'static + MaybeSend + Fn(&'lua Lua, T, A) -> MR,
|
||||
MR: 'lua + Future<Output = Result<R>>;
|
||||
|
||||
/// Add a metamethod which accepts generic arguments.
|
||||
///
|
||||
/// Metamethods for binary operators can be triggered if either the left or right argument to
|
||||
@@ -420,6 +454,23 @@ pub trait UserDataMethods<'lua, T: UserData> {
|
||||
R: ToLuaMulti<'lua>,
|
||||
F: 'static + MaybeSend + FnMut(&'lua Lua, A) -> Result<R>;
|
||||
|
||||
/// Add a metamethod which accepts generic arguments and returns Future.
|
||||
///
|
||||
/// This is an async version of [`add_meta_function`].
|
||||
///
|
||||
/// Requires `feature = "async"`
|
||||
///
|
||||
/// [`add_meta_function`]: #method.add_meta_function
|
||||
#[cfg(all(feature = "async", not(feature = "lua51")))]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
|
||||
fn add_async_meta_function<S, A, R, F, FR>(&mut self, name: S, function: F)
|
||||
where
|
||||
S: Into<MetaMethod>,
|
||||
A: FromLuaMulti<'lua>,
|
||||
R: ToLuaMulti<'lua>,
|
||||
F: 'static + MaybeSend + Fn(&'lua Lua, A) -> FR,
|
||||
FR: 'lua + Future<Output = Result<R>>;
|
||||
|
||||
//
|
||||
// Below are internal methods used in generated code
|
||||
//
|
||||
@@ -433,11 +484,20 @@ pub trait UserDataMethods<'lua, T: UserData> {
|
||||
|
||||
#[doc(hidden)]
|
||||
fn add_meta_callback(&mut self, _meta: MetaMethod, _callback: Callback<'lua, 'static>) {}
|
||||
|
||||
#[doc(hidden)]
|
||||
#[cfg(feature = "async")]
|
||||
fn add_async_meta_callback(
|
||||
&mut self,
|
||||
_meta: MetaMethod,
|
||||
_callback: AsyncCallback<'lua, 'static>,
|
||||
) {
|
||||
}
|
||||
}
|
||||
|
||||
/// Field registry for [`UserData`] implementors.
|
||||
///
|
||||
/// [`UserData`]: trait.UserData.html
|
||||
/// [`UserData`]: crate::UserData
|
||||
pub trait UserDataFields<'lua, T: UserData> {
|
||||
/// Add a regular field getter as a method which accepts a `&T` as the parameter.
|
||||
///
|
||||
@@ -470,7 +530,7 @@ pub trait UserDataFields<'lua, T: UserData> {
|
||||
///
|
||||
/// Prefer to use [`add_field_method_get`] as it is easier to use.
|
||||
///
|
||||
/// [`AnyUserData`]: struct.AnyUserData.html
|
||||
/// [`AnyUserData`]: crate::AnyUserData
|
||||
/// [`add_field_method_get`]: #method.add_field_method_get
|
||||
fn add_field_function_get<S, R, F>(&mut self, name: &S, function: F)
|
||||
where
|
||||
@@ -483,7 +543,7 @@ pub trait UserDataFields<'lua, T: UserData> {
|
||||
///
|
||||
/// Prefer to use [`add_field_method_set`] as it is easier to use.
|
||||
///
|
||||
/// [`AnyUserData`]: struct.AnyUserData.html
|
||||
/// [`AnyUserData`]: crate::AnyUserData
|
||||
/// [`add_field_method_set`]: #method.add_field_method_set
|
||||
fn add_field_function_set<S, A, F>(&mut self, name: &S, function: F)
|
||||
where
|
||||
@@ -579,10 +639,10 @@ pub trait UserDataFields<'lua, T: UserData> {
|
||||
/// # }
|
||||
/// ```
|
||||
///
|
||||
/// [`ToLua`]: trait.ToLua.html
|
||||
/// [`FromLua`]: trait.FromLua.html
|
||||
/// [`UserDataFields`]: trait.UserDataFields.html
|
||||
/// [`UserDataMethods`]: trait.UserDataMethods.html
|
||||
/// [`ToLua`]: crate::ToLua
|
||||
/// [`FromLua`]: crate::FromLua
|
||||
/// [`UserDataFields`]: crate::UserDataFields
|
||||
/// [`UserDataMethods`]: crate::UserDataMethods
|
||||
pub trait UserData: Sized {
|
||||
/// Adds custom fields specific to this userdata.
|
||||
fn add_fields<'lua, F: UserDataFields<'lua, Self>>(_fields: &mut F) {}
|
||||
@@ -626,18 +686,24 @@ impl<T> UserDataCell<T> {
|
||||
.map(|r| RefMut::map(r, |r| r.deref_mut()))
|
||||
.map_err(|_| Error::UserDataBorrowMutError)
|
||||
}
|
||||
|
||||
// Consumes this `UserDataCell`, returning the wrapped value.
|
||||
#[inline]
|
||||
fn into_inner(self) -> T {
|
||||
self.0.into_inner().into_inner()
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) enum UserDataWrapped<T> {
|
||||
Default(T),
|
||||
Default(Box<T>),
|
||||
#[cfg(feature = "serialize")]
|
||||
Serializable(*mut T, *const dyn erased_serde::Serialize),
|
||||
Serializable(Box<dyn erased_serde::Serialize>),
|
||||
}
|
||||
|
||||
impl<T> UserDataWrapped<T> {
|
||||
#[inline]
|
||||
fn new(data: T) -> Self {
|
||||
UserDataWrapped::Default(data)
|
||||
UserDataWrapped::Default(Box::new(data))
|
||||
}
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
@@ -646,16 +712,15 @@ impl<T> UserDataWrapped<T> {
|
||||
where
|
||||
T: 'static + Serialize,
|
||||
{
|
||||
let data_raw = Box::into_raw(Box::new(data));
|
||||
UserDataWrapped::Serializable(data_raw, data_raw)
|
||||
UserDataWrapped::Serializable(Box::new(data))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
impl<T> Drop for UserDataWrapped<T> {
|
||||
fn drop(&mut self) {
|
||||
if let UserDataWrapped::Serializable(data, _) = *self {
|
||||
drop(unsafe { Box::from_raw(data) });
|
||||
#[inline]
|
||||
fn into_inner(self) -> T {
|
||||
match self {
|
||||
Self::Default(data) => *data,
|
||||
#[cfg(feature = "serialize")]
|
||||
Self::Serializable(data) => unsafe { *Box::from_raw(Box::into_raw(data) as *mut T) },
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -668,7 +733,9 @@ impl<T> Deref for UserDataWrapped<T> {
|
||||
match self {
|
||||
Self::Default(data) => data,
|
||||
#[cfg(feature = "serialize")]
|
||||
Self::Serializable(data, _) => unsafe { &**data },
|
||||
Self::Serializable(data) => unsafe {
|
||||
&*(data.as_ref() as *const _ as *const Self::Target)
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -679,7 +746,9 @@ impl<T> DerefMut for UserDataWrapped<T> {
|
||||
match self {
|
||||
Self::Default(data) => data,
|
||||
#[cfg(feature = "serialize")]
|
||||
Self::Serializable(data, _) => unsafe { &mut **data },
|
||||
Self::Serializable(data) => unsafe {
|
||||
&mut *(data.as_mut() as *mut _ as *mut Self::Target)
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -710,9 +779,9 @@ impl Serialize for UserDataSerializeError {
|
||||
/// This API should only be used when necessary. Implementing [`UserData`] already allows defining
|
||||
/// methods which check the type and acquire a borrow behind the scenes.
|
||||
///
|
||||
/// [`UserData`]: trait.UserData.html
|
||||
/// [`is`]: #method.is
|
||||
/// [`borrow`]: #method.borrow
|
||||
/// [`UserData`]: crate::UserData
|
||||
/// [`is`]: crate::AnyUserData::is
|
||||
/// [`borrow`]: crate::AnyUserData::borrow
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct AnyUserData<'lua>(pub(crate) LuaRef<'lua>);
|
||||
|
||||
@@ -748,57 +817,239 @@ impl<'lua> AnyUserData<'lua> {
|
||||
self.inspect(|cell| cell.try_borrow_mut())
|
||||
}
|
||||
|
||||
/// Sets an associated value to this `AnyUserData`.
|
||||
/// Takes out the value of `UserData` and sets the special "destructed" metatable that prevents
|
||||
/// any further operations with this userdata.
|
||||
///
|
||||
/// The value may be any Lua value whatsoever, and can be retrieved with [`get_user_value`].
|
||||
/// As Lua < 5.3 allows to store only tables, the value will be stored in a table at index 1.
|
||||
///
|
||||
/// [`get_user_value`]: #method.get_user_value
|
||||
pub fn set_user_value<V: ToLua<'lua>>(&self, v: V) -> Result<()> {
|
||||
/// All associated user values will be also cleared.
|
||||
pub fn take<T: 'static + UserData>(&self) -> Result<T> {
|
||||
let lua = self.0.lua;
|
||||
#[cfg(any(feature = "lua52", feature = "lua51", feature = "luajit"))]
|
||||
let v = {
|
||||
// Lua <= 5.2 allows to store only a table. Then we will wrap the value.
|
||||
let t = lua.create_table_with_capacity(1, 0)?;
|
||||
t.raw_set(1, v)?;
|
||||
Value::Table(t)
|
||||
};
|
||||
#[cfg(any(feature = "lua54", feature = "lua53"))]
|
||||
let v = v.to_lua(lua)?;
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(lua.state);
|
||||
check_stack(lua.state, 3)?;
|
||||
|
||||
let type_id = lua.push_userdata_ref(&self.0)?;
|
||||
match type_id {
|
||||
Some(type_id) if type_id == TypeId::of::<T>() => {
|
||||
// Try to borrow userdata exclusively
|
||||
let _ = (*get_userdata::<UserDataCell<T>>(lua.state, -1)).try_borrow_mut()?;
|
||||
|
||||
// Clear associated user values
|
||||
#[cfg(feature = "lua54")]
|
||||
for i in 1..=USER_VALUE_MAXSLOT {
|
||||
ffi::lua_pushnil(lua.state);
|
||||
ffi::lua_setiuservalue(lua.state, -2, i as c_int);
|
||||
}
|
||||
#[cfg(any(feature = "lua53", feature = "lua52"))]
|
||||
{
|
||||
ffi::lua_pushnil(lua.state);
|
||||
ffi::lua_setuservalue(lua.state, -2);
|
||||
}
|
||||
#[cfg(any(feature = "lua51", feature = "luajit"))]
|
||||
protect_lua!(lua.state, 1, 1, fn(state) {
|
||||
ffi::lua_newtable(state);
|
||||
ffi::lua_setuservalue(state, -2);
|
||||
})?;
|
||||
|
||||
Ok(take_userdata::<UserDataCell<T>>(lua.state).into_inner())
|
||||
}
|
||||
_ => Err(Error::UserDataTypeMismatch),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets an associated value to this `AnyUserData`.
|
||||
///
|
||||
/// The value may be any Lua value whatsoever, and can be retrieved with [`get_user_value`].
|
||||
///
|
||||
/// This is the same as calling [`set_nth_user_value`] with `n` set to 1.
|
||||
///
|
||||
/// [`get_user_value`]: #method.get_user_value
|
||||
/// [`set_nth_user_value`]: #method.set_nth_user_value
|
||||
#[inline]
|
||||
pub fn set_user_value<V: ToLua<'lua>>(&self, v: V) -> Result<()> {
|
||||
self.set_nth_user_value(1, v)
|
||||
}
|
||||
|
||||
/// Returns an associated value set by [`set_user_value`].
|
||||
///
|
||||
/// This is the same as calling [`get_nth_user_value`] with `n` set to 1.
|
||||
///
|
||||
/// [`set_user_value`]: #method.set_user_value
|
||||
/// [`get_nth_user_value`]: #method.get_nth_user_value
|
||||
#[inline]
|
||||
pub fn get_user_value<V: FromLua<'lua>>(&self) -> Result<V> {
|
||||
self.get_nth_user_value(1)
|
||||
}
|
||||
|
||||
/// Sets an associated `n`th value to this `AnyUserData`.
|
||||
///
|
||||
/// The value may be any Lua value whatsoever, and can be retrieved with [`get_nth_user_value`].
|
||||
/// `n` starts from 1 and can be up to 65535.
|
||||
///
|
||||
/// This is supported for all Lua versions.
|
||||
/// In Lua 5.4 first 7 elements are stored in a most efficient way.
|
||||
/// For other Lua versions this functionality is provided using a wrapping table.
|
||||
///
|
||||
/// [`get_nth_user_value`]: #method.get_nth_user_value
|
||||
pub fn set_nth_user_value<V: ToLua<'lua>>(&self, n: usize, v: V) -> Result<()> {
|
||||
if n < 1 || n > u16::MAX as usize {
|
||||
return Err(Error::RuntimeError(
|
||||
"user value index out of bounds".to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
let lua = self.0.lua;
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(lua.state);
|
||||
check_stack(lua.state, 5)?;
|
||||
|
||||
lua.push_userdata_ref(&self.0)?;
|
||||
lua.push_value(v)?;
|
||||
ffi::lua_setuservalue(lua.state, -2);
|
||||
lua.push_value(v.to_lua(lua)?)?;
|
||||
|
||||
#[cfg(feature = "lua54")]
|
||||
if n < USER_VALUE_MAXSLOT {
|
||||
ffi::lua_setiuservalue(lua.state, -2, n as c_int);
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// Multiple (extra) user values are emulated by storing them in a table
|
||||
protect_lua!(lua.state, 2, 0, |state| {
|
||||
if getuservalue_table(lua.state, -2) != ffi::LUA_TTABLE {
|
||||
// Create a new table to use as uservalue
|
||||
ffi::lua_pop(lua.state, 1);
|
||||
ffi::lua_newtable(state);
|
||||
ffi::lua_pushvalue(state, -1);
|
||||
|
||||
#[cfg(feature = "lua54")]
|
||||
ffi::lua_setiuservalue(lua.state, -4, USER_VALUE_MAXSLOT as c_int);
|
||||
#[cfg(not(feature = "lua54"))]
|
||||
ffi::lua_setuservalue(lua.state, -4);
|
||||
}
|
||||
ffi::lua_pushvalue(state, -2);
|
||||
#[cfg(feature = "lua54")]
|
||||
ffi::lua_rawseti(state, -2, (n - USER_VALUE_MAXSLOT + 1) as ffi::lua_Integer);
|
||||
#[cfg(not(feature = "lua54"))]
|
||||
ffi::lua_rawseti(state, -2, n as ffi::lua_Integer);
|
||||
})?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns an associated value set by [`set_user_value`].
|
||||
/// Returns an associated `n`th value set by [`set_nth_user_value`].
|
||||
///
|
||||
/// For Lua < 5.3 the value will be automatically extracted from the table wrapper from index 1.
|
||||
/// `n` starts from 1 and can be up to 65535.
|
||||
///
|
||||
/// [`set_user_value`]: #method.set_user_value
|
||||
pub fn get_user_value<V: FromLua<'lua>>(&self) -> Result<V> {
|
||||
/// This is supported for all Lua versions.
|
||||
/// In Lua 5.4 first 7 elements are stored in a most efficient way.
|
||||
/// For other Lua versions this functionality is provided using a wrapping table.
|
||||
///
|
||||
/// [`set_nth_user_value`]: #method.set_nth_user_value
|
||||
pub fn get_nth_user_value<V: FromLua<'lua>>(&self, n: usize) -> Result<V> {
|
||||
if n < 1 || n > u16::MAX as usize {
|
||||
return Err(Error::RuntimeError(
|
||||
"user value index out of bounds".to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
let lua = self.0.lua;
|
||||
let res = unsafe {
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(lua.state);
|
||||
check_stack(lua.state, 3)?;
|
||||
check_stack(lua.state, 4)?;
|
||||
|
||||
lua.push_userdata_ref(&self.0)?;
|
||||
ffi::lua_getuservalue(lua.state, -1);
|
||||
lua.pop_value()
|
||||
};
|
||||
#[cfg(any(feature = "lua52", feature = "lua51", feature = "luajit"))]
|
||||
return match <Option<Table>>::from_lua(res, lua)? {
|
||||
Some(t) => t.get(1),
|
||||
None => V::from_lua(Value::Nil, lua),
|
||||
};
|
||||
#[cfg(any(feature = "lua54", feature = "lua53"))]
|
||||
V::from_lua(res, lua)
|
||||
|
||||
#[cfg(feature = "lua54")]
|
||||
if n < USER_VALUE_MAXSLOT {
|
||||
ffi::lua_getiuservalue(lua.state, -1, n as c_int);
|
||||
return V::from_lua(lua.pop_value(), lua);
|
||||
}
|
||||
|
||||
// Multiple (extra) user values are emulated by storing them in a table
|
||||
protect_lua!(lua.state, 1, 1, |state| {
|
||||
if getuservalue_table(lua.state, -1) != ffi::LUA_TTABLE {
|
||||
ffi::lua_pushnil(lua.state);
|
||||
return;
|
||||
}
|
||||
#[cfg(feature = "lua54")]
|
||||
ffi::lua_rawgeti(state, -1, (n - USER_VALUE_MAXSLOT + 1) as ffi::lua_Integer);
|
||||
#[cfg(not(feature = "lua54"))]
|
||||
ffi::lua_rawgeti(state, -1, n as ffi::lua_Integer);
|
||||
})?;
|
||||
|
||||
V::from_lua(lua.pop_value(), lua)
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets an associated value to this `AnyUserData` by name.
|
||||
///
|
||||
/// The value can be retrieved with [`get_named_user_value`].
|
||||
///
|
||||
/// [`get_named_user_value`]: #method.get_named_user_value
|
||||
pub fn set_named_user_value<S, V>(&self, name: &S, v: V) -> Result<()>
|
||||
where
|
||||
S: AsRef<[u8]> + ?Sized,
|
||||
V: ToLua<'lua>,
|
||||
{
|
||||
let lua = self.0.lua;
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(lua.state);
|
||||
check_stack(lua.state, 5)?;
|
||||
|
||||
lua.push_userdata_ref(&self.0)?;
|
||||
lua.push_value(v.to_lua(lua)?)?;
|
||||
|
||||
// Multiple (extra) user values are emulated by storing them in a table
|
||||
let name = name.as_ref();
|
||||
protect_lua!(lua.state, 2, 0, |state| {
|
||||
if getuservalue_table(lua.state, -2) != ffi::LUA_TTABLE {
|
||||
// Create a new table to use as uservalue
|
||||
ffi::lua_pop(lua.state, 1);
|
||||
ffi::lua_newtable(state);
|
||||
ffi::lua_pushvalue(state, -1);
|
||||
|
||||
#[cfg(feature = "lua54")]
|
||||
ffi::lua_setiuservalue(lua.state, -4, USER_VALUE_MAXSLOT as c_int);
|
||||
#[cfg(not(feature = "lua54"))]
|
||||
ffi::lua_setuservalue(lua.state, -4);
|
||||
}
|
||||
ffi::lua_pushlstring(state, name.as_ptr() as *const c_char, name.len());
|
||||
ffi::lua_pushvalue(state, -3);
|
||||
ffi::lua_rawset(state, -3);
|
||||
})?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns an associated value by name set by [`set_named_user_value`].
|
||||
///
|
||||
/// [`set_named_user_value`]: #method.set_named_user_value
|
||||
pub fn get_named_user_value<S, V>(&self, name: &S) -> Result<V>
|
||||
where
|
||||
S: AsRef<[u8]> + ?Sized,
|
||||
V: FromLua<'lua>,
|
||||
{
|
||||
let lua = self.0.lua;
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(lua.state);
|
||||
check_stack(lua.state, 4)?;
|
||||
|
||||
lua.push_userdata_ref(&self.0)?;
|
||||
|
||||
// Multiple (extra) user values are emulated by storing them in a table
|
||||
let name = name.as_ref();
|
||||
protect_lua!(lua.state, 1, 1, |state| {
|
||||
if getuservalue_table(lua.state, -1) != ffi::LUA_TTABLE {
|
||||
ffi::lua_pushnil(lua.state);
|
||||
return;
|
||||
}
|
||||
ffi::lua_pushlstring(state, name.as_ptr() as *const c_char, name.len());
|
||||
ffi::lua_rawget(state, -2);
|
||||
})?;
|
||||
|
||||
V::from_lua(lua.pop_value(), lua)
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a metatable of this `UserData`.
|
||||
@@ -808,7 +1059,7 @@ impl<'lua> AnyUserData<'lua> {
|
||||
///
|
||||
/// For `T: UserData + 'static` returned metatable is shared among all instances of type `T`.
|
||||
///
|
||||
/// [`UserDataMetatable`]: struct.UserDataMetatable.html
|
||||
/// [`UserDataMetatable`]: crate::UserDataMetatable
|
||||
pub fn get_metatable(&self) -> Result<UserDataMetatable<'lua>> {
|
||||
self.get_raw_metatable().map(UserDataMetatable)
|
||||
}
|
||||
@@ -880,6 +1131,13 @@ impl<'lua> AsRef<AnyUserData<'lua>> for AnyUserData<'lua> {
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn getuservalue_table(state: *mut ffi::lua_State, idx: c_int) -> c_int {
|
||||
#[cfg(feature = "lua54")]
|
||||
return ffi::lua_getiuservalue(state, idx, USER_VALUE_MAXSLOT as c_int);
|
||||
#[cfg(not(feature = "lua54"))]
|
||||
return ffi::lua_getuservalue(state, idx);
|
||||
}
|
||||
|
||||
/// Handle to a `UserData` metatable.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct UserDataMetatable<'lua>(pub(crate) Table<'lua>);
|
||||
@@ -917,7 +1175,7 @@ impl<'lua> UserDataMetatable<'lua> {
|
||||
///
|
||||
/// The pairs are wrapped in a [`Result`], since they are lazily converted to `V` type.
|
||||
///
|
||||
/// [`Result`]: type.Result.html
|
||||
/// [`Result`]: crate::Result
|
||||
pub fn pairs<V: FromLua<'lua>>(self) -> UserDataMetatablePairs<'lua, V> {
|
||||
UserDataMetatablePairs(self.0.pairs())
|
||||
}
|
||||
@@ -929,8 +1187,8 @@ impl<'lua> UserDataMetatable<'lua> {
|
||||
///
|
||||
/// This struct is created by the [`UserDataMetatable::pairs`] method.
|
||||
///
|
||||
/// [`UserData`]: trait.UserData.html
|
||||
/// [`UserDataMetatable::pairs`]: struct.UserDataMetatable.html#method.pairs
|
||||
/// [`UserData`]: crate::UserData
|
||||
/// [`UserDataMetatable::pairs`]: crate::UserDataMetatable::method.pairs
|
||||
pub struct UserDataMetatablePairs<'lua, V>(TablePairs<'lua, StdString, V>);
|
||||
|
||||
impl<'lua, V> Iterator for UserDataMetatablePairs<'lua, V>
|
||||
@@ -960,20 +1218,19 @@ impl<'lua> Serialize for AnyUserData<'lua> {
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
unsafe {
|
||||
let lua = self.0.lua;
|
||||
let lua = self.0.lua;
|
||||
let data = unsafe {
|
||||
let _sg = StackGuard::new(lua.state);
|
||||
check_stack(lua.state, 3).map_err(ser::Error::custom)?;
|
||||
|
||||
lua.push_userdata_ref(&self.0).map_err(ser::Error::custom)?;
|
||||
let ud = &*get_userdata::<UserDataCell<c_void>>(lua.state, -1);
|
||||
let data =
|
||||
ud.0.try_borrow()
|
||||
.map_err(|_| ser::Error::custom(Error::UserDataBorrowError))?;
|
||||
match *data {
|
||||
UserDataWrapped::Default(_) => UserDataSerializeError.serialize(serializer),
|
||||
UserDataWrapped::Serializable(_, ser) => (&*ser).serialize(serializer),
|
||||
}
|
||||
let ud = &*get_userdata::<UserDataCell<()>>(lua.state, -1);
|
||||
ud.0.try_borrow()
|
||||
.map_err(|_| ser::Error::custom(Error::UserDataBorrowError))?
|
||||
};
|
||||
match &*data {
|
||||
UserDataWrapped::Default(_) => UserDataSerializeError.serialize(serializer),
|
||||
UserDataWrapped::Serializable(ser) => ser.serialize(serializer),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+30
-41
@@ -1,6 +1,4 @@
|
||||
use std::any::{Any, TypeId};
|
||||
use std::collections::HashMap;
|
||||
use std::error::Error as StdError;
|
||||
use std::fmt::Write;
|
||||
use std::os::raw::{c_char, c_int, c_void};
|
||||
use std::panic::{catch_unwind, resume_unwind, AssertUnwindSafe};
|
||||
@@ -8,12 +6,13 @@ use std::sync::Arc;
|
||||
use std::{mem, ptr, slice};
|
||||
|
||||
use once_cell::sync::Lazy;
|
||||
use rustc_hash::FxHashMap;
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
use crate::ffi;
|
||||
|
||||
static METATABLE_CACHE: Lazy<HashMap<TypeId, u8>> = Lazy::new(|| {
|
||||
let mut map = HashMap::with_capacity(32);
|
||||
static METATABLE_CACHE: Lazy<FxHashMap<TypeId, u8>> = Lazy::new(|| {
|
||||
let mut map = FxHashMap::with_capacity_and_hasher(32, Default::default());
|
||||
crate::lua::init_metatable_cache(&mut map);
|
||||
map.insert(TypeId::of::<WrappedFailure>(), 0);
|
||||
map.insert(TypeId::of::<String>(), 0);
|
||||
@@ -288,6 +287,17 @@ pub unsafe fn push_userdata<T>(state: *mut ffi::lua_State, t: T) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Internally uses 3 stack spaces, does not call checkstack.
|
||||
#[cfg(feature = "lua54")]
|
||||
#[inline]
|
||||
pub unsafe fn push_userdata_uv<T>(state: *mut ffi::lua_State, t: T, nuvalue: c_int) -> Result<()> {
|
||||
let ud = protect_lua!(state, 0, 1, |state| {
|
||||
ffi::lua_newuserdatauv(state, mem::size_of::<T>(), nuvalue) as *mut T
|
||||
})?;
|
||||
ptr::write(ud, t);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub unsafe fn get_userdata<T>(state: *mut ffi::lua_State, index: c_int) -> *mut T {
|
||||
let ud = ffi::lua_touserdata(state, index) as *mut T;
|
||||
@@ -535,11 +545,8 @@ where
|
||||
ffi::lua_settop(state, 1);
|
||||
|
||||
let wrapped_error = ud as *mut WrappedFailure;
|
||||
ptr::write(wrapped_error, WrappedFailure::Error(err));
|
||||
get_gc_metatable::<WrappedFailure>(state);
|
||||
ffi::lua_setmetatable(state, -2);
|
||||
|
||||
// Convert to CallbackError and attach traceback
|
||||
// Build `CallbackError` with traceback
|
||||
let traceback = if ffi::lua_checkstack(state, ffi::LUA_TRACEBACK_STACK) != 0 {
|
||||
ffi::luaL_traceback(state, state, ptr::null(), 0);
|
||||
let traceback = to_string(state, -1);
|
||||
@@ -548,10 +555,13 @@ where
|
||||
} else {
|
||||
"<not enough stack space for traceback>".to_string()
|
||||
};
|
||||
if let WrappedFailure::Error(ref mut err) = *wrapped_error {
|
||||
let cause = Arc::new(err.clone());
|
||||
*err = Error::CallbackError { traceback, cause };
|
||||
}
|
||||
let cause = Arc::new(err);
|
||||
ptr::write(
|
||||
wrapped_error,
|
||||
WrappedFailure::Error(Error::CallbackError { traceback, cause }),
|
||||
);
|
||||
get_gc_metatable::<WrappedFailure>(state);
|
||||
ffi::lua_setmetatable(state, -2);
|
||||
|
||||
ffi::lua_error(state)
|
||||
}
|
||||
@@ -575,7 +585,7 @@ pub unsafe extern "C" fn error_traceback(state: *mut ffi::lua_State) -> c_int {
|
||||
if get_gc_userdata::<WrappedFailure>(state, -1).is_null() {
|
||||
let s = ffi::luaL_tolstring(state, -1, ptr::null_mut());
|
||||
if ffi::lua_checkstack(state, ffi::LUA_TRACEBACK_STACK) != 0 {
|
||||
ffi::luaL_traceback(state, state, s, 1);
|
||||
ffi::luaL_traceback(state, state, s, 0);
|
||||
ffi::lua_remove(state, -2);
|
||||
}
|
||||
}
|
||||
@@ -736,32 +746,6 @@ pub unsafe fn init_error_registry(state: *mut ffi::lua_State) -> Result<()> {
|
||||
// be possible to make this consume arbitrary amounts of memory (for example, some
|
||||
// kind of recursive error structure?)
|
||||
let _ = write!(&mut (*err_buf), "{}", error);
|
||||
// Find first two sources that caused the error
|
||||
let mut source1 = error.source();
|
||||
let mut source0 = source1.and_then(|s| s.source());
|
||||
while let Some(source) = source0.and_then(|s| s.source()) {
|
||||
source1 = source0;
|
||||
source0 = Some(source);
|
||||
}
|
||||
match (source1, source0) {
|
||||
(_, Some(error0))
|
||||
if error0.to_string().contains("\nstack traceback:\n") =>
|
||||
{
|
||||
let _ = write!(&mut (*err_buf), "\ncaused by: {}", error0);
|
||||
}
|
||||
(Some(error1), Some(error0)) => {
|
||||
let _ = write!(&mut (*err_buf), "\ncaused by: {}", error0);
|
||||
let s = error1.to_string();
|
||||
if let Some(traceback) = s.splitn(2, "\nstack traceback:\n").nth(1) {
|
||||
let _ =
|
||||
write!(&mut (*err_buf), "\nstack traceback:\n{}", traceback);
|
||||
}
|
||||
}
|
||||
(Some(error1), None) => {
|
||||
let _ = write!(&mut (*err_buf), "\ncaused by: {}", error1);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
Ok(err_buf)
|
||||
}
|
||||
Some(WrappedFailure::Panic(Some(ref panic))) => {
|
||||
@@ -841,9 +825,14 @@ pub unsafe fn init_error_registry(state: *mut ffi::lua_State) -> Result<()> {
|
||||
"__newindex",
|
||||
"__call",
|
||||
"__tostring",
|
||||
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
|
||||
#[cfg(any(
|
||||
feature = "lua54",
|
||||
feature = "lua53",
|
||||
feature = "lua52",
|
||||
feature = "luajit52"
|
||||
))]
|
||||
"__pairs",
|
||||
#[cfg(any(feature = "lua53", feature = "lua52"))]
|
||||
#[cfg(any(feature = "lua53", feature = "lua52", feature = "luajit52"))]
|
||||
"__ipairs",
|
||||
#[cfg(feature = "lua54")]
|
||||
"__close",
|
||||
|
||||
+32
-2
@@ -49,10 +49,11 @@ pub enum Value<'lua> {
|
||||
/// `Error` is a special builtin userdata type. When received from Lua it is implicitly cloned.
|
||||
Error(Error),
|
||||
}
|
||||
|
||||
pub use self::Value::Nil;
|
||||
|
||||
impl<'lua> Value<'lua> {
|
||||
pub fn type_name(&self) -> &'static str {
|
||||
pub const fn type_name(&self) -> &'static str {
|
||||
match *self {
|
||||
Value::Nil => "nil",
|
||||
Value::Boolean(_) => "boolean",
|
||||
@@ -161,6 +162,12 @@ impl<'lua> MultiValue<'lua> {
|
||||
pub fn new() -> MultiValue<'lua> {
|
||||
MultiValue(Vec::new())
|
||||
}
|
||||
|
||||
/// Similar to `new` but can return previously used container with allocated capacity.
|
||||
#[inline]
|
||||
pub(crate) fn new_or_cached(lua: &'lua Lua) -> MultiValue<'lua> {
|
||||
lua.new_or_cached_multivalue()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> Default for MultiValue<'lua> {
|
||||
@@ -226,6 +233,11 @@ impl<'lua> MultiValue<'lua> {
|
||||
self.0.pop()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn clear(&mut self) {
|
||||
self.0.clear();
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn len(&self) -> usize {
|
||||
self.0.len()
|
||||
@@ -233,13 +245,31 @@ impl<'lua> MultiValue<'lua> {
|
||||
|
||||
#[inline]
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.0.len() == 0
|
||||
self.0.is_empty()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn iter(&self) -> iter::Rev<slice::Iter<Value<'lua>>> {
|
||||
self.0.iter().rev()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn drain_all(&mut self) -> iter::Rev<vec::Drain<Value<'lua>>> {
|
||||
self.0.drain(..).rev()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn refill(
|
||||
&mut self,
|
||||
iter: impl IntoIterator<Item = Result<Value<'lua>>>,
|
||||
) -> Result<()> {
|
||||
self.0.clear();
|
||||
for value in iter {
|
||||
self.0.push(value?);
|
||||
}
|
||||
self.0.reverse();
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Trait for types convertible to any number of Lua values.
|
||||
|
||||
+24
-5
@@ -3,7 +3,7 @@
|
||||
use std::cell::Cell;
|
||||
use std::rc::Rc;
|
||||
use std::sync::{
|
||||
atomic::{AtomicI64, Ordering},
|
||||
atomic::{AtomicI64, AtomicU64, Ordering},
|
||||
Arc,
|
||||
};
|
||||
use std::time::Duration;
|
||||
@@ -12,7 +12,8 @@ use futures_timer::Delay;
|
||||
use futures_util::stream::TryStreamExt;
|
||||
|
||||
use mlua::{
|
||||
Error, Function, Lua, Result, Table, TableExt, Thread, UserData, UserDataMethods, Value,
|
||||
Error, Function, Lua, LuaOptions, MetaMethod, Result, StdLib, Table, TableExt, Thread,
|
||||
UserData, UserDataMethods, Value,
|
||||
};
|
||||
|
||||
#[tokio::test]
|
||||
@@ -227,7 +228,8 @@ async fn test_async_thread() -> Result<()> {
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_async_table() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
let options = LuaOptions::new().thread_cache_size(4);
|
||||
let lua = Lua::new_with(StdLib::ALL_SAFE, options)?;
|
||||
|
||||
let table = lua.create_table()?;
|
||||
table.set("val", 10)?;
|
||||
@@ -276,7 +278,7 @@ async fn test_async_table() -> Result<()> {
|
||||
#[tokio::test]
|
||||
async fn test_async_userdata() -> Result<()> {
|
||||
#[derive(Clone)]
|
||||
struct MyUserData(Arc<AtomicI64>);
|
||||
struct MyUserData(Arc<AtomicU64>);
|
||||
|
||||
impl UserData for MyUserData {
|
||||
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
|
||||
@@ -295,13 +297,20 @@ async fn test_async_userdata() -> Result<()> {
|
||||
Delay::new(Duration::from_millis(n)).await;
|
||||
Ok(format!("elapsed:{}ms", n))
|
||||
});
|
||||
|
||||
#[cfg(not(feature = "lua51"))]
|
||||
methods.add_async_meta_method(MetaMethod::Call, |_, data, ()| async move {
|
||||
let n = data.0.load(Ordering::Relaxed);
|
||||
Delay::new(Duration::from_millis(n)).await;
|
||||
Ok(format!("elapsed:{}ms", n))
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
let lua = Lua::new();
|
||||
let globals = lua.globals();
|
||||
|
||||
let userdata = lua.create_userdata(MyUserData(Arc::new(AtomicI64::new(11))))?;
|
||||
let userdata = lua.create_userdata(MyUserData(Arc::new(AtomicU64::new(11))))?;
|
||||
globals.set("userdata", userdata.clone())?;
|
||||
|
||||
lua.load(
|
||||
@@ -315,6 +324,16 @@ async fn test_async_userdata() -> Result<()> {
|
||||
.exec_async()
|
||||
.await?;
|
||||
|
||||
#[cfg(not(feature = "lua51"))]
|
||||
lua.load(
|
||||
r#"
|
||||
userdata:set_value(15)
|
||||
assert(userdata() == "elapsed:15ms")
|
||||
"#,
|
||||
)
|
||||
.exec_async()
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
error[E0373]: closure may outlive the current function, but it borrows `test`, which is owned by the current function
|
||||
--> $DIR/function_borrow.rs:9:33
|
||||
--> tests/compile/function_borrow.rs:9:33
|
||||
|
|
||||
9 | let _ = lua.create_function(|_, ()| -> Result<i32> {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^ may outlive borrowed value `test`
|
||||
@@ -7,7 +7,7 @@ error[E0373]: closure may outlive the current function, but it borrows `test`, w
|
||||
| ------ `test` is borrowed here
|
||||
|
|
||||
note: function requires argument type to outlive `'static`
|
||||
--> $DIR/function_borrow.rs:9:13
|
||||
--> tests/compile/function_borrow.rs:9:13
|
||||
|
|
||||
9 | let _ = lua.create_function(|_, ()| -> Result<i32> {
|
||||
| _____________^
|
||||
@@ -17,4 +17,4 @@ note: function requires argument type to outlive `'static`
|
||||
help: to force the closure to take ownership of `test` (and any other referenced variables), use the `move` keyword
|
||||
|
|
||||
9 | let _ = lua.create_function(move |_, ()| -> Result<i32> {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
| ++++
|
||||
|
||||
@@ -1,31 +1,26 @@
|
||||
error[E0277]: the type `UnsafeCell<()>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
|
||||
--> $DIR/lua_norefunwindsafe.rs:7:5
|
||||
--> tests/compile/lua_norefunwindsafe.rs:7:5
|
||||
|
|
||||
7 | catch_unwind(|| lua.create_table().unwrap());
|
||||
| ^^^^^^^^^^^^ `UnsafeCell<()>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
|
||||
|
|
||||
::: $RUST/std/src/panic.rs
|
||||
|
|
||||
| pub fn catch_unwind<F: FnOnce() -> R + UnwindSafe, R>(f: F) -> Result<R> {
|
||||
| ---------- required by this bound in `catch_unwind`
|
||||
|
|
||||
= help: within `Lua`, the trait `RefUnwindSafe` is not implemented for `UnsafeCell<()>`
|
||||
= note: required because it appears within the type `PhantomData<UnsafeCell<()>>`
|
||||
= note: required because it appears within the type `Lua`
|
||||
= note: required because of the requirements on the impl of `UnwindSafe` for `&Lua`
|
||||
= note: required because it appears within the type `[closure@$DIR/tests/compile/lua_norefunwindsafe.rs:7:18: 7:48]`
|
||||
note: required by a bound in `catch_unwind`
|
||||
--> $RUST/std/src/panic.rs
|
||||
|
|
||||
| pub fn catch_unwind<F: FnOnce() -> R + UnwindSafe, R>(f: F) -> Result<R> {
|
||||
| ^^^^^^^^^^ required by this bound in `catch_unwind`
|
||||
|
||||
error[E0277]: the type `UnsafeCell<mlua::lua::ExtraData>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
|
||||
--> $DIR/lua_norefunwindsafe.rs:7:5
|
||||
--> tests/compile/lua_norefunwindsafe.rs:7:5
|
||||
|
|
||||
7 | catch_unwind(|| lua.create_table().unwrap());
|
||||
| ^^^^^^^^^^^^ `UnsafeCell<mlua::lua::ExtraData>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
|
||||
|
|
||||
::: $RUST/std/src/panic.rs
|
||||
|
|
||||
| pub fn catch_unwind<F: FnOnce() -> R + UnwindSafe, R>(f: F) -> Result<R> {
|
||||
| ---------- required by this bound in `catch_unwind`
|
||||
|
|
||||
= help: within `Lua`, the trait `RefUnwindSafe` is not implemented for `UnsafeCell<mlua::lua::ExtraData>`
|
||||
= note: required because it appears within the type `alloc::sync::ArcInner<UnsafeCell<mlua::lua::ExtraData>>`
|
||||
= note: required because it appears within the type `PhantomData<alloc::sync::ArcInner<UnsafeCell<mlua::lua::ExtraData>>>`
|
||||
@@ -33,3 +28,8 @@ error[E0277]: the type `UnsafeCell<mlua::lua::ExtraData>` may contain interior m
|
||||
= note: required because it appears within the type `Lua`
|
||||
= note: required because of the requirements on the impl of `UnwindSafe` for `&Lua`
|
||||
= note: required because it appears within the type `[closure@$DIR/tests/compile/lua_norefunwindsafe.rs:7:18: 7:48]`
|
||||
note: required by a bound in `catch_unwind`
|
||||
--> $RUST/std/src/panic.rs
|
||||
|
|
||||
| pub fn catch_unwind<F: FnOnce() -> R + UnwindSafe, R>(f: F) -> Result<R> {
|
||||
| ^^^^^^^^^^ required by this bound in `catch_unwind`
|
||||
|
||||
@@ -1,14 +1,9 @@
|
||||
error[E0277]: the type `UnsafeCell<()>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
|
||||
--> $DIR/ref_nounwindsafe.rs:8:5
|
||||
--> tests/compile/ref_nounwindsafe.rs:8:5
|
||||
|
|
||||
8 | catch_unwind(move || table.set("a", "b").unwrap());
|
||||
| ^^^^^^^^^^^^ `UnsafeCell<()>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
|
||||
|
|
||||
::: $RUST/std/src/panic.rs
|
||||
|
|
||||
| pub fn catch_unwind<F: FnOnce() -> R + UnwindSafe, R>(f: F) -> Result<R> {
|
||||
| ---------- required by this bound in `catch_unwind`
|
||||
|
|
||||
= help: within `Lua`, the trait `RefUnwindSafe` is not implemented for `UnsafeCell<()>`
|
||||
= note: required because it appears within the type `PhantomData<UnsafeCell<()>>`
|
||||
= note: required because it appears within the type `Lua`
|
||||
@@ -16,18 +11,18 @@ error[E0277]: the type `UnsafeCell<()>` may contain interior mutability and a re
|
||||
= note: required because it appears within the type `mlua::types::LuaRef<'_>`
|
||||
= note: required because it appears within the type `LuaTable<'_>`
|
||||
= note: required because it appears within the type `[closure@$DIR/tests/compile/ref_nounwindsafe.rs:8:18: 8:54]`
|
||||
note: required by a bound in `catch_unwind`
|
||||
--> $RUST/std/src/panic.rs
|
||||
|
|
||||
| pub fn catch_unwind<F: FnOnce() -> R + UnwindSafe, R>(f: F) -> Result<R> {
|
||||
| ^^^^^^^^^^ required by this bound in `catch_unwind`
|
||||
|
||||
error[E0277]: the type `UnsafeCell<mlua::lua::ExtraData>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
|
||||
--> $DIR/ref_nounwindsafe.rs:8:5
|
||||
--> tests/compile/ref_nounwindsafe.rs:8:5
|
||||
|
|
||||
8 | catch_unwind(move || table.set("a", "b").unwrap());
|
||||
| ^^^^^^^^^^^^ `UnsafeCell<mlua::lua::ExtraData>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
|
||||
|
|
||||
::: $RUST/std/src/panic.rs
|
||||
|
|
||||
| pub fn catch_unwind<F: FnOnce() -> R + UnwindSafe, R>(f: F) -> Result<R> {
|
||||
| ---------- required by this bound in `catch_unwind`
|
||||
|
|
||||
= help: within `Lua`, the trait `RefUnwindSafe` is not implemented for `UnsafeCell<mlua::lua::ExtraData>`
|
||||
= note: required because it appears within the type `alloc::sync::ArcInner<UnsafeCell<mlua::lua::ExtraData>>`
|
||||
= note: required because it appears within the type `PhantomData<alloc::sync::ArcInner<UnsafeCell<mlua::lua::ExtraData>>>`
|
||||
@@ -37,3 +32,8 @@ error[E0277]: the type `UnsafeCell<mlua::lua::ExtraData>` may contain interior m
|
||||
= note: required because it appears within the type `mlua::types::LuaRef<'_>`
|
||||
= note: required because it appears within the type `LuaTable<'_>`
|
||||
= note: required because it appears within the type `[closure@$DIR/tests/compile/ref_nounwindsafe.rs:8:18: 8:54]`
|
||||
note: required by a bound in `catch_unwind`
|
||||
--> $RUST/std/src/panic.rs
|
||||
|
|
||||
| pub fn catch_unwind<F: FnOnce() -> R + UnwindSafe, R>(f: F) -> Result<R> {
|
||||
| ^^^^^^^^^^ required by this bound in `catch_unwind`
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
error[E0521]: borrowed data escapes outside of closure
|
||||
--> $DIR/scope_callback_inner.rs:7:17
|
||||
--> tests/compile/scope_callback_inner.rs:7:17
|
||||
|
|
||||
5 | lua.scope(|scope| {
|
||||
| -----
|
||||
@@ -16,7 +16,7 @@ error[E0521]: borrowed data escapes outside of closure
|
||||
| |______________^ `scope` escapes the closure body here
|
||||
|
||||
error[E0373]: closure may outlive the current function, but it borrows `inner`, which is owned by the current function
|
||||
--> $DIR/scope_callback_inner.rs:8:34
|
||||
--> tests/compile/scope_callback_inner.rs:8:34
|
||||
|
|
||||
5 | lua.scope(|scope| {
|
||||
| ----- has type `&Scope<'_, '2>`
|
||||
@@ -27,7 +27,7 @@ error[E0373]: closure may outlive the current function, but it borrows `inner`,
|
||||
| ----- `inner` is borrowed here
|
||||
|
|
||||
note: function requires argument type to outlive `'2`
|
||||
--> $DIR/scope_callback_inner.rs:7:17
|
||||
--> tests/compile/scope_callback_inner.rs:7:17
|
||||
|
|
||||
7 | let f = scope
|
||||
| _________________^
|
||||
@@ -39,4 +39,4 @@ note: function requires argument type to outlive `'2`
|
||||
help: to force the closure to take ownership of `inner` (and any other referenced variables), use the `move` keyword
|
||||
|
|
||||
8 | .create_function_mut(move |_, t: Table| {
|
||||
| ^^^^^^^^^^^^^^^^^^
|
||||
| ++++
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
error[E0373]: closure may outlive the current function, but it borrows `test`, which is owned by the current function
|
||||
--> $DIR/scope_invariance.rs:14:38
|
||||
--> tests/compile/scope_invariance.rs:14:38
|
||||
|
|
||||
9 | lua.scope(|scope| {
|
||||
| ----- has type `&Scope<'_, '1>`
|
||||
@@ -10,7 +10,7 @@ error[E0373]: closure may outlive the current function, but it borrows `test`, w
|
||||
| ---------- `test` is borrowed here
|
||||
|
|
||||
note: function requires argument type to outlive `'1`
|
||||
--> $DIR/scope_invariance.rs:13:13
|
||||
--> tests/compile/scope_invariance.rs:13:13
|
||||
|
|
||||
13 | / scope
|
||||
14 | | .create_function_mut(|_, ()| {
|
||||
@@ -22,4 +22,4 @@ note: function requires argument type to outlive `'1`
|
||||
help: to force the closure to take ownership of `test` (and any other referenced variables), use the `move` keyword
|
||||
|
|
||||
14 | .create_function_mut(move |_, ()| {
|
||||
| ^^^^^^^^^^^^
|
||||
| ++++
|
||||
|
||||
+16
-1
@@ -3,7 +3,7 @@ use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet};
|
||||
use std::ffi::{CStr, CString};
|
||||
|
||||
use maplit::{btreemap, btreeset, hashmap, hashset};
|
||||
use mlua::{Lua, Result};
|
||||
use mlua::{Error, Lua, Result};
|
||||
|
||||
#[test]
|
||||
fn test_conv_vec() -> Result<()> {
|
||||
@@ -123,3 +123,18 @@ fn test_conv_boxed_slice() -> Result<()> {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_conv_array() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let v = [1, 2, 3];
|
||||
lua.globals().set("v", v)?;
|
||||
let v2: [i32; 3] = lua.globals().get("v")?;
|
||||
assert_eq!(v, v2);
|
||||
|
||||
let v2 = lua.globals().get::<_, [i32; 4]>("v");
|
||||
assert!(matches!(v2, Err(Error::FromLuaConversionError { .. })));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
[target.x86_64-apple-darwin]
|
||||
rustflags = ["-C", "link-args=-rdynamic"]
|
||||
|
||||
[target.aarch64-apple-darwin]
|
||||
rustflags = ["-C", "link-args=-rdynamic"]
|
||||
|
||||
[target.x86_64-unknown-linux-gnu]
|
||||
rustflags = ["-C", "link-args=-rdynamic"]
|
||||
|
||||
+5
-24
@@ -178,30 +178,6 @@ fn test_to_value_struct() -> LuaResult<()> {
|
||||
fn test_to_value_enum() -> LuaResult<()> {
|
||||
let lua = Lua::new();
|
||||
let globals = lua.globals();
|
||||
globals.set("null", lua.null())?;
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct Test {
|
||||
name: String,
|
||||
key: i64,
|
||||
data: Option<bool>,
|
||||
}
|
||||
|
||||
let test = Test {
|
||||
name: "alex".to_string(),
|
||||
key: -16,
|
||||
data: None,
|
||||
};
|
||||
|
||||
globals.set("value", lua.to_value(&test)?)?;
|
||||
lua.load(
|
||||
r#"
|
||||
assert(value["name"] == "alex")
|
||||
assert(value["key"] == -16)
|
||||
assert(value["data"] == null)
|
||||
"#,
|
||||
)
|
||||
.exec()?;
|
||||
|
||||
#[derive(Serialize)]
|
||||
enum E {
|
||||
@@ -484,6 +460,11 @@ fn test_from_value_with_options() -> Result<(), Box<dyn std::error::Error>> {
|
||||
Err(err) => panic!("expected `DeserializeError` error, got {:?}", err),
|
||||
};
|
||||
|
||||
// Check recursion when using `Serialize` impl
|
||||
let t = lua.create_table()?;
|
||||
t.set("t", t.clone())?;
|
||||
assert!(serde_json::to_string(&t).is_err());
|
||||
|
||||
// Serialize Lua globals table
|
||||
#[derive(Debug, Deserialize)]
|
||||
struct Globals {
|
||||
|
||||
+15
-10
@@ -1,4 +1,4 @@
|
||||
use mlua::{Lua, Nil, Result, Table, TableExt, Value};
|
||||
use mlua::{Error, Lua, Nil, Result, Table, TableExt, Value};
|
||||
|
||||
#[test]
|
||||
fn test_set_get() -> Result<()> {
|
||||
@@ -139,14 +139,6 @@ fn test_table_sequence_from() -> Result<()> {
|
||||
vec![1, 2, 3]
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
get_table
|
||||
.call::<_, Table>(&[1, 2, 3])?
|
||||
.sequence_values()
|
||||
.collect::<Result<Vec<i64>>>()?,
|
||||
vec![1, 2, 3]
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -274,7 +266,12 @@ fn test_table_call() -> Result<()> {
|
||||
|
||||
lua.load(
|
||||
r#"
|
||||
table = {a = 1}
|
||||
table = {a = 1, b = 2}
|
||||
setmetatable(table, {
|
||||
__call = function(t, key)
|
||||
return "call_"..t[key]
|
||||
end
|
||||
})
|
||||
|
||||
function table.func(key)
|
||||
return "func_"..key
|
||||
@@ -289,11 +286,19 @@ fn test_table_call() -> Result<()> {
|
||||
|
||||
let table: Table = lua.globals().get("table")?;
|
||||
|
||||
assert_eq!(table.call::<_, String>("b")?, "call_2");
|
||||
assert_eq!(table.call_function::<_, _, String>("func", "a")?, "func_a");
|
||||
assert_eq!(
|
||||
table.call_method::<_, _, String>("method", "a")?,
|
||||
"method_1"
|
||||
);
|
||||
|
||||
// Test calling non-callable table
|
||||
let table2 = lua.create_table()?;
|
||||
assert!(matches!(
|
||||
table2.call::<_, ()>(()),
|
||||
Err(Error::RuntimeError(_))
|
||||
));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
+178
-4
@@ -800,6 +800,17 @@ fn test_drop_registry_value() -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_replace_registry_value() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let key = lua.create_registry_value::<i32>(42)?;
|
||||
lua.replace_registry_value(&key, "new value")?;
|
||||
assert_eq!(lua.registry_value::<String>(&key)?, "new value");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_lua_registry_hash() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
@@ -847,6 +858,37 @@ fn test_mismatched_registry_key() -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_application_data() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
lua.set_app_data("test1");
|
||||
lua.set_app_data(vec!["test2"]);
|
||||
|
||||
let f = lua.create_function(|lua, ()| {
|
||||
{
|
||||
let data1 = lua.app_data_ref::<&str>().unwrap();
|
||||
assert_eq!(*data1, "test1");
|
||||
}
|
||||
let mut data2 = lua.app_data_mut::<Vec<&str>>().unwrap();
|
||||
assert_eq!(*data2, vec!["test2"]);
|
||||
data2.push("test3");
|
||||
Ok(())
|
||||
})?;
|
||||
f.call(())?;
|
||||
|
||||
assert_eq!(*lua.app_data_ref::<&str>().unwrap(), "test1");
|
||||
assert_eq!(
|
||||
*lua.app_data_ref::<Vec<&str>>().unwrap(),
|
||||
vec!["test2", "test3"]
|
||||
);
|
||||
|
||||
lua.remove_app_data::<Vec<&str>>();
|
||||
assert!(matches!(lua.app_data_ref::<Vec<&str>>(), None));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_recursion() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
@@ -1046,17 +1088,22 @@ fn test_context_thread() -> Result<()> {
|
||||
)
|
||||
.into_function()?;
|
||||
|
||||
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
|
||||
#[cfg(any(
|
||||
feature = "lua54",
|
||||
feature = "lua53",
|
||||
feature = "lua52",
|
||||
feature = "luajit52"
|
||||
))]
|
||||
f.call::<_, ()>(lua.current_thread())?;
|
||||
|
||||
#[cfg(any(feature = "lua51", feature = "luajit"))]
|
||||
#[cfg(any(feature = "lua51", all(feature = "luajit", not(feature = "luajit52"))))]
|
||||
f.call::<_, ()>(Nil)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "lua51", feature = "luajit"))]
|
||||
#[cfg(any(feature = "lua51", all(feature = "luajit", not(feature = "luajit52"))))]
|
||||
fn test_context_thread_51() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
@@ -1105,12 +1152,139 @@ fn test_load_from_function() -> Result<()> {
|
||||
assert_eq!(t.get::<_, String>("__name")?, "my_module");
|
||||
assert_eq!(i.load(Ordering::Relaxed), 1);
|
||||
|
||||
let _: Value = lua.load_from_function("my_module", func)?;
|
||||
let _: Value = lua.load_from_function("my_module", func.clone())?;
|
||||
assert_eq!(i.load(Ordering::Relaxed), 1);
|
||||
|
||||
let func_nil = lua.create_function(move |_, _: String| Ok(Value::Nil))?;
|
||||
let v: Value = lua.load_from_function("my_module2", func_nil)?;
|
||||
assert_eq!(v, Value::Boolean(true));
|
||||
|
||||
// Test unloading and loading again
|
||||
lua.unload("my_module")?;
|
||||
let _: Value = lua.load_from_function("my_module", func)?;
|
||||
assert_eq!(i.load(Ordering::Relaxed), 2);
|
||||
|
||||
// Unloading nonexistent module must not fail
|
||||
lua.unload("my_module2")?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_inspect_stack() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
// Not inside any function
|
||||
assert!(lua.inspect_stack(0).is_none());
|
||||
|
||||
let logline = lua.create_function(|lua, msg: StdString| {
|
||||
let debug = lua.inspect_stack(1).unwrap(); // caller
|
||||
let source = debug.source().short_src.map(core::str::from_utf8);
|
||||
let source = source.transpose().unwrap().unwrap_or("?");
|
||||
let line = debug.curr_line();
|
||||
Ok(format!("{}:{} {}", source, line, msg))
|
||||
})?;
|
||||
lua.globals().set("logline", logline)?;
|
||||
|
||||
lua.load(
|
||||
r#"
|
||||
local function foo()
|
||||
local line = logline("hello")
|
||||
return line
|
||||
end
|
||||
local function bar()
|
||||
return foo()
|
||||
end
|
||||
|
||||
assert(foo() == '[string "chunk"]:3 hello')
|
||||
assert(bar() == '[string "chunk"]:3 hello')
|
||||
assert(logline("world") == '[string "chunk"]:12 world')
|
||||
"#,
|
||||
)
|
||||
.set_name("chunk")?
|
||||
.exec()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_multi_states() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let f = lua.create_function(|_, g: Option<Function>| {
|
||||
if let Some(g) = g {
|
||||
g.call(())?;
|
||||
}
|
||||
Ok(())
|
||||
})?;
|
||||
lua.globals().set("f", f)?;
|
||||
|
||||
lua.load("f(function() coroutine.wrap(function() f() end)() end)")
|
||||
.exec()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "lua54")]
|
||||
fn test_warnings() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
lua.set_app_data::<Vec<(StdString, bool)>>(Vec::new());
|
||||
|
||||
lua.set_warning_function(|lua, msg, tocont| {
|
||||
let msg = msg.to_string_lossy().to_string();
|
||||
lua.app_data_mut::<Vec<(StdString, bool)>>()
|
||||
.unwrap()
|
||||
.push((msg, tocont));
|
||||
Ok(())
|
||||
});
|
||||
|
||||
lua.warning("native warning ...", true)?;
|
||||
lua.warning("finish", false)?;
|
||||
lua.load(r#"warn("lua warning", "continue")"#).exec()?;
|
||||
|
||||
lua.remove_warning_function();
|
||||
lua.warning("one more warning", false)?;
|
||||
|
||||
let messages = lua.app_data_ref::<Vec<(StdString, bool)>>().unwrap();
|
||||
assert_eq!(
|
||||
*messages,
|
||||
vec![
|
||||
("native warning ...".to_string(), true),
|
||||
("finish".to_string(), false),
|
||||
("lua warning".to_string(), true),
|
||||
("continue".to_string(), false),
|
||||
]
|
||||
);
|
||||
|
||||
// Trigger error inside warning
|
||||
lua.set_warning_function(|_, _, _| Err(Error::RuntimeError("warning error".to_string())));
|
||||
assert!(matches!(
|
||||
lua.load(r#"warn("test")"#).exec(),
|
||||
Err(Error::CallbackError { cause, .. })
|
||||
if matches!(*cause, Error::RuntimeError(ref err) if err == "warning error")
|
||||
));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "luajit")]
|
||||
#[should_panic]
|
||||
fn test_luajit_cdata() {
|
||||
let lua = unsafe { Lua::unsafe_new() };
|
||||
let _v: Result<Value> = lua
|
||||
.load(
|
||||
r#"
|
||||
local ffi = require("ffi")
|
||||
ffi.cdef[[
|
||||
void *malloc(size_t size);
|
||||
void free(void *ptr);
|
||||
]]
|
||||
local ptr = ffi.C.malloc(1)
|
||||
ffi.C.free(ptr)
|
||||
return ptr
|
||||
"#,
|
||||
)
|
||||
.eval();
|
||||
}
|
||||
|
||||
+9
-1
@@ -130,7 +130,15 @@ fn test_thread_reset() -> Result<()> {
|
||||
assert_eq!(thread.status(), ThreadStatus::Error);
|
||||
assert_eq!(Arc::strong_count(&arc), 2);
|
||||
assert!(thread.reset(func.clone()).is_err());
|
||||
assert_eq!(thread.status(), ThreadStatus::Error);
|
||||
// Reset behavior has changed in Lua v5.4.4
|
||||
// It's became possible to force reset thread by popping error object
|
||||
assert!(matches!(
|
||||
thread.status(),
|
||||
ThreadStatus::Unresumable | ThreadStatus::Error
|
||||
));
|
||||
// Would pass in 5.4.4
|
||||
// assert!(thread.reset(func.clone()).is_ok());
|
||||
// assert_eq!(thread.status(), ThreadStatus::Resumable);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
||||
+149
-36
@@ -36,6 +36,7 @@ fn test_user_data() -> Result<()> {
|
||||
|
||||
#[test]
|
||||
fn test_methods() -> Result<()> {
|
||||
#[cfg_attr(feature = "serialize", derive(serde::Serialize))]
|
||||
struct MyUserData(i64);
|
||||
|
||||
impl UserData for MyUserData {
|
||||
@@ -48,29 +49,38 @@ fn test_methods() -> Result<()> {
|
||||
}
|
||||
}
|
||||
|
||||
let lua = Lua::new();
|
||||
let globals = lua.globals();
|
||||
let userdata = lua.create_userdata(MyUserData(42))?;
|
||||
globals.set("userdata", userdata.clone())?;
|
||||
lua.load(
|
||||
r#"
|
||||
function get_it()
|
||||
return userdata:get_value()
|
||||
end
|
||||
fn check_methods(lua: &Lua, userdata: AnyUserData) -> Result<()> {
|
||||
let globals = lua.globals();
|
||||
globals.set("userdata", userdata.clone())?;
|
||||
lua.load(
|
||||
r#"
|
||||
function get_it()
|
||||
return userdata:get_value()
|
||||
end
|
||||
|
||||
function set_it(i)
|
||||
return userdata:set_value(i)
|
||||
end
|
||||
"#,
|
||||
)
|
||||
.exec()?;
|
||||
let get = globals.get::<_, Function>("get_it")?;
|
||||
let set = globals.get::<_, Function>("set_it")?;
|
||||
assert_eq!(get.call::<_, i64>(())?, 42);
|
||||
userdata.borrow_mut::<MyUserData>()?.0 = 64;
|
||||
assert_eq!(get.call::<_, i64>(())?, 64);
|
||||
set.call::<_, ()>(100)?;
|
||||
assert_eq!(get.call::<_, i64>(())?, 100);
|
||||
function set_it(i)
|
||||
return userdata:set_value(i)
|
||||
end
|
||||
"#,
|
||||
)
|
||||
.exec()?;
|
||||
let get = globals.get::<_, Function>("get_it")?;
|
||||
let set = globals.get::<_, Function>("set_it")?;
|
||||
assert_eq!(get.call::<_, i64>(())?, 42);
|
||||
userdata.borrow_mut::<MyUserData>()?.0 = 64;
|
||||
assert_eq!(get.call::<_, i64>(())?, 64);
|
||||
set.call::<_, ()>(100)?;
|
||||
assert_eq!(get.call::<_, i64>(())?, 100);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
let lua = Lua::new();
|
||||
|
||||
check_methods(&lua, lua.create_userdata(MyUserData(42))?)?;
|
||||
|
||||
// Additionally check serializable userdata
|
||||
#[cfg(feature = "serialize")]
|
||||
check_methods(&lua, lua.create_ser_userdata(MyUserData(42))?)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -101,7 +111,12 @@ fn test_metamethods() -> Result<()> {
|
||||
Err("no such custom index".to_lua_err())
|
||||
}
|
||||
});
|
||||
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
|
||||
#[cfg(any(
|
||||
feature = "lua54",
|
||||
feature = "lua53",
|
||||
feature = "lua52",
|
||||
feature = "luajit52"
|
||||
))]
|
||||
methods.add_meta_method(MetaMethod::Pairs, |lua, data, ()| {
|
||||
use std::iter::FromIterator;
|
||||
let stateless_iter = lua.create_function(|_, (data, i): (MyUserData, i64)| {
|
||||
@@ -126,11 +141,16 @@ fn test_metamethods() -> Result<()> {
|
||||
10
|
||||
);
|
||||
|
||||
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
|
||||
let pairs_it = {
|
||||
lua.load(
|
||||
#[cfg(any(
|
||||
feature = "lua54",
|
||||
feature = "lua53",
|
||||
feature = "lua52",
|
||||
feature = "luajit52"
|
||||
))]
|
||||
let pairs_it = lua
|
||||
.load(
|
||||
r#"
|
||||
function pairs_it()
|
||||
function()
|
||||
local r = 0
|
||||
for i, v in pairs(userdata1) do
|
||||
r = r + v
|
||||
@@ -139,17 +159,21 @@ fn test_metamethods() -> Result<()> {
|
||||
end
|
||||
"#,
|
||||
)
|
||||
.exec()?;
|
||||
globals.get::<_, Function>("pairs_it")?
|
||||
};
|
||||
.eval::<Function>()?;
|
||||
|
||||
assert_eq!(lua.load("userdata1 - userdata2").eval::<MyUserData>()?.0, 4);
|
||||
assert_eq!(lua.load("userdata1:get()").eval::<i64>()?, 7);
|
||||
assert_eq!(lua.load("userdata2.inner").eval::<i64>()?, 3);
|
||||
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
|
||||
assert_eq!(pairs_it.call::<_, i64>(())?, 28);
|
||||
assert!(lua.load("userdata2.nonexist_field").eval::<()>().is_err());
|
||||
|
||||
#[cfg(any(
|
||||
feature = "lua54",
|
||||
feature = "lua53",
|
||||
feature = "lua52",
|
||||
feature = "luajit52"
|
||||
))]
|
||||
assert_eq!(pairs_it.call::<_, i64>(())?, 28);
|
||||
|
||||
let userdata2: Value = globals.get("userdata2")?;
|
||||
let userdata3: Value = globals.get("userdata3")?;
|
||||
|
||||
@@ -252,6 +276,79 @@ fn test_gc_userdata() -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_userdata_take() -> Result<()> {
|
||||
#[derive(Debug)]
|
||||
struct MyUserdata(Arc<i64>);
|
||||
|
||||
impl UserData for MyUserdata {
|
||||
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
|
||||
methods.add_method("num", |_, this, ()| Ok(*this.0))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
impl serde::Serialize for MyUserdata {
|
||||
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
|
||||
where
|
||||
S: serde::Serializer,
|
||||
{
|
||||
serializer.serialize_i64(*self.0)
|
||||
}
|
||||
}
|
||||
|
||||
fn check_userdata_take(lua: &Lua, userdata: AnyUserData, rc: Arc<i64>) -> Result<()> {
|
||||
lua.globals().set("userdata", userdata.clone())?;
|
||||
assert_eq!(Arc::strong_count(&rc), 3);
|
||||
let userdata_copy = userdata.clone();
|
||||
{
|
||||
let _value = userdata.borrow::<MyUserdata>()?;
|
||||
// We should not be able to take userdata if it's borrowed
|
||||
match userdata_copy.take::<MyUserdata>() {
|
||||
Err(Error::UserDataBorrowMutError) => {}
|
||||
r => panic!("expected `UserDataBorrowMutError` error, got {:?}", r),
|
||||
}
|
||||
}
|
||||
|
||||
let value = userdata_copy.take::<MyUserdata>()?;
|
||||
assert_eq!(*value.0, 18);
|
||||
drop(value);
|
||||
lua.gc_collect()?;
|
||||
assert_eq!(Arc::strong_count(&rc), 1);
|
||||
|
||||
match userdata.borrow::<MyUserdata>() {
|
||||
Err(Error::UserDataDestructed) => {}
|
||||
r => panic!("expected `UserDataDestructed` error, got {:?}", r),
|
||||
}
|
||||
match lua.load("userdata:num()").exec() {
|
||||
Err(Error::CallbackError { ref cause, .. }) => match cause.as_ref() {
|
||||
Error::CallbackDestructed => {}
|
||||
err => panic!("expected `CallbackDestructed`, got {:?}", err),
|
||||
},
|
||||
r => panic!("improper return for destructed userdata: {:?}", r),
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
let lua = Lua::new();
|
||||
|
||||
let rc = Arc::new(18);
|
||||
let userdata = lua.create_userdata(MyUserdata(rc.clone()))?;
|
||||
userdata.set_nth_user_value(2, MyUserdata(rc.clone()))?;
|
||||
check_userdata_take(&lua, userdata, rc)?;
|
||||
|
||||
// Additionally check serializable userdata
|
||||
#[cfg(feature = "serialize")]
|
||||
{
|
||||
let rc = Arc::new(18);
|
||||
let userdata = lua.create_ser_userdata(MyUserdata(rc.clone()))?;
|
||||
userdata.set_nth_user_value(2, MyUserdata(rc.clone()))?;
|
||||
check_userdata_take(&lua, userdata, rc)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_destroy_userdata() -> Result<()> {
|
||||
struct MyUserdata(Arc<()>);
|
||||
@@ -275,16 +372,32 @@ fn test_destroy_userdata() -> Result<()> {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_user_value() -> Result<()> {
|
||||
fn test_user_values() -> Result<()> {
|
||||
struct MyUserData;
|
||||
|
||||
impl UserData for MyUserData {}
|
||||
|
||||
let lua = Lua::new();
|
||||
let ud = lua.create_userdata(MyUserData)?;
|
||||
ud.set_user_value("hello")?;
|
||||
assert_eq!(ud.get_user_value::<String>()?, "hello");
|
||||
assert!(ud.get_user_value::<u32>().is_err());
|
||||
|
||||
ud.set_nth_user_value(1, "hello")?;
|
||||
ud.set_nth_user_value(2, "world")?;
|
||||
ud.set_nth_user_value(65535, 321)?;
|
||||
assert_eq!(ud.get_nth_user_value::<String>(1)?, "hello");
|
||||
assert_eq!(ud.get_nth_user_value::<String>(2)?, "world");
|
||||
assert_eq!(ud.get_nth_user_value::<Value>(3)?, Value::Nil);
|
||||
assert_eq!(ud.get_nth_user_value::<i32>(65535)?, 321);
|
||||
|
||||
assert!(ud.get_nth_user_value::<Value>(0).is_err());
|
||||
assert!(ud.get_nth_user_value::<Value>(65536).is_err());
|
||||
|
||||
// Named user values
|
||||
ud.set_named_user_value("name", "alex")?;
|
||||
ud.set_named_user_value("age", 10)?;
|
||||
|
||||
assert_eq!(ud.get_named_user_value::<_, String>("name")?, "alex");
|
||||
assert_eq!(ud.get_named_user_value::<_, i32>("age")?, 10);
|
||||
assert_eq!(ud.get_named_user_value::<_, Value>("nonexist")?, Value::Nil);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user