mirror of
https://github.com/mlua-rs/mlua
synced 2026-06-08 16:05:43 +00:00
Compare commits
64 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| cb1ac28f2a | |||
| 3e7f25670a | |||
| 0d404ce4c3 | |||
| e26cec5db9 | |||
| 0bd36b42e7 | |||
| e0da6ac929 | |||
| 0c7db4916c | |||
| b9589491e4 | |||
| 58cb371f06 | |||
| 8add60b019 | |||
| c363fb9288 | |||
| 3900e23839 | |||
| 726fde7e1f | |||
| 7cb9c4f39c | |||
| b93ace0224 | |||
| 5f37bf812d | |||
| 1f7e760d20 | |||
| 90bea4aa34 | |||
| 7775b4a99c | |||
| 1d9cda10eb | |||
| 7332c6a28c | |||
| 94670e3fdb | |||
| 335f433df4 | |||
| 2aed548747 | |||
| 6a77b5f003 | |||
| aeb66115f7 | |||
| ce873a40bf | |||
| 8de75d1c18 | |||
| b6ff501b8c | |||
| 0e73ae18f4 | |||
| e62fd400d7 | |||
| 1c79f646de | |||
| 7f5fd36a2b | |||
| faf19e4a06 | |||
| 24d9099ef7 | |||
| 84003f31e7 | |||
| e0d9ec41e2 | |||
| 618874ef3c | |||
| 9f82cbe0c5 | |||
| 1a81f8d447 | |||
| bedd430eb5 | |||
| afc41ab23c | |||
| c5d0ccc433 | |||
| ce8955f5b9 | |||
| 368c4428eb | |||
| 6e2b687cb7 | |||
| 661f8e592a | |||
| c8a7aa947c | |||
| a52135b62d | |||
| 68378a3957 | |||
| c65058a40a | |||
| 93d63cef35 | |||
| b743245aba | |||
| a172c021c9 | |||
| de2c5cd9a9 | |||
| d201beadc9 | |||
| dd58cdad52 | |||
| 5c8a5e0a5a | |||
| e07c53eafe | |||
| ad619390e1 | |||
| 350602ab6e | |||
| 4b1bc88273 | |||
| 883bf082b9 | |||
| 4265250cfd |
@@ -0,0 +1,23 @@
|
||||
name: coverage
|
||||
on: [push]
|
||||
|
||||
jobs:
|
||||
test:
|
||||
name: coverage
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: xd009642/tarpaulin
|
||||
options: --security-opt seccomp=unconfined
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Generate code coverage
|
||||
run: |
|
||||
cargo tarpaulin --verbose --features lua53,vendored,async,send,serialize --out xml --exclude-files benches --exclude-files tests --exclude-files build --exclude-files src/ffi
|
||||
|
||||
- name: Upload to codecov.io
|
||||
uses: codecov/codecov-action@v1
|
||||
with:
|
||||
token: ${{secrets.CODECOV_TOKEN}}
|
||||
fail_ci_if_error: false
|
||||
+76
-40
@@ -27,15 +27,78 @@ jobs:
|
||||
- name: Build ${{ matrix.lua }} vendored
|
||||
run: |
|
||||
cargo build --release --features "${{ matrix.lua }} vendored"
|
||||
cargo build --release --features "${{ matrix.lua }} vendored async send"
|
||||
cargo build --release --features "${{ matrix.lua }} vendored async send serialize"
|
||||
shell: bash
|
||||
- name: Build ${{ matrix.lua }} pkg-config
|
||||
if: ${{ matrix.os == 'ubuntu-18.04' && matrix.lua != 'lua54' }}
|
||||
run: |
|
||||
sudo apt-get update -y
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y --no-install-recommends liblua5.3-dev liblua5.2-dev liblua5.1-0-dev libluajit-5.1-dev
|
||||
cargo build --release --features "${{ matrix.lua }}"
|
||||
|
||||
build_aarch64_cross_macos:
|
||||
name: Cross-compile to aarch64-apple-darwin
|
||||
runs-on: macos-11.0
|
||||
needs: build
|
||||
strategy:
|
||||
matrix:
|
||||
lua: [lua54, lua53, lua52, lua51, luajit]
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: nightly
|
||||
target: aarch64-apple-darwin
|
||||
override: true
|
||||
- name: Cross-compile
|
||||
run: cargo build --target aarch64-apple-darwin --features "${{ matrix.lua }} async send serialize vendored"
|
||||
|
||||
build_aarch64_cross_ubuntu:
|
||||
name: Cross-compile to aarch64-unknown-linux-gnu
|
||||
runs-on: ubuntu-18.04
|
||||
needs: build
|
||||
strategy:
|
||||
matrix:
|
||||
lua: [lua54, lua53, lua52, lua51, luajit]
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: nightly
|
||||
target: aarch64-unknown-linux-gnu
|
||||
override: true
|
||||
- name: Install ARM compiler toolchain
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y --no-install-recommends gcc-aarch64-linux-gnu libc6-dev-arm64-cross
|
||||
shell: bash
|
||||
- name: Cross-compile
|
||||
run: cargo build --target aarch64-unknown-linux-gnu --features "${{ matrix.lua }} async send serialize vendored"
|
||||
shell: bash
|
||||
|
||||
build_armv7_cross_ubuntu:
|
||||
name: Cross-compile to armv7-unknown-linux-gnueabihf
|
||||
runs-on: ubuntu-18.04
|
||||
needs: build
|
||||
strategy:
|
||||
matrix:
|
||||
lua: [lua54, lua53, lua52, lua51]
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: nightly
|
||||
target: armv7-unknown-linux-gnueabihf
|
||||
override: true
|
||||
- name: Install ARM compiler toolchain
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y --no-install-recommends gcc-arm-linux-gnueabihf libc-dev-armhf-cross
|
||||
shell: bash
|
||||
- name: Cross-compile
|
||||
run: cargo build --target armv7-unknown-linux-gnueabihf --features "${{ matrix.lua }} async send serialize vendored"
|
||||
shell: bash
|
||||
|
||||
test:
|
||||
name: Test
|
||||
runs-on: ${{ matrix.os }}
|
||||
@@ -60,41 +123,19 @@ jobs:
|
||||
target: ${{ matrix.target }}
|
||||
override: true
|
||||
- name: Run ${{ matrix.lua }} tests
|
||||
if: ${{ matrix.os != 'macos-latest' || matrix.lua != 'luajit' }}
|
||||
run: |
|
||||
cargo test --release --features "${{ matrix.lua }} vendored"
|
||||
cargo test --release --features "${{ matrix.lua }} vendored async send"
|
||||
cargo test --release --features "${{ matrix.lua }} vendored async send serialize"
|
||||
shell: bash
|
||||
- name: Run compile tests
|
||||
if: ${{ matrix.os == 'ubuntu-18.04' && matrix.lua == 'lua53' }}
|
||||
- name: Run compile tests (macos lua53)
|
||||
if: ${{ matrix.os == 'macos-latest' && matrix.lua == 'lua53' }}
|
||||
run: |
|
||||
cargo test --release --features "${{ matrix.lua }} vendored" -- --ignored
|
||||
cargo test --release --features "${{ matrix.lua }} vendored async send" -- --ignored
|
||||
shell: bash
|
||||
|
||||
test_luajit_macos:
|
||||
name: Test LuaJIT on macOS
|
||||
runs-on: macos-latest
|
||||
needs: build
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: nightly
|
||||
target: x86_64-apple-darwin
|
||||
override: true
|
||||
- name: Run LuaJIT 2.0.5 tests
|
||||
run: |
|
||||
brew install luajit pkg-config
|
||||
cargo test --tests --release --features "luajit async send" -- --test-threads=1
|
||||
shell: bash
|
||||
- name: Run LuaJIT vendored tests
|
||||
run: |
|
||||
cargo test --release --features "luajit vendored async send"
|
||||
TRYBUILD=overwrite cargo test --release --features "${{ matrix.lua }} vendored" -- --ignored
|
||||
TRYBUILD=overwrite cargo test --release --features "${{ matrix.lua }} vendored async send serialize" -- --ignored
|
||||
shell: bash
|
||||
|
||||
test_modules:
|
||||
name: Test modules on Linux and macOS
|
||||
name: Test modules
|
||||
runs-on: ${{ matrix.os }}
|
||||
needs: build
|
||||
strategy:
|
||||
@@ -126,21 +167,16 @@ jobs:
|
||||
needs: build
|
||||
strategy:
|
||||
matrix:
|
||||
lua: [lua53, lua51, luajit]
|
||||
lua: [lua53, luajit]
|
||||
defaults:
|
||||
run:
|
||||
shell: bash.exe --login -eo pipefail "{0}"
|
||||
env:
|
||||
MSYSTEM: MINGW64
|
||||
CHERE_INVOKING: 1
|
||||
shell: msys2 {0}
|
||||
steps:
|
||||
- uses: msys2/setup-msys2@v2
|
||||
- uses: actions/checkout@v2
|
||||
- name: Set up shell
|
||||
run: echo ::add-path::C:\msys64\usr\bin\
|
||||
shell: pwsh
|
||||
- name: Install Rust & Lua
|
||||
run: |
|
||||
pacman -S --noconfirm mingw-w64-x86_64-rust mingw-w64-x86_64-lua mingw-w64-x86_64-lua51 mingw-w64-x86_64-luajit
|
||||
pacman -S --noconfirm mingw-w64-x86_64-rust mingw-w64-x86_64-lua mingw-w64-x86_64-luajit mingw-w64-x86_64-pkg-config
|
||||
- name: Run ${{ matrix.lua }} module tests
|
||||
run: |
|
||||
(cd examples/module && cargo build --release --features "${{ matrix.lua }}")
|
||||
@@ -174,4 +210,4 @@ jobs:
|
||||
- uses: actions-rs/clippy-check@v1
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
args: --features "${{ matrix.lua }},async,send,vendored
|
||||
args: --features "${{ matrix.lua }},vendored,async,send,serialize
|
||||
|
||||
@@ -1,3 +1,39 @@
|
||||
## v0.5.4
|
||||
|
||||
- Build script improvements
|
||||
- Improvements in panic handling (resume panic on value popping)
|
||||
- Fixed bug serializing 3rd party userdata (causes segfault)
|
||||
- Make error::Error non exhaustive
|
||||
|
||||
## v0.5.3
|
||||
|
||||
- Fixed bug when returning nil-prefixed multi values from async function (+ test)
|
||||
- Performance optimisation for async callbacks (polling)
|
||||
|
||||
## v0.5.2
|
||||
|
||||
- Some performance optimisations (callbacks)
|
||||
- `ToLua` implementation for `Cow<str>` and `Cow<CStr>`
|
||||
- Fixed bug with `Scope` destruction of partially polled futures
|
||||
|
||||
## v0.5.1
|
||||
|
||||
- Support cross compilation that should work well for vendored builds (including LuaJIT with some restrictions)
|
||||
- Fix numeric types conversion for 32bit Lua
|
||||
- Update tokio to 1.0 for async examples
|
||||
|
||||
## v0.5.0
|
||||
|
||||
- Serde support under `serialize` feature flag.
|
||||
- Re-export `mlua_derive`.
|
||||
- impl `ToLua` and `FromLua` for `HashSet` and `BTreeSet`
|
||||
|
||||
## v0.4.2
|
||||
|
||||
- Added `Function::dump()` to dump lua function to a binary chunk
|
||||
- Added `ChunkMode` enum to mark chunks as text or binary
|
||||
- Updated `set_memory_limit` doc
|
||||
|
||||
## v0.4.0
|
||||
|
||||
- Lua 5.4 support with `MetaMethod::Close`.
|
||||
|
||||
+28
-16
@@ -1,27 +1,24 @@
|
||||
[package]
|
||||
name = "mlua"
|
||||
version = "0.4.0"
|
||||
version = "0.5.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"
|
||||
documentation = "https://docs.rs/mlua"
|
||||
readme = "README.md"
|
||||
keywords = ["lua", "luajit", "async", "futures"]
|
||||
keywords = ["lua", "luajit", "async", "futures", "scripting"]
|
||||
categories = ["api-bindings", "asynchronous"]
|
||||
license = "MIT"
|
||||
links = "lua"
|
||||
build = "build/main.rs"
|
||||
description = """
|
||||
High level bindings to Lua 5.1/5.2/5.3/5.4 (including LuaJIT)
|
||||
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.
|
||||
"""
|
||||
|
||||
[badges]
|
||||
# github-actions = { repository = "khvzak/mlua", workflow = "CI" }
|
||||
maintenance = { status = "actively-developed" }
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
features = ["async"]
|
||||
features = ["lua53", "async", "send", "serialize"]
|
||||
rustdoc-args = ["--cfg", "docsrs"]
|
||||
|
||||
[workspace]
|
||||
members = [
|
||||
@@ -37,41 +34,52 @@ lua52 = []
|
||||
lua51 = []
|
||||
luajit = []
|
||||
vendored = ["lua-src", "luajit-src"]
|
||||
module = []
|
||||
module = ["mlua_derive"]
|
||||
async = ["futures-core", "futures-task", "futures-util"]
|
||||
send = []
|
||||
serialize = ["serde", "erased-serde"]
|
||||
|
||||
[dependencies]
|
||||
mlua_derive = { version = "0.5", optional = true, path = "mlua_derive" }
|
||||
bstr = { version = "0.2", features = ["std"], default_features = false }
|
||||
lazy_static = { version = "1.4" }
|
||||
num-traits = { version = "0.2.11" }
|
||||
num-traits = { version = "0.2.14" }
|
||||
futures-core = { version = "0.3.5", optional = true }
|
||||
futures-task = { version = "0.3.5", optional = true }
|
||||
futures-util = { version = "0.3.5", optional = true }
|
||||
serde = { version = "1.0", optional = true }
|
||||
erased-serde = { version = "0.3", optional = true }
|
||||
|
||||
[build-dependencies]
|
||||
cc = { version = "1.0" }
|
||||
pkg-config = { version = "0.3.17" }
|
||||
lua-src = { version = "535.0.4", optional = true }
|
||||
luajit-src = { version = "210.1.0", optional = true }
|
||||
lua-src = { version = ">= 540.0.0, < 550.0.0", optional = true }
|
||||
luajit-src = { version = ">= 210.1.2, < 220.0.0", optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
rustyline = "6.0"
|
||||
criterion = "0.3"
|
||||
rustyline = "7.0"
|
||||
criterion = { version = "0.3.4", features = ["html_reports", "async_tokio"] }
|
||||
trybuild = "1.0"
|
||||
futures = "0.3.5"
|
||||
hyper = "0.13"
|
||||
tokio = { version = "0.2", features = ["full"] }
|
||||
hyper = { version = "0.14", features = ["client", "server"] }
|
||||
reqwest = { version = "0.11", features = ["json"] }
|
||||
tokio = { version = "1.0", features = ["full"] }
|
||||
futures-timer = "3.0"
|
||||
serde_json = "1.0"
|
||||
|
||||
[[bench]]
|
||||
name = "benchmark"
|
||||
harness = false
|
||||
required-features = ["async"]
|
||||
|
||||
[[example]]
|
||||
name = "async_http_client"
|
||||
required-features = ["async"]
|
||||
|
||||
[[example]]
|
||||
name = "async_http_reqwest"
|
||||
required-features = ["async", "serialize"]
|
||||
|
||||
[[example]]
|
||||
name = "async_http_server"
|
||||
required-features = ["async", "send"]
|
||||
@@ -79,3 +87,7 @@ required-features = ["async", "send"]
|
||||
[[example]]
|
||||
name = "async_tcp_server"
|
||||
required-features = ["async"]
|
||||
|
||||
[[example]]
|
||||
name = "serialize"
|
||||
required-features = ["serialize"]
|
||||
|
||||
@@ -3,7 +3,7 @@ below:
|
||||
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2019 A. Orlenko
|
||||
Copyright (c) 2019-2021 A. Orlenko
|
||||
Copyright (c) 2017 rlua
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# mlua
|
||||
[![Build Status]][github-actions] [![Latest Version]][crates.io] [![API Documentation]][docs.rs]
|
||||
[![Build Status]][github-actions] [![Latest Version]][crates.io] [![API Documentation]][docs.rs] [![Coverage Status]][codecov.io]
|
||||
|
||||
[Build Status]: https://github.com/khvzak/mlua/workflows/CI/badge.svg
|
||||
[github-actions]: https://github.com/khvzak/mlua/actions
|
||||
@@ -7,35 +7,70 @@
|
||||
[crates.io]: https://crates.io/crates/mlua
|
||||
[API Documentation]: https://docs.rs/mlua/badge.svg
|
||||
[docs.rs]: https://docs.rs/mlua
|
||||
[Coverage Status]: https://codecov.io/gh/khvzak/mlua/branch/master/graph/badge.svg?token=99339FS1CG
|
||||
[codecov.io]: https://codecov.io/gh/khvzak/mlua
|
||||
|
||||
[Guided Tour](examples/guided_tour.rs)
|
||||
|
||||
`mlua` is bindings to [Lua](https://www.lua.org) programming language for Rust with a goal to provide
|
||||
_safe_ (as far as it's possible), high level, easy to use, practical and flexible API.
|
||||
|
||||
Started as [rlua v0.15](https://github.com/amethyst/rlua/tree/0.15.3) fork, `mlua` supports *__all__* major Lua versions (including LuaJIT) and allows to write native Lua modules in Rust as well as use Lua in a standalone mode.
|
||||
Started as [rlua](https://github.com/amethyst/rlua/tree/0.15.3) fork, `mlua` supports Lua 5.4, 5.3, 5.2 and 5.1 including LuaJIT (2.0.5 and 2.1 beta) and allows to write native Lua modules in Rust as well as use Lua in a standalone mode.
|
||||
|
||||
`mlua` supports the following Lua versions (and tested on Windows/macOS/Linux):
|
||||
- Lua 5.4 (`feature = "lua54"`)
|
||||
- Lua 5.3 (`feature = "lua53"`)
|
||||
- Lua 5.2 (`feature = "lua52"`)
|
||||
- Lua 5.1 (`feature = "lua51"`)
|
||||
- LuaJIT 2.1.0 beta (`feature = "luajit"`)
|
||||
- LuaJIT 2.0.5 stable (`feature = "luajit"`)
|
||||
`mlua` tested on Windows/macOS/Linux including module mode in [GitHub Actions] on `x86_64` platform and cross-compilation to `aarch64` (other targes are also supported).
|
||||
|
||||
Additional `feature = "vendored"` enables building static Lua from sources during `mlua` compilation.
|
||||
[GitHub Actions]: https://github.com/khvzak/mlua/actions
|
||||
|
||||
## Usage
|
||||
|
||||
### Feature flags
|
||||
|
||||
`mlua` uses feature flags to reduce the amount of depenendies, compiled code and allow to choose only required set of features.
|
||||
Below is a list of the available feature flags. By default `mlua` does not enable any features.
|
||||
|
||||
* `lua54`: activate Lua [5.4] support
|
||||
* `lua53`: activate Lua [5.3] support
|
||||
* `lua52`: activate Lua [5.2] support
|
||||
* `lua51`: activate Lua [5.1] support
|
||||
* `luajit`: activate [LuaJIT] support
|
||||
* `vendored`: build static Lua(JIT) library from sources during `mlua` compilation using [lua-src] or [luajit-src] crates
|
||||
* `module`: enable module mode (building loadable `cdylib` library for Lua)
|
||||
* `async`: enable async/await support (any executor can be used, eg. [tokio] or [async-std])
|
||||
* `send`: make `mlua::Lua` transferable across thread boundaries (adds [`Send`] requirement to `mlua::Function` and `mlua::UserData`)
|
||||
* `serialize`: add serialization and deserialization support to `mlua` types usign [serde] framework
|
||||
|
||||
[5.4]: https://www.lua.org/manual/5.4/manual.html
|
||||
[5.3]: https://www.lua.org/manual/5.3/manual.html
|
||||
[5.2]: https://www.lua.org/manual/5.2/manual.html
|
||||
[5.1]: https://www.lua.org/manual/5.1/manual.html
|
||||
[LuaJIT]: https://luajit.org/
|
||||
[lua-src]: https://github.com/khvzak/lua-src-rs
|
||||
[luajit-src]: https://github.com/khvzak/luajit-src-rs
|
||||
[tokio]: https://github.com/tokio-rs/tokio
|
||||
[async-std]: https://github.com/async-rs/async-std
|
||||
[`Send`]: https://doc.rust-lang.org/std/marker/trait.Send.html
|
||||
[serde]: https://github.com/serde-rs/serde
|
||||
|
||||
### Async/await support
|
||||
|
||||
Starting from v0.3, `mlua` supports async/await for all Lua versions. This works using Lua [coroutines](https://www.lua.org/manual/5.3/manual.html#2.6) and require running [Thread](https://docs.rs/mlua/latest/mlua/struct.Thread.html) along with enabling `feature = "async"` in `Cargo.toml`.
|
||||
`mlua` supports async/await for all Lua versions. This works using Lua [coroutines](https://www.lua.org/manual/5.3/manual.html#2.6) and require running [Thread](https://docs.rs/mlua/latest/mlua/struct.Thread.html) along with enabling `feature = "async"` in `Cargo.toml`.
|
||||
|
||||
**Examples**:
|
||||
- [HTTP Client](examples/async_http_client.rs)
|
||||
- [HTTP Client (json)](examples/async_http_reqwest.rs)
|
||||
- [HTTP Server](examples/async_http_server.rs)
|
||||
- [TCP Server](examples/async_tcp_server.rs)
|
||||
|
||||
### Serialization (serde) support
|
||||
|
||||
With `serialize` feature flag enabled, `mlua` allows you to serialize/deserialize any type that implements [`serde::Serialize`] and [`serde::Deserialize`] into/from [`mlua::Value`]. In addition `mlua` provides [`serde::Serialize`] trait implementation for it (including `UserData` support).
|
||||
|
||||
[Example](examples/serialize.rs)
|
||||
|
||||
[`serde::Serialize`]: https://docs.serde.rs/serde/ser/trait.Serialize.html
|
||||
[`serde::Deserialize`]: https://docs.serde.rs/serde/de/trait.Deserialize.html
|
||||
[`mlua::Value`]: https://docs.rs/mlua/latest/mlua/enum.Value.html
|
||||
|
||||
### Compiling
|
||||
|
||||
You have to enable one of the features `lua54`, `lua53`, `lua52`, `lua51` or `luajit`, according to the choosen Lua version.
|
||||
@@ -55,11 +90,13 @@ my_project $ LUA_INC=$HOME/tmp/lua-5.2.4/src LUA_LIB=$HOME/tmp/lua-5.2.4/src LUA
|
||||
Just enable the `vendored` feature and cargo will automatically build and link specified lua/luajit version. This is the easiest way to get started with `mlua`.
|
||||
|
||||
### Standalone mode
|
||||
In a standalone mode `mlua` allows to add to your application scripting support with a gently configured Lua runtime to ensure safety and soundness.
|
||||
|
||||
Add to `Cargo.toml` :
|
||||
|
||||
``` toml
|
||||
[dependencies]
|
||||
mlua = { version = "0.4", features = ["lua53"] }
|
||||
mlua = { version = "0.5", features = ["lua53", "vendored"] }
|
||||
```
|
||||
|
||||
`main.rs`
|
||||
@@ -83,6 +120,7 @@ fn main() -> LuaResult<()> {
|
||||
```
|
||||
|
||||
### Module mode
|
||||
In a module mode `mlua` allows to create a compiled Lua module that can be loaded from Lua code using [`require`](https://www.lua.org/manual/5.3/manual.html#pdf-require). In this case `mlua` uses an external Lua runtime which could lead to potential unsafety due to unpredictability of the Lua environment and usage of libraries such as [`debug`](https://www.lua.org/manual/5.3/manual.html#6.10).
|
||||
|
||||
[Example](examples/module)
|
||||
|
||||
@@ -93,15 +131,12 @@ Add to `Cargo.toml` :
|
||||
crate-type = ["cdylib"]
|
||||
|
||||
[dependencies]
|
||||
mlua = { version = "0.4", features = ["lua53", "module"] }
|
||||
mlua_derive = "0.4"
|
||||
mlua = { version = "0.5", features = ["lua53", "vendored", "module"] }
|
||||
```
|
||||
|
||||
`lib.rs` :
|
||||
|
||||
``` rust
|
||||
#[macro_use]
|
||||
extern crate mlua_derive;
|
||||
use mlua::prelude::*;
|
||||
|
||||
fn hello(_: &Lua, name: String) -> LuaResult<()> {
|
||||
@@ -109,7 +144,7 @@ fn hello(_: &Lua, name: String) -> LuaResult<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[lua_module]
|
||||
#[mlua::lua_module]
|
||||
fn my_module(lua: &Lua) -> LuaResult<LuaTable> {
|
||||
let exports = lua.create_table()?;
|
||||
exports.set("hello", lua.create_function(hello)?)?;
|
||||
@@ -133,11 +168,17 @@ rustflags = [
|
||||
"-C", "link-arg=-undefined",
|
||||
"-C", "link-arg=dynamic_lookup",
|
||||
]
|
||||
|
||||
[target.aarch64-apple-darwin]
|
||||
rustflags = [
|
||||
"-C", "link-arg=-undefined",
|
||||
"-C", "link-arg=dynamic_lookup",
|
||||
]
|
||||
```
|
||||
On Linux you can build modules normally with `cargo build --release`.
|
||||
Vendored and non-vendored builds are supported for these OS.
|
||||
|
||||
On Windows `vendored` mode is not supported since you need to link to a Lua dll.
|
||||
On Windows `vendored` mode for modules is not supported since you need to link to a Lua dll.
|
||||
Easiest way is to use either MinGW64 (as part of [MSYS2](https://github.com/msys2/msys2) package) with `pkg-config` or
|
||||
MSVC with `LUA_INC` / `LUA_LIB` / `LUA_LIB_NAME` environment variables.
|
||||
|
||||
|
||||
+171
-110
@@ -1,22 +1,22 @@
|
||||
#![cfg_attr(
|
||||
all(feature = "luajit", target_os = "macos", target_arch = "x86_64"),
|
||||
feature(link_args)
|
||||
)]
|
||||
|
||||
#[cfg_attr(
|
||||
all(feature = "luajit", target_os = "macos", target_arch = "x86_64"),
|
||||
link_args = "-pagezero_size 10000 -image_base 100000000"
|
||||
)]
|
||||
extern "system" {}
|
||||
|
||||
use criterion::{criterion_group, criterion_main, BatchSize, Criterion};
|
||||
use std::time::Duration;
|
||||
use tokio::runtime::Runtime;
|
||||
use tokio::task;
|
||||
|
||||
use mlua::prelude::*;
|
||||
|
||||
fn collect_gc_twice(lua: &Lua) {
|
||||
lua.gc_collect().unwrap();
|
||||
lua.gc_collect().unwrap();
|
||||
}
|
||||
|
||||
fn create_table(c: &mut Criterion) {
|
||||
c.bench_function("create table", |b| {
|
||||
b.iter_batched_ref(
|
||||
|| Lua::new(),
|
||||
|lua| {
|
||||
let lua = Lua::new();
|
||||
|
||||
c.bench_function("create [table empty]", |b| {
|
||||
b.iter_batched(
|
||||
|| collect_gc_twice(&lua),
|
||||
|_| {
|
||||
lua.create_table().unwrap();
|
||||
},
|
||||
BatchSize::SmallInput,
|
||||
@@ -25,12 +25,14 @@ fn create_table(c: &mut Criterion) {
|
||||
}
|
||||
|
||||
fn create_array(c: &mut Criterion) {
|
||||
c.bench_function("create array 10", |b| {
|
||||
b.iter_batched_ref(
|
||||
|| Lua::new(),
|
||||
|lua| {
|
||||
let lua = Lua::new();
|
||||
|
||||
c.bench_function("create [array] 10", |b| {
|
||||
b.iter_batched(
|
||||
|| collect_gc_twice(&lua),
|
||||
|_| {
|
||||
let table = lua.create_table().unwrap();
|
||||
for i in 1..11 {
|
||||
for i in 1..=10 {
|
||||
table.set(i, i).unwrap();
|
||||
}
|
||||
},
|
||||
@@ -40,10 +42,12 @@ fn create_array(c: &mut Criterion) {
|
||||
}
|
||||
|
||||
fn create_string_table(c: &mut Criterion) {
|
||||
c.bench_function("create string table 10", |b| {
|
||||
b.iter_batched_ref(
|
||||
|| Lua::new(),
|
||||
|lua| {
|
||||
let lua = Lua::new();
|
||||
|
||||
c.bench_function("create [table string] 10", |b| {
|
||||
b.iter_batched(
|
||||
|| collect_gc_twice(&lua),
|
||||
|_| {
|
||||
let table = lua.create_table().unwrap();
|
||||
for &s in &["1", "2", "3", "4", "5", "6", "7", "8", "9", "10"] {
|
||||
let s = lua.create_string(s).unwrap();
|
||||
@@ -55,30 +59,20 @@ fn create_string_table(c: &mut Criterion) {
|
||||
});
|
||||
}
|
||||
|
||||
fn call_add_function(c: &mut Criterion) {
|
||||
c.bench_function("call add function 3 10", |b| {
|
||||
fn call_lua_function(c: &mut Criterion) {
|
||||
let lua = Lua::new();
|
||||
|
||||
c.bench_function("call Lua function [sum] 3 10", |b| {
|
||||
b.iter_batched_ref(
|
||||
|| {
|
||||
let lua = Lua::new();
|
||||
let f = {
|
||||
let f: LuaFunction = lua
|
||||
.load(
|
||||
r#"
|
||||
function(a, b, c)
|
||||
return a + b + c
|
||||
end
|
||||
"#,
|
||||
)
|
||||
.eval()
|
||||
.unwrap();
|
||||
lua.create_registry_value(f).unwrap()
|
||||
};
|
||||
(lua, f)
|
||||
collect_gc_twice(&lua);
|
||||
lua.load("function(a, b, c) return a + b + c end")
|
||||
.eval::<LuaFunction>()
|
||||
.unwrap()
|
||||
},
|
||||
|(lua, f)| {
|
||||
let add_function: LuaFunction = lua.registry_value(f).unwrap();
|
||||
|function| {
|
||||
for i in 0..10 {
|
||||
let _result: i64 = add_function.call((i, i + 1, i + 2)).unwrap();
|
||||
let _result: i64 = function.call((i, i + 1, i + 2)).unwrap();
|
||||
}
|
||||
},
|
||||
BatchSize::SmallInput,
|
||||
@@ -86,72 +80,75 @@ fn call_add_function(c: &mut Criterion) {
|
||||
});
|
||||
}
|
||||
|
||||
fn call_add_callback(c: &mut Criterion) {
|
||||
c.bench_function("call callback add 2 10", |b| {
|
||||
fn call_sum_callback(c: &mut Criterion) {
|
||||
let lua = Lua::new();
|
||||
let callback = lua
|
||||
.create_function(|_, (a, b, c): (i64, i64, i64)| Ok(a + b + c))
|
||||
.unwrap();
|
||||
lua.globals().set("callback", callback).unwrap();
|
||||
|
||||
c.bench_function("call Rust callback [sum] 3 10", |b| {
|
||||
b.iter_batched_ref(
|
||||
|| {
|
||||
let lua = Lua::new();
|
||||
let f = {
|
||||
let c: LuaFunction = lua
|
||||
.create_function(|_, (a, b, c): (i64, i64, i64)| Ok(a + b + c))
|
||||
.unwrap();
|
||||
lua.globals().set("callback", c).unwrap();
|
||||
let f: LuaFunction = lua
|
||||
.load(
|
||||
r#"
|
||||
function()
|
||||
for i = 1,10 do
|
||||
callback(i, i, i)
|
||||
end
|
||||
end
|
||||
"#,
|
||||
)
|
||||
.eval()
|
||||
.unwrap();
|
||||
lua.create_registry_value(f).unwrap()
|
||||
};
|
||||
(lua, f)
|
||||
collect_gc_twice(&lua);
|
||||
lua.load("function() for i = 1,10 do callback(i, i+1, i+2) end end")
|
||||
.eval::<LuaFunction>()
|
||||
.unwrap()
|
||||
},
|
||||
|(lua, f)| {
|
||||
let entry_function: LuaFunction = lua.registry_value(f).unwrap();
|
||||
entry_function.call::<_, ()>(()).unwrap();
|
||||
|function| {
|
||||
function.call::<_, ()>(()).unwrap();
|
||||
},
|
||||
BatchSize::SmallInput,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
fn call_append_callback(c: &mut Criterion) {
|
||||
c.bench_function("call callback append 10", |b| {
|
||||
fn call_async_sum_callback(c: &mut Criterion) {
|
||||
let lua = Lua::new();
|
||||
let callback = lua
|
||||
.create_async_function(|_, (a, b, c): (i64, i64, i64)| async move {
|
||||
task::yield_now().await;
|
||||
Ok(a + b + c)
|
||||
})
|
||||
.unwrap();
|
||||
lua.globals().set("callback", callback).unwrap();
|
||||
|
||||
c.bench_function("call async Rust callback [sum] 3 10", |b| {
|
||||
let rt = Runtime::new().unwrap();
|
||||
b.to_async(rt).iter_batched(
|
||||
|| {
|
||||
collect_gc_twice(&lua);
|
||||
lua.load("function() for i = 1,10 do callback(i, i+1, i+2) end end")
|
||||
.eval::<LuaFunction>()
|
||||
.unwrap()
|
||||
},
|
||||
|function| async move {
|
||||
function.call_async::<_, ()>(()).await.unwrap();
|
||||
},
|
||||
BatchSize::SmallInput,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
fn call_concat_callback(c: &mut Criterion) {
|
||||
let lua = Lua::new();
|
||||
let callback = lua
|
||||
.create_function(|_, (a, b): (LuaString, LuaString)| {
|
||||
Ok(format!("{}{}", a.to_str()?, b.to_str()?))
|
||||
})
|
||||
.unwrap();
|
||||
lua.globals().set("callback", callback).unwrap();
|
||||
|
||||
c.bench_function("call Rust callback [concat string] 10", |b| {
|
||||
b.iter_batched_ref(
|
||||
|| {
|
||||
let lua = Lua::new();
|
||||
let f = {
|
||||
let c: LuaFunction = lua
|
||||
.create_function(|_, (a, b): (LuaString, LuaString)| {
|
||||
Ok(format!("{}{}", a.to_str()?, b.to_str()?))
|
||||
})
|
||||
.unwrap();
|
||||
lua.globals().set("callback", c).unwrap();
|
||||
let f: LuaFunction = lua
|
||||
.load(
|
||||
r#"
|
||||
function()
|
||||
for _ = 1,10 do
|
||||
callback("a", "b")
|
||||
end
|
||||
end
|
||||
"#,
|
||||
)
|
||||
.eval()
|
||||
.unwrap();
|
||||
lua.create_registry_value(f).unwrap()
|
||||
};
|
||||
(lua, f)
|
||||
collect_gc_twice(&lua);
|
||||
lua.load("function() for i = 1,10 do callback('a', tostring(i)) end end")
|
||||
.eval::<LuaFunction>()
|
||||
.unwrap()
|
||||
},
|
||||
|(lua, f)| {
|
||||
let entry_function: LuaFunction = lua.registry_value(f).unwrap();
|
||||
entry_function.call::<_, ()>(()).unwrap();
|
||||
|function| {
|
||||
function.call::<_, ()>(()).unwrap();
|
||||
},
|
||||
BatchSize::SmallInput,
|
||||
);
|
||||
@@ -159,10 +156,12 @@ fn call_append_callback(c: &mut Criterion) {
|
||||
}
|
||||
|
||||
fn create_registry_values(c: &mut Criterion) {
|
||||
c.bench_function("create registry 10", |b| {
|
||||
b.iter_batched_ref(
|
||||
|| Lua::new(),
|
||||
|lua| {
|
||||
let lua = Lua::new();
|
||||
|
||||
c.bench_function("create [registry value] 10", |b| {
|
||||
b.iter_batched(
|
||||
|| collect_gc_twice(&lua),
|
||||
|_| {
|
||||
for _ in 0..10 {
|
||||
lua.create_registry_value(lua.pack(true).unwrap()).unwrap();
|
||||
}
|
||||
@@ -177,10 +176,12 @@ fn create_userdata(c: &mut Criterion) {
|
||||
struct UserData(i64);
|
||||
impl LuaUserData for UserData {}
|
||||
|
||||
c.bench_function("create userdata 10", |b| {
|
||||
b.iter_batched_ref(
|
||||
|| Lua::new(),
|
||||
|lua| {
|
||||
let lua = Lua::new();
|
||||
|
||||
c.bench_function("create [table userdata] 10", |b| {
|
||||
b.iter_batched(
|
||||
|| collect_gc_twice(&lua),
|
||||
|_| {
|
||||
let table: LuaTable = lua.create_table().unwrap();
|
||||
for i in 1..11 {
|
||||
table.set(i, UserData(i)).unwrap();
|
||||
@@ -191,20 +192,80 @@ fn create_userdata(c: &mut Criterion) {
|
||||
});
|
||||
}
|
||||
|
||||
fn call_userdata_method(c: &mut Criterion) {
|
||||
struct UserData(i64);
|
||||
impl LuaUserData for UserData {
|
||||
fn add_methods<'lua, M: LuaUserDataMethods<'lua, Self>>(methods: &mut M) {
|
||||
methods.add_method("method", |_, this, ()| Ok(this.0));
|
||||
}
|
||||
}
|
||||
|
||||
let lua = Lua::new();
|
||||
lua.globals().set("userdata", UserData(10)).unwrap();
|
||||
|
||||
c.bench_function("call [userdata method] 10", |b| {
|
||||
b.iter_batched_ref(
|
||||
|| {
|
||||
collect_gc_twice(&lua);
|
||||
lua.load("function() for i = 1,10 do userdata:method() end end")
|
||||
.eval::<LuaFunction>()
|
||||
.unwrap()
|
||||
},
|
||||
|function| {
|
||||
function.call::<_, ()>(()).unwrap();
|
||||
},
|
||||
BatchSize::SmallInput,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
fn call_async_userdata_method(c: &mut Criterion) {
|
||||
#[derive(Clone, Copy)]
|
||||
struct UserData(i64);
|
||||
impl LuaUserData for UserData {
|
||||
fn add_methods<'lua, M: LuaUserDataMethods<'lua, Self>>(methods: &mut M) {
|
||||
methods.add_async_method("method", |_, this, ()| async move { Ok(this.0) });
|
||||
}
|
||||
}
|
||||
|
||||
let lua = Lua::new();
|
||||
lua.globals().set("userdata", UserData(10)).unwrap();
|
||||
|
||||
c.bench_function("call async [userdata method] 10", |b| {
|
||||
let rt = Runtime::new().unwrap();
|
||||
b.to_async(rt).iter_batched(
|
||||
|| {
|
||||
collect_gc_twice(&lua);
|
||||
lua.load("function() for i = 1,10 do userdata:method() end end")
|
||||
.eval::<LuaFunction>()
|
||||
.unwrap()
|
||||
},
|
||||
|function| async move {
|
||||
function.call_async::<_, ()>(()).await.unwrap();
|
||||
},
|
||||
BatchSize::SmallInput,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
criterion_group! {
|
||||
name = benches;
|
||||
config = Criterion::default()
|
||||
.sample_size(200)
|
||||
.sample_size(300)
|
||||
.measurement_time(Duration::from_secs(10))
|
||||
.noise_threshold(0.02);
|
||||
targets =
|
||||
create_table,
|
||||
create_array,
|
||||
create_string_table,
|
||||
call_add_function,
|
||||
call_add_callback,
|
||||
call_append_callback,
|
||||
call_lua_function,
|
||||
call_sum_callback,
|
||||
call_async_sum_callback,
|
||||
call_concat_callback,
|
||||
create_registry_values,
|
||||
create_userdata,
|
||||
call_userdata_method,
|
||||
call_async_userdata_method,
|
||||
}
|
||||
|
||||
criterion_main!(benches);
|
||||
|
||||
+27
-46
@@ -1,13 +1,19 @@
|
||||
use std::env;
|
||||
use std::fs::File;
|
||||
use std::io::{BufRead, BufReader, Result};
|
||||
use std::ops::Bound;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::path::PathBuf;
|
||||
|
||||
fn get_env_var(name: &str) -> String {
|
||||
match env::var(name) {
|
||||
Ok(val) => val,
|
||||
Err(env::VarError::NotPresent) => String::new(),
|
||||
Err(err) => panic!("cannot get {}: {}", name, err),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn probe_lua() -> PathBuf {
|
||||
let include_dir = env::var_os("LUA_INC").unwrap_or_default();
|
||||
let lib_dir = env::var_os("LUA_LIB").unwrap_or_default();
|
||||
let lua_lib = env::var_os("LUA_LIB_NAME").unwrap_or_default();
|
||||
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");
|
||||
|
||||
println!("cargo:rerun-if-env-changed=LUA_INC");
|
||||
println!("cargo:rerun-if-env-changed=LUA_LIB");
|
||||
@@ -16,11 +22,22 @@ pub fn probe_lua() -> PathBuf {
|
||||
|
||||
let need_lua_lib = cfg!(any(not(feature = "module"), target_os = "windows"));
|
||||
|
||||
if include_dir != "" && (!need_lua_lib || lib_dir != "") {
|
||||
if lua_lib == "" {
|
||||
panic!("LUA_LIB_NAME is not set");
|
||||
if include_dir != "" {
|
||||
if need_lua_lib {
|
||||
if lib_dir == "" {
|
||||
panic!("LUA_LIB is not set");
|
||||
}
|
||||
if lua_lib == "" {
|
||||
panic!("LUA_LIB_NAME is not set");
|
||||
}
|
||||
|
||||
let mut link_lib = "";
|
||||
if get_env_var("LUA_LINK") == "static" {
|
||||
link_lib = "static=";
|
||||
};
|
||||
println!("cargo:rustc-link-search=native={}", lib_dir);
|
||||
println!("cargo:rustc-link-lib={}{}", link_lib, lua_lib);
|
||||
}
|
||||
let _version = use_custom_lua(&include_dir, &lib_dir, &lua_lib).unwrap();
|
||||
return PathBuf::from(include_dir);
|
||||
}
|
||||
|
||||
@@ -100,39 +117,3 @@ pub fn probe_lua() -> PathBuf {
|
||||
lua.unwrap().include_paths[0].clone()
|
||||
}
|
||||
}
|
||||
|
||||
fn use_custom_lua<S: AsRef<Path>>(include_dir: &S, lib_dir: &S, lua_lib: &S) -> Result<String> {
|
||||
let mut version_found = String::new();
|
||||
|
||||
// Find LUA_VERSION_NUM
|
||||
let mut lua_h_path = include_dir.as_ref().to_owned();
|
||||
lua_h_path.push("lua.h");
|
||||
let f = File::open(lua_h_path)?;
|
||||
let reader = BufReader::new(f);
|
||||
for line in reader.lines() {
|
||||
let line = line?;
|
||||
let parts = line.split_whitespace().collect::<Vec<_>>();
|
||||
if parts.len() == 3 && parts[1] == "LUA_VERSION_NUM" {
|
||||
version_found = parts[2].to_string();
|
||||
}
|
||||
}
|
||||
|
||||
let link_lib = match env::var("LUA_LINK") {
|
||||
Ok(s) if s == "static" => "static=",
|
||||
_ => "",
|
||||
};
|
||||
|
||||
if cfg!(any(not(feature = "module"), target_os = "windows")) {
|
||||
println!(
|
||||
"cargo:rustc-link-search=native={}",
|
||||
lib_dir.as_ref().display()
|
||||
);
|
||||
println!(
|
||||
"cargo:rustc-link-lib={}{}",
|
||||
link_lib,
|
||||
lua_lib.as_ref().display()
|
||||
);
|
||||
}
|
||||
|
||||
Ok(version_found)
|
||||
}
|
||||
|
||||
+109
-2
@@ -1,7 +1,8 @@
|
||||
#![allow(unreachable_code)]
|
||||
|
||||
use std::env;
|
||||
use std::io::{Error, ErrorKind, Result};
|
||||
use std::fs::File;
|
||||
use std::io::{Error, ErrorKind, Result, Write};
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::process::Command;
|
||||
|
||||
@@ -91,6 +92,105 @@ fn build_glue<P: AsRef<Path> + std::fmt::Debug>(include_path: &P) {
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
// When cross-compiling, we cannot use `build_glue` as we cannot run the generated
|
||||
// executable. Instead, let's take a stab at synthesizing the likely values.
|
||||
// If you're cross-compiling and using a non-vendored library then there is a chance
|
||||
// that the values selected here may be incorrect, but we have no way to determine
|
||||
// that here.
|
||||
fn generate_glue() -> Result<()> {
|
||||
let build_dir = PathBuf::from(env::var_os("OUT_DIR").unwrap());
|
||||
let mut glue = File::create(build_dir.join("glue.rs"))?;
|
||||
writeln!(
|
||||
glue,
|
||||
"/* This file was generated by build/main.rs; do not modify by hand */"
|
||||
)?;
|
||||
writeln!(glue, "use std::os::raw::*;")?;
|
||||
|
||||
writeln!(glue, "/* luaconf.h */")?;
|
||||
let pointer_bit_width: usize = env::var("CARGO_CFG_TARGET_POINTER_WIDTH")
|
||||
.unwrap()
|
||||
.parse()
|
||||
.unwrap();
|
||||
writeln!(
|
||||
glue,
|
||||
"pub const LUA_EXTRASPACE: c_int = {} / 8;",
|
||||
pointer_bit_width
|
||||
)?;
|
||||
|
||||
// This is generally hardcoded to this size
|
||||
writeln!(glue, "pub const LUA_IDSIZE: c_int = 60;")?;
|
||||
|
||||
// Unless the target is restricted, the defaults are 64 bit
|
||||
writeln!(glue, "pub type LUA_NUMBER = c_double;")?;
|
||||
writeln!(glue, "pub type LUA_INTEGER = i64;")?;
|
||||
writeln!(glue, "pub type LUA_UNSIGNED = u64;")?;
|
||||
|
||||
writeln!(glue, "/* lua.h */")?;
|
||||
let version = if cfg!(any(feature = "luajit", feature = "lua51")) {
|
||||
(5, 1, 0)
|
||||
} else if cfg!(feature = "lua52") {
|
||||
(5, 2, 0)
|
||||
} else if cfg!(feature = "lua53") {
|
||||
(5, 3, 0)
|
||||
} else if cfg!(feature = "lua54") {
|
||||
(5, 4, 0)
|
||||
} else {
|
||||
unreachable!();
|
||||
};
|
||||
writeln!(
|
||||
glue,
|
||||
"pub const LUA_VERSION_NUM: c_int = {};",
|
||||
(version.0 * 100) + version.1
|
||||
)?;
|
||||
|
||||
let max_stack = if pointer_bit_width >= 32 {
|
||||
1_000_000
|
||||
} else {
|
||||
15_000
|
||||
};
|
||||
writeln!(
|
||||
glue,
|
||||
"pub const LUA_REGISTRYINDEX: c_int = -{} - 1000;",
|
||||
max_stack
|
||||
)?;
|
||||
|
||||
// These two are only defined in lua 5.1
|
||||
writeln!(glue, "pub const LUA_ENVIRONINDEX: c_int = -10001;")?;
|
||||
writeln!(glue, "pub const LUA_GLOBALSINDEX: c_int = -10002;")?;
|
||||
|
||||
writeln!(glue, "/* lauxlib.h */")?;
|
||||
// This is only defined in lua 5.3 and up, but we can always generate its value here,
|
||||
// even if we don't use it.
|
||||
// This matches the default definition in lauxlib.h
|
||||
writeln!(glue, "pub const LUAL_NUMSIZES: c_int = std::mem::size_of::<LUA_INTEGER>() as c_int * 16 + std::mem::size_of::<LUA_NUMBER>() as c_int;")?;
|
||||
|
||||
writeln!(glue, "/* lualib.h */")?;
|
||||
write!(
|
||||
glue,
|
||||
r#"
|
||||
#[cfg(feature = "luajit")]
|
||||
pub const LUA_BITLIBNAME: &str = "bit";
|
||||
#[cfg(not(feature = "luajit"))]
|
||||
pub const LUA_BITLIBNAME: &str = "bit32";
|
||||
|
||||
pub const LUA_COLIBNAME: &str = "coroutine";
|
||||
pub const LUA_DBLIBNAME: &str = "debug";
|
||||
pub const LUA_IOLIBNAME: &str = "io";
|
||||
pub const LUA_LOADLIBNAME: &str = "package";
|
||||
pub const LUA_MATHLIBNAME: &str = "math";
|
||||
pub const LUA_OSLIBNAME: &str = "os";
|
||||
pub const LUA_STRLIBNAME: &str = "string";
|
||||
pub const LUA_TABLIBNAME: &str = "table";
|
||||
pub const LUA_UTF8LIBNAME: &str = "utf8";
|
||||
|
||||
pub const LUA_JITLIBNAME: &str = "jit";
|
||||
pub const LUA_FFILIBNAME: &str = "ffi";
|
||||
"#
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn main() {
|
||||
#[cfg(not(any(
|
||||
feature = "lua54",
|
||||
@@ -132,5 +232,12 @@ fn main() {
|
||||
);
|
||||
|
||||
let include_dir = find::probe_lua();
|
||||
build_glue(&include_dir);
|
||||
if env::var("TARGET").unwrap() != env::var("HOST").unwrap() {
|
||||
generate_glue().unwrap();
|
||||
} else {
|
||||
build_glue(&include_dir);
|
||||
println!("cargo:rerun-if-changed=src/ffi/glue/glue.c");
|
||||
}
|
||||
|
||||
println!("cargo:rerun-if-changed=build");
|
||||
}
|
||||
|
||||
@@ -2,8 +2,9 @@ use std::collections::HashMap;
|
||||
use std::sync::Arc;
|
||||
|
||||
use bstr::BString;
|
||||
use hyper::{body::Body as HyperBody, Client as HyperClient};
|
||||
use tokio::{stream::StreamExt, sync::Mutex};
|
||||
use hyper::body::{Body as HyperBody, HttpBody as _};
|
||||
use hyper::Client as HyperClient;
|
||||
use tokio::sync::Mutex;
|
||||
|
||||
use mlua::{Error, Lua, Result, UserData, UserDataMethods};
|
||||
|
||||
@@ -20,8 +21,8 @@ impl UserData for BodyReader {
|
||||
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
|
||||
methods.add_async_method("read", |_, reader, ()| async move {
|
||||
let mut reader = reader.0.lock().await;
|
||||
let bytes = reader.try_next().await.map_err(Error::external)?;
|
||||
if let Some(bytes) = bytes {
|
||||
if let Some(bytes) = reader.data().await {
|
||||
let bytes = bytes.map_err(Error::external)?;
|
||||
return Ok(Some(BString::from(bytes.as_ref())));
|
||||
}
|
||||
Ok(None)
|
||||
@@ -61,7 +62,7 @@ async fn main() -> Result<()> {
|
||||
let f = lua
|
||||
.load(
|
||||
r#"
|
||||
local res = fetch_url(...);
|
||||
local res = fetch_url(...)
|
||||
print(res.status)
|
||||
for key, vals in pairs(res.headers) do
|
||||
for _, val in ipairs(vals) do
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
use mlua::{Error, Lua, LuaSerdeExt, Result};
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
let globals = lua.globals();
|
||||
globals.set("null", lua.null()?)?;
|
||||
|
||||
let fetch_json = lua.create_async_function(|lua, uri: String| async move {
|
||||
let resp = reqwest::get(&uri)
|
||||
.await
|
||||
.and_then(|resp| resp.error_for_status())
|
||||
.map_err(Error::external)?;
|
||||
let json = resp
|
||||
.json::<serde_json::Value>()
|
||||
.await
|
||||
.map_err(Error::external)?;
|
||||
lua.to_value(&json)
|
||||
})?;
|
||||
globals.set("fetch_json", fetch_json)?;
|
||||
|
||||
let f = lua
|
||||
.load(
|
||||
r#"
|
||||
function print_r(t, indent)
|
||||
local indent = indent or ''
|
||||
for k, v in pairs(t) do
|
||||
io.write(indent, tostring(k))
|
||||
if type(v) == "table" then io.write(':\n') print_r(v, indent..' ')
|
||||
else io.write(': ', v == null and "null" or tostring(v), '\n') end
|
||||
end
|
||||
end
|
||||
|
||||
local res = fetch_json(...)
|
||||
print_r(res)
|
||||
"#,
|
||||
)
|
||||
.into_function()?;
|
||||
|
||||
f.call_async("https://httpbin.org/anything?arg0=val0").await
|
||||
}
|
||||
@@ -1,9 +1,8 @@
|
||||
use std::net::Shutdown;
|
||||
use std::sync::Arc;
|
||||
|
||||
use bstr::BString;
|
||||
use tokio::io::{AsyncReadExt, AsyncWriteExt};
|
||||
use tokio::net::{TcpListener, TcpStream};
|
||||
use tokio::prelude::*;
|
||||
use tokio::sync::Mutex;
|
||||
use tokio::task;
|
||||
|
||||
@@ -55,7 +54,7 @@ impl UserData for LuaTcpStream {
|
||||
});
|
||||
|
||||
methods.add_async_method("close", |_, stream, ()| async move {
|
||||
stream.0.lock().await.shutdown(Shutdown::Both)?;
|
||||
stream.0.lock().await.shutdown().await?;
|
||||
Ok(())
|
||||
});
|
||||
}
|
||||
@@ -63,7 +62,7 @@ impl UserData for LuaTcpStream {
|
||||
|
||||
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.unwrap() });
|
||||
task::spawn_local(async move { func.call_async::<_, ()>(()).await });
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
|
||||
@@ -164,6 +164,33 @@ fn main() -> Result<()> {
|
||||
< f32::EPSILON
|
||||
);
|
||||
|
||||
// Normally, Rust types passed to `Lua` must be `'static`, because there is no way to be
|
||||
// sure of their lifetime inside the Lua state. There is, however, a limited way to lift this
|
||||
// requirement. You can call `Lua::scope` to create userdata and callbacks types that only live
|
||||
// for as long as the call to scope, but do not have to be `'static` (and `Send`).
|
||||
|
||||
{
|
||||
let mut rust_val = 0;
|
||||
|
||||
lua.scope(|scope| {
|
||||
// We create a 'sketchy' Lua callback that holds a mutable reference to the variable
|
||||
// `rust_val`. Outside of a `Lua::scope` call, this would not be allowed
|
||||
// because it could be unsafe.
|
||||
|
||||
lua.globals().set(
|
||||
"sketchy",
|
||||
scope.create_function_mut(|_, ()| {
|
||||
rust_val = 42;
|
||||
Ok(())
|
||||
})?,
|
||||
)?;
|
||||
|
||||
lua.load("sketchy()").exec()
|
||||
})?;
|
||||
|
||||
assert_eq!(rust_val, 42);
|
||||
}
|
||||
|
||||
// We were able to run our 'sketchy' function inside the scope just fine. However, if we
|
||||
// try to run our 'sketchy' function outside of the scope, the function we created will have
|
||||
// been invalidated and we will generate an error. If our function wasn't invalidated, we
|
||||
|
||||
@@ -17,4 +17,3 @@ vendored = ["mlua/vendored"]
|
||||
|
||||
[dependencies]
|
||||
mlua = { path = "../..", features = ["module"] }
|
||||
mlua_derive = { path = "../../mlua_derive" }
|
||||
|
||||
@@ -8,7 +8,7 @@ fn used_memory(lua: &Lua, _: ()) -> LuaResult<usize> {
|
||||
Ok(lua.used_memory())
|
||||
}
|
||||
|
||||
#[mlua_derive::lua_module]
|
||||
#[mlua::lua_module]
|
||||
fn rust_module(lua: &Lua) -> LuaResult<LuaTable> {
|
||||
let exports = lua.create_table()?;
|
||||
exports.set("sum", lua.create_function(sum)?)?;
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
use mlua::{Error, Lua, LuaSerdeExt, Result, UserData, Value};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
enum Transmission {
|
||||
Manual,
|
||||
Automatic,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
struct Engine {
|
||||
v: u32,
|
||||
kw: u32,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
struct Car {
|
||||
active: bool,
|
||||
model: String,
|
||||
transmission: Transmission,
|
||||
engine: Engine,
|
||||
}
|
||||
|
||||
impl UserData for Car {}
|
||||
|
||||
fn main() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
let globals = lua.globals();
|
||||
|
||||
// Create Car struct from a Lua table
|
||||
let car: Car = lua.from_value(lua.load(r#"
|
||||
{active = true, model = "Volkswagen Golf", transmission = "Automatic", engine = {v = 1499, kw = 90}}
|
||||
"#).eval()?)?;
|
||||
|
||||
// Set it as (serializable) userdata
|
||||
globals.set("null", lua.null()?)?;
|
||||
globals.set("array_mt", lua.array_metatable()?)?;
|
||||
globals.set("car", lua.create_ser_userdata(car)?)?;
|
||||
|
||||
// Create a Lua table with multiple data types
|
||||
let val: Value = lua
|
||||
.load(r#"{driver = "Boris", car = car, price = null, points = setmetatable({}, array_mt)}"#)
|
||||
.eval()?;
|
||||
|
||||
// Serialize the table above to JSON
|
||||
let json_str = serde_json::to_string(&val).map_err(Error::external)?;
|
||||
println!("{}", json_str);
|
||||
|
||||
// Create Lua Value from JSON (or any serializable type)
|
||||
let json = serde_json::json!({
|
||||
"key": "value",
|
||||
"null": null,
|
||||
"array": [],
|
||||
});
|
||||
globals.set("json_value", lua.to_value(&json)?)?;
|
||||
lua.load(
|
||||
r#"
|
||||
assert(json_value["key"] == "value")
|
||||
assert(json_value["null"] == null)
|
||||
assert(#(json_value["array"]) == 0)
|
||||
"#,
|
||||
)
|
||||
.exec()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "mlua_derive"
|
||||
version = "0.4.0"
|
||||
version = "0.5.0"
|
||||
authors = ["Aleksandr Orlenko <zxteam@pm.me>"]
|
||||
edition = "2018"
|
||||
description = "Procedural macros for the mlua crate."
|
||||
|
||||
+72
-3
@@ -1,4 +1,5 @@
|
||||
use std::collections::{BTreeMap, HashMap};
|
||||
use std::borrow::Cow;
|
||||
use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet};
|
||||
use std::ffi::{CStr, CString};
|
||||
use std::hash::{BuildHasher, Hash};
|
||||
use std::string::String as StdString;
|
||||
@@ -12,7 +13,7 @@ use crate::lua::Lua;
|
||||
use crate::string::String;
|
||||
use crate::table::Table;
|
||||
use crate::thread::Thread;
|
||||
use crate::types::{LightUserData, MaybeSend, Number};
|
||||
use crate::types::{LightUserData, MaybeSend};
|
||||
use crate::userdata::{AnyUserData, UserData};
|
||||
use crate::value::{FromLua, Nil, ToLua, Value};
|
||||
|
||||
@@ -222,6 +223,12 @@ impl<'lua> ToLua<'lua> for &str {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> ToLua<'lua> for Cow<'_, str> {
|
||||
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
|
||||
Ok(Value::String(lua.create_string(self.as_bytes())?))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> ToLua<'lua> for CString {
|
||||
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
|
||||
Ok(Value::String(lua.create_string(self.as_bytes())?))
|
||||
@@ -256,6 +263,12 @@ impl<'lua> ToLua<'lua> for &CStr {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> ToLua<'lua> for Cow<'_, CStr> {
|
||||
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
|
||||
Ok(Value::String(lua.create_string(self.to_bytes())?))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> ToLua<'lua> for BString {
|
||||
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
|
||||
Ok(Value::String(lua.create_string(&self)?))
|
||||
@@ -345,7 +358,13 @@ macro_rules! lua_convert_float {
|
||||
($x:ty) => {
|
||||
impl<'lua> ToLua<'lua> for $x {
|
||||
fn to_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
|
||||
Ok(Value::Number(self as Number))
|
||||
cast(self)
|
||||
.ok_or_else(|| Error::ToLuaConversionError {
|
||||
from: stringify!($x),
|
||||
to: "number",
|
||||
message: Some("out of range".to_string()),
|
||||
})
|
||||
.map(Value::Number)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -481,6 +500,56 @@ impl<'lua, K: Ord + FromLua<'lua>, V: FromLua<'lua>> FromLua<'lua> for BTreeMap<
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua, T: Eq + Hash + ToLua<'lua>, S: BuildHasher> ToLua<'lua> for HashSet<T, S> {
|
||||
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
|
||||
Ok(Value::Table(lua.create_table_from(
|
||||
self.into_iter().map(|val| (val, true)),
|
||||
)?))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua, T: Eq + Hash + FromLua<'lua>, S: BuildHasher + Default> FromLua<'lua> for HashSet<T, S> {
|
||||
fn from_lua(value: Value<'lua>, _: &'lua Lua) -> Result<Self> {
|
||||
if let Value::Table(table) = value {
|
||||
table
|
||||
.pairs::<T, Value<'lua>>()
|
||||
.map(|res| res.map(|(k, _)| k))
|
||||
.collect()
|
||||
} else {
|
||||
Err(Error::FromLuaConversionError {
|
||||
from: value.type_name(),
|
||||
to: "HashSet",
|
||||
message: Some("expected table".to_string()),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua, T: Ord + ToLua<'lua>> ToLua<'lua> for BTreeSet<T> {
|
||||
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
|
||||
Ok(Value::Table(lua.create_table_from(
|
||||
self.into_iter().map(|val| (val, true)),
|
||||
)?))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua, T: Ord + FromLua<'lua>> FromLua<'lua> for BTreeSet<T> {
|
||||
fn from_lua(value: Value<'lua>, _: &'lua Lua) -> Result<Self> {
|
||||
if let Value::Table(table) = value {
|
||||
table
|
||||
.pairs::<T, Value<'lua>>()
|
||||
.map(|res| res.map(|(k, _)| k))
|
||||
.collect()
|
||||
} else {
|
||||
Err(Error::FromLuaConversionError {
|
||||
from: value.type_name(),
|
||||
to: "BTreeSet",
|
||||
message: Some("expected table".to_string()),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua, T: ToLua<'lua>> ToLua<'lua> for Option<T> {
|
||||
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
|
||||
match self {
|
||||
|
||||
@@ -9,6 +9,7 @@ use std::sync::Arc;
|
||||
|
||||
/// Error type returned by `mlua` methods.
|
||||
#[derive(Debug, Clone)]
|
||||
#[non_exhaustive]
|
||||
pub enum Error {
|
||||
/// Syntax error while parsing Lua source code.
|
||||
SyntaxError {
|
||||
@@ -105,6 +106,13 @@ pub enum Error {
|
||||
/// [`AnyUserData`]: struct.AnyUserData.html
|
||||
/// [`UserDataMethods`]: trait.UserDataMethods.html
|
||||
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
|
||||
UserDataDestructed,
|
||||
/// An [`AnyUserData`] immutable borrow failed because it is already borrowed mutably.
|
||||
///
|
||||
/// This error can occur when a method on a [`UserData`] type calls back into Lua, which then
|
||||
@@ -132,6 +140,19 @@ pub enum Error {
|
||||
/// Original error returned by the Rust code.
|
||||
cause: Arc<Error>,
|
||||
},
|
||||
/// A Rust panic that was previosly resumed, returned again.
|
||||
///
|
||||
/// This error can occur only when a Rust panic resumed previously was recovered
|
||||
/// and returned again.
|
||||
PreviouslyResumedPanic,
|
||||
/// Serialization error.
|
||||
#[cfg(feature = "serialize")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "serialize")))]
|
||||
SerializeError(StdString),
|
||||
/// Deserialization error.
|
||||
#[cfg(feature = "serialize")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "serialize")))]
|
||||
DeserializeError(StdString),
|
||||
/// A custom error.
|
||||
///
|
||||
/// This can be used for returning user-defined errors from callbacks.
|
||||
@@ -206,6 +227,7 @@ impl fmt::Display for Error {
|
||||
}
|
||||
Error::CoroutineInactive => write!(fmt, "cannot resume inactive coroutine"),
|
||||
Error::UserDataTypeMismatch => write!(fmt, "userdata is not expected type"),
|
||||
Error::UserDataDestructed => write!(fmt, "userdata has been destructed"),
|
||||
Error::UserDataBorrowError => write!(fmt, "userdata already mutably borrowed"),
|
||||
Error::UserDataBorrowMutError => write!(fmt, "userdata already borrowed"),
|
||||
Error::MismatchedRegistryKey => {
|
||||
@@ -214,6 +236,17 @@ impl fmt::Display for Error {
|
||||
Error::CallbackError { ref traceback, .. } => {
|
||||
write!(fmt, "callback error: {}", traceback)
|
||||
}
|
||||
Error::PreviouslyResumedPanic => {
|
||||
write!(fmt, "previously resumed panic returned again")
|
||||
}
|
||||
#[cfg(feature = "serialize")]
|
||||
Error::SerializeError(ref err) => {
|
||||
write!(fmt, "serialize error: {}", err)
|
||||
},
|
||||
#[cfg(feature = "serialize")]
|
||||
Error::DeserializeError(ref err) => {
|
||||
write!(fmt, "deserialize error: {}", err)
|
||||
},
|
||||
Error::ExternalError(ref err) => write!(fmt, "{}", err),
|
||||
}
|
||||
}
|
||||
@@ -289,3 +322,17 @@ impl std::convert::From<Utf8Error> for Error {
|
||||
Error::external(err)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
impl serde::ser::Error for Error {
|
||||
fn custom<T: fmt::Display>(msg: T) -> Self {
|
||||
Self::SerializeError(msg.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
impl serde::de::Error for Error {
|
||||
fn custom<T: fmt::Display>(msg: T) -> Self {
|
||||
Self::DeserializeError(msg.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
// The MIT License (MIT)
|
||||
//
|
||||
// Copyright (c) 2019 A. Orlenko
|
||||
// Copyright (c) 2019-2021 A. Orlenko
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
+1
-7
@@ -1,6 +1,6 @@
|
||||
// The MIT License (MIT)
|
||||
//
|
||||
// Copyright (c) 2019-2020 A. Orlenko
|
||||
// Copyright (c) 2019-2021 A. Orlenko
|
||||
// Copyright (c) 2014 J.C. Moyer
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
@@ -225,12 +225,8 @@ int main(int argc, const char **argv) {
|
||||
// == luaconf.h ==========================================================
|
||||
|
||||
RS_COMMENT("luaconf.h"),
|
||||
RS_STR("LUA_PATH_DEFAULT", LUA_PATH_DEFAULT),
|
||||
RS_STR("LUA_CPATH_DEFAULT", LUA_CPATH_DEFAULT),
|
||||
RS_STR("LUA_DIRSEP", LUA_DIRSEP),
|
||||
RS_INT("LUA_EXTRASPACE", LUA_EXTRASPACE),
|
||||
RS_INT("LUA_IDSIZE", LUA_IDSIZE),
|
||||
RS_INT("LUAL_BUFFERSIZE", LUAL_BUFFERSIZE),
|
||||
RS_TYPE("LUA_NUMBER",
|
||||
sizeof(LUA_NUMBER) > sizeof(float) ? "c_double" : "c_float"),
|
||||
RS_TYPE("LUA_INTEGER", rs_int_type(sizeof(LUA_INTEGER))),
|
||||
@@ -244,8 +240,6 @@ int main(int argc, const char **argv) {
|
||||
|
||||
RS_COMMENT("lua.h"),
|
||||
RS_INT("LUA_VERSION_NUM", LUA_VERSION_NUM),
|
||||
RS_STR("LUA_VERSION", LUA_VERSION),
|
||||
RS_STR("LUA_RELEASE", LUA_RELEASE),
|
||||
RS_INT("LUA_REGISTRYINDEX", LUA_REGISTRYINDEX),
|
||||
#if LUA_VERSION_NUM == 501
|
||||
RS_INT("LUA_ENVIRONINDEX", LUA_ENVIRONINDEX),
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
// The MIT License (MIT)
|
||||
//
|
||||
// Copyright (c) 2019-2020 A. Orlenko
|
||||
// Copyright (c) 2019-2021 A. Orlenko
|
||||
// Copyright (c) 2014 J.C. Moyer
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
|
||||
+5
-4
@@ -1,6 +1,6 @@
|
||||
// The MIT License (MIT)
|
||||
//
|
||||
// Copyright (c) 2019-2020 A. Orlenko
|
||||
// Copyright (c) 2019-2021 A. Orlenko
|
||||
// Copyright (c) 2014 J.C. Moyer
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
@@ -32,13 +32,14 @@ use std::ptr;
|
||||
|
||||
use super::luaconf;
|
||||
|
||||
pub use super::glue::{LUA_RELEASE, LUA_VERSION, LUA_VERSION_NUM};
|
||||
|
||||
pub use super::glue::LUA_REGISTRYINDEX;
|
||||
#[cfg(any(feature = "lua51", feature = "luajit"))]
|
||||
pub use super::glue::{LUA_ENVIRONINDEX, LUA_GLOBALSINDEX};
|
||||
pub use super::glue::{LUA_REGISTRYINDEX, LUA_VERSION_NUM};
|
||||
|
||||
#[cfg(not(feature = "luajit"))]
|
||||
pub const LUA_SIGNATURE: &[u8] = b"\x1bLua";
|
||||
#[cfg(feature = "luajit")]
|
||||
pub const LUA_SIGNATURE: &[u8] = b"\x1bLJ";
|
||||
|
||||
// option for multiple returns in 'lua_pcall' and 'lua_call'
|
||||
pub const LUA_MULTRET: c_int = -1;
|
||||
|
||||
+1
-2
@@ -1,6 +1,6 @@
|
||||
// The MIT License (MIT)
|
||||
//
|
||||
// Copyright (c) 2019-2020 A. Orlenko
|
||||
// Copyright (c) 2019-2021 A. Orlenko
|
||||
// Copyright (c) 2014 J.C. Moyer
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
@@ -23,7 +23,6 @@
|
||||
|
||||
//! Contains definitions from `luaconf.h`.
|
||||
|
||||
pub use super::glue::LUAL_BUFFERSIZE;
|
||||
pub use super::glue::LUA_INTEGER;
|
||||
pub use super::glue::LUA_NUMBER;
|
||||
pub use super::glue::LUA_UNSIGNED;
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
// The MIT License (MIT)
|
||||
//
|
||||
// Copyright (c) 2019-2020 A. Orlenko
|
||||
// Copyright (c) 2019-2021 A. Orlenko
|
||||
// Copyright (c) 2014 J.C. Moyer
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
|
||||
+3
-3
@@ -1,6 +1,6 @@
|
||||
// The MIT License (MIT)
|
||||
//
|
||||
// Copyright (c) 2019-2020 A. Orlenko
|
||||
// Copyright (c) 2019-2021 A. Orlenko
|
||||
// Copyright (c) 2014 J.C. Moyer
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
@@ -211,8 +211,8 @@ pub use self::lua::{
|
||||
LUA_HOOKCOUNT, LUA_HOOKLINE, LUA_HOOKRET, LUA_HOOKTAILCALL, LUA_MASKCALL, LUA_MASKCOUNT,
|
||||
LUA_MASKLINE, LUA_MASKRET, LUA_MINSTACK, LUA_MULTRET, LUA_OK, LUA_OPADD, LUA_OPDIV, LUA_OPEQ,
|
||||
LUA_OPLE, LUA_OPLT, LUA_OPMOD, LUA_OPMUL, LUA_OPPOW, LUA_OPSUB, LUA_OPUNM, LUA_REGISTRYINDEX,
|
||||
LUA_TBOOLEAN, LUA_TFUNCTION, LUA_TLIGHTUSERDATA, LUA_TNIL, LUA_TNONE, LUA_TNUMBER, LUA_TSTRING,
|
||||
LUA_TTABLE, LUA_TTHREAD, LUA_TUSERDATA, LUA_YIELD,
|
||||
LUA_SIGNATURE, LUA_TBOOLEAN, LUA_TFUNCTION, LUA_TLIGHTUSERDATA, LUA_TNIL, LUA_TNONE,
|
||||
LUA_TNUMBER, LUA_TSTRING, LUA_TTABLE, LUA_TTHREAD, LUA_TUSERDATA, LUA_YIELD,
|
||||
};
|
||||
|
||||
#[cfg(any(feature = "lua54", feature = "lua53"))]
|
||||
|
||||
+39
-2
@@ -1,5 +1,5 @@
|
||||
use std::os::raw::c_int;
|
||||
use std::ptr;
|
||||
use std::os::raw::{c_int, c_void};
|
||||
use std::{ptr, slice};
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
use crate::ffi;
|
||||
@@ -119,6 +119,7 @@ impl<'lua> Function<'lua> {
|
||||
///
|
||||
/// [`AsyncThread`]: struct.AsyncThread.html
|
||||
#[cfg(feature = "async")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
|
||||
pub fn call_async<'fut, A, R>(&self, args: A) -> LocalBoxFuture<'fut, Result<R>>
|
||||
where
|
||||
'lua: 'fut,
|
||||
@@ -205,6 +206,42 @@ impl<'lua> Function<'lua> {
|
||||
Ok(Function(lua.pop_ref()))
|
||||
}
|
||||
}
|
||||
|
||||
/// Dumps the function as a binary chunk.
|
||||
///
|
||||
/// If `strip` is true, the binary representation may not include all debug information
|
||||
/// about the function, to save space.
|
||||
pub fn dump(&self, strip: bool) -> Result<Vec<u8>> {
|
||||
unsafe extern "C" fn writer(
|
||||
_state: *mut ffi::lua_State,
|
||||
buf: *const c_void,
|
||||
buf_len: usize,
|
||||
data: *mut c_void,
|
||||
) -> c_int {
|
||||
let data = &mut *(data as *mut Vec<u8>);
|
||||
let buf = slice::from_raw_parts(buf as *const u8, buf_len);
|
||||
data.extend_from_slice(buf);
|
||||
0
|
||||
}
|
||||
|
||||
let lua = self.0.lua;
|
||||
let mut data: Vec<u8> = Vec::new();
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(lua.state);
|
||||
assert_stack(lua.state, 1);
|
||||
lua.push_ref(&self.0);
|
||||
let strip = if strip { 1 } else { 0 };
|
||||
ffi::lua_dump(
|
||||
lua.state,
|
||||
writer,
|
||||
&mut data as *mut Vec<u8> as *mut c_void,
|
||||
strip,
|
||||
);
|
||||
ffi::lua_pop(lua.state, 1);
|
||||
}
|
||||
|
||||
Ok(data)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> PartialEq for Function<'lua> {
|
||||
|
||||
+1
-1
@@ -37,7 +37,7 @@ impl<'a> Debug<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Corresponds to the `n` what mask.
|
||||
/// Corresponds to the `S` what mask.
|
||||
pub fn source(&self) -> DebugSource<'a> {
|
||||
unsafe {
|
||||
mlua_assert!(
|
||||
|
||||
+37
-2
@@ -24,6 +24,18 @@
|
||||
//! The [`UserData`] trait can be implemented by user-defined types to make them available to Lua.
|
||||
//! Methods and operators to be used from Lua can be added using the [`UserDataMethods`] API.
|
||||
//!
|
||||
//! # Serde support
|
||||
//!
|
||||
//! The [`LuaSerdeExt`] trait implemented for [`Lua`] allows conversion from Rust types to Lua values
|
||||
//! and vice versa using serde. Any user defined data type that implements [`serde::Serialize`] or
|
||||
//! [`serde::Deserialize`] can be converted.
|
||||
//! For convenience, additional functionality to handle `NULL` values and arrays is provided.
|
||||
//!
|
||||
//! The [`Value`] enum implements [`serde::Serialize`] trait to support serializing Lua values
|
||||
//! (including [`UserData`]) into Rust values.
|
||||
//!
|
||||
//! Requires `feature = "serialize"`.
|
||||
//!
|
||||
//! # Async/await support
|
||||
//!
|
||||
//! The [`create_async_function`] allows creating non-blocking functions that returns [`Future`].
|
||||
@@ -34,7 +46,7 @@
|
||||
//!
|
||||
//! # `Send` requirement
|
||||
//! By default `mlua` is `!Send`. This can be changed by enabling `feature = "send"` that adds `Send` requirement
|
||||
//! to `Function`s and [`UserData`].
|
||||
//! to [`Function`]s and [`UserData`].
|
||||
//!
|
||||
//! [Lua programming language]: https://www.lua.org/
|
||||
//! [`Lua`]: struct.Lua.html
|
||||
@@ -45,16 +57,24 @@
|
||||
//! [`FromLua`]: trait.FromLua.html
|
||||
//! [`ToLuaMulti`]: trait.ToLuaMulti.html
|
||||
//! [`FromLuaMulti`]: trait.FromLuaMulti.html
|
||||
//! [`Function`]: struct.Function.html
|
||||
//! [`UserData`]: trait.UserData.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
|
||||
//! [`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.5.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))))]
|
||||
#![cfg_attr(docsrs, feature(doc_cfg))]
|
||||
|
||||
#[macro_use]
|
||||
mod macros;
|
||||
@@ -81,7 +101,7 @@ pub use crate::ffi::lua_State;
|
||||
pub use crate::error::{Error, ExternalError, ExternalResult, Result};
|
||||
pub use crate::function::Function;
|
||||
pub use crate::hook::{Debug, DebugNames, DebugSource, DebugStack, HookTriggers};
|
||||
pub use crate::lua::{Chunk, GCMode, Lua};
|
||||
pub use crate::lua::{Chunk, ChunkMode, GCMode, Lua};
|
||||
pub use crate::multi::Variadic;
|
||||
pub use crate::scope::Scope;
|
||||
pub use crate::stdlib::StdLib;
|
||||
@@ -95,4 +115,19 @@ pub use crate::value::{FromLua, FromLuaMulti, MultiValue, Nil, ToLua, ToLuaMulti
|
||||
#[cfg(feature = "async")]
|
||||
pub use crate::thread::AsyncThread;
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
pub use crate::serde::LuaSerdeExt;
|
||||
|
||||
pub mod prelude;
|
||||
#[cfg(feature = "serialize")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "serialize")))]
|
||||
pub mod serde;
|
||||
|
||||
// Re-export #[mlua_derive::lua_module].
|
||||
#[cfg(feature = "mlua_derive")]
|
||||
#[allow(unused_imports)]
|
||||
#[macro_use]
|
||||
extern crate mlua_derive;
|
||||
#[cfg(feature = "mlua_derive")]
|
||||
#[doc(hidden)]
|
||||
pub use mlua_derive::*;
|
||||
|
||||
+253
-92
@@ -1,9 +1,10 @@
|
||||
use std::any::TypeId;
|
||||
use std::cell::{RefCell, UnsafeCell};
|
||||
use std::collections::HashMap;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::ffi::CString;
|
||||
use std::marker::PhantomData;
|
||||
use std::os::raw::{c_char, c_int, c_void};
|
||||
use std::panic::resume_unwind;
|
||||
use std::sync::{Arc, Mutex, Weak};
|
||||
use std::{mem, ptr, str};
|
||||
|
||||
@@ -18,13 +19,14 @@ use crate::table::Table;
|
||||
use crate::thread::Thread;
|
||||
use crate::types::{
|
||||
Callback, HookCallback, Integer, LightUserData, LuaRef, MaybeSend, Number, RegistryKey,
|
||||
UserDataCell,
|
||||
};
|
||||
use crate::userdata::{AnyUserData, MetaMethod, UserData, UserDataMethods};
|
||||
use crate::userdata::{AnyUserData, MetaMethod, UserData, UserDataMethods, UserDataWrapped};
|
||||
use crate::util::{
|
||||
assert_stack, callback_error, check_stack, get_gc_userdata, get_main_state,
|
||||
get_meta_gc_userdata, get_wrapped_error, init_error_registry, init_gc_metatable_for,
|
||||
init_userdata_metatable, pop_error, protect_lua, protect_lua_closure, push_gc_userdata,
|
||||
push_meta_gc_userdata, push_string, push_userdata, push_wrapped_error, StackGuard,
|
||||
assert_stack, callback_error, check_stack, get_gc_userdata, get_main_state, get_userdata,
|
||||
get_wrapped_error, init_error_registry, init_gc_metatable_for, init_userdata_metatable,
|
||||
pop_error, protect_lua, protect_lua_closure, push_gc_userdata, push_meta_gc_userdata,
|
||||
push_string, push_userdata, push_wrapped_error, StackGuard, WrappedPanic,
|
||||
};
|
||||
use crate::value::{FromLua, FromLuaMulti, MultiValue, Nil, ToLua, ToLuaMulti, Value};
|
||||
|
||||
@@ -39,6 +41,9 @@ use {
|
||||
futures_util::future::{self, TryFutureExt},
|
||||
};
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
use {crate::util::get_destructed_userdata_metatable, serde::Serialize};
|
||||
|
||||
/// Top level Lua struct which holds the Lua state itself.
|
||||
pub struct Lua {
|
||||
pub(crate) state: *mut ffi::lua_State,
|
||||
@@ -53,8 +58,10 @@ pub struct Lua {
|
||||
// Data associated with the lua_State.
|
||||
struct ExtraData {
|
||||
registered_userdata: HashMap<TypeId, c_int>,
|
||||
registered_userdata_mt: HashSet<isize>,
|
||||
registry_unref_list: Arc<Mutex<Option<Vec<c_int>>>>,
|
||||
|
||||
libs: StdLib,
|
||||
mem_info: *mut MemoryInfo,
|
||||
|
||||
ref_thread: *mut ffi::lua_State,
|
||||
@@ -94,19 +101,22 @@ pub(crate) static EXTRA_REGISTRY_KEY: u8 = 0;
|
||||
|
||||
/// Requires `feature = "send"`
|
||||
#[cfg(feature = "send")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "send")))]
|
||||
unsafe impl Send for Lua {}
|
||||
|
||||
impl Drop for Lua {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
if !self.ephemeral {
|
||||
let mut extra = mlua_expect!(self.extra.lock(), "extra is poisoned");
|
||||
let extra = mlua_expect!(self.extra.lock(), "extra is poisoned");
|
||||
mlua_debug_assert!(
|
||||
ffi::lua_gettop(extra.ref_thread) == extra.ref_stack_max
|
||||
&& extra.ref_stack_max as usize == extra.ref_free.len(),
|
||||
"reference leak detected"
|
||||
);
|
||||
*mlua_expect!(extra.registry_unref_list.lock(), "unref list poisoned") = None;
|
||||
let mut unref_list =
|
||||
mlua_expect!(extra.registry_unref_list.lock(), "unref list poisoned");
|
||||
*unref_list = None;
|
||||
ffi::lua_close(self.main_state.expect("main_state is null"));
|
||||
if !extra.mem_info.is_null() {
|
||||
Box::from_raw(extra.mem_info);
|
||||
@@ -164,7 +174,9 @@ impl Lua {
|
||||
|
||||
let mut lua = unsafe { Self::unsafe_new_with(libs) };
|
||||
|
||||
mlua_expect!(lua.disable_c_modules(), "Error during disabling C modules");
|
||||
if libs.contains(StdLib::PACKAGE) {
|
||||
mlua_expect!(lua.disable_c_modules(), "Error during disabling C modules");
|
||||
}
|
||||
lua.safe = true;
|
||||
|
||||
Ok(lua)
|
||||
@@ -254,7 +266,7 @@ impl Lua {
|
||||
lua.ephemeral = false;
|
||||
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
|
||||
{
|
||||
lua.extra.lock().unwrap().mem_info = mem_info;
|
||||
mlua_expect!(lua.extra.lock(), "extra is poisoned").mem_info = mem_info;
|
||||
}
|
||||
|
||||
mlua_expect!(
|
||||
@@ -263,6 +275,7 @@ impl Lua {
|
||||
}),
|
||||
"Error during loading standard libraries"
|
||||
);
|
||||
mlua_expect!(lua.extra.lock(), "extra is poisoned").libs |= libs;
|
||||
|
||||
lua
|
||||
}
|
||||
@@ -292,6 +305,10 @@ impl Lua {
|
||||
init_gc_metatable_for::<Waker>(state, None);
|
||||
}
|
||||
|
||||
// Init serde metatables
|
||||
#[cfg(feature = "serialize")]
|
||||
crate::serde::init_metatables(state);
|
||||
|
||||
// Create ref stack thread and place it in the registry to prevent it from being garbage
|
||||
// collected.
|
||||
|
||||
@@ -306,8 +323,10 @@ impl Lua {
|
||||
|
||||
let extra = Arc::new(Mutex::new(ExtraData {
|
||||
registered_userdata: HashMap::new(),
|
||||
registered_userdata_mt: HashSet::new(),
|
||||
registry_unref_list: Arc::new(Mutex::new(Some(Vec::new()))),
|
||||
ref_thread,
|
||||
libs: StdLib::NONE,
|
||||
mem_info: ptr::null_mut(),
|
||||
// We need 1 extra stack space to move values in and out of the ref stack.
|
||||
ref_stack_size: ffi::LUA_MINSTACK - 1,
|
||||
@@ -368,11 +387,20 @@ impl Lua {
|
||||
}
|
||||
|
||||
let state = self.main_state.unwrap_or(self.state);
|
||||
unsafe {
|
||||
let res = unsafe {
|
||||
protect_lua_closure(state, 0, 0, |state| {
|
||||
load_from_std_lib(state, libs);
|
||||
})
|
||||
};
|
||||
|
||||
// If `package` library loaded into a safe lua state then disable C modules
|
||||
let curr_libs = mlua_expect!(self.extra.lock(), "extra is poisoned").libs;
|
||||
if self.safe && (curr_libs ^ (curr_libs | libs)).contains(StdLib::PACKAGE) {
|
||||
mlua_expect!(self.disable_c_modules(), "Error during disabling C modules");
|
||||
}
|
||||
mlua_expect!(self.extra.lock(), "extra is poisoned").libs |= libs;
|
||||
|
||||
res
|
||||
}
|
||||
|
||||
/// Consumes and leaks `Lua` object, returning a static reference `&'static Lua`.
|
||||
@@ -490,7 +518,7 @@ impl Lua {
|
||||
unsafe { (*extra.mem_info).used_memory as usize }
|
||||
}
|
||||
|
||||
/// Sets a memory limit on this Lua state.
|
||||
/// Sets a memory limit (in bytes) on this Lua state.
|
||||
///
|
||||
/// Once an allocation occurs that would pass this memory limit,
|
||||
/// a `Error::MemoryError` is generated instead.
|
||||
@@ -658,6 +686,9 @@ impl Lua {
|
||||
/// similar on the returned builder. Code is not even parsed until one of these methods is
|
||||
/// called.
|
||||
///
|
||||
/// If this `Lua` was created with `unsafe_new`, `load` will automatically detect and load
|
||||
/// chunks of either text or binary type, as if passing `bt` mode to `luaL_loadbufferx`.
|
||||
///
|
||||
/// [`Chunk::exec`]: struct.Chunk.html#method.exec
|
||||
pub fn load<'lua, 'a, S>(&'lua self, source: &'a S) -> Chunk<'lua, 'a>
|
||||
where
|
||||
@@ -668,6 +699,7 @@ impl Lua {
|
||||
source: source.as_ref(),
|
||||
name: None,
|
||||
env: None,
|
||||
mode: None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -676,28 +708,35 @@ impl Lua {
|
||||
source: &[u8],
|
||||
name: Option<&CString>,
|
||||
env: Option<Value<'lua>>,
|
||||
mode: Option<ChunkMode>,
|
||||
) -> Result<Function<'lua>> {
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(self.state);
|
||||
assert_stack(self.state, 1);
|
||||
|
||||
match if let Some(name) = name {
|
||||
ffi::luaL_loadbufferx(
|
||||
self.state,
|
||||
source.as_ptr() as *const c_char,
|
||||
source.len(),
|
||||
name.as_ptr() as *const c_char,
|
||||
cstr!("t"),
|
||||
)
|
||||
} else {
|
||||
ffi::luaL_loadbufferx(
|
||||
self.state,
|
||||
source.as_ptr() as *const c_char,
|
||||
source.len(),
|
||||
ptr::null(),
|
||||
cstr!("t"),
|
||||
)
|
||||
} {
|
||||
let mode_str = match mode {
|
||||
Some(ChunkMode::Binary) if self.safe => {
|
||||
return Err(Error::SafetyError(
|
||||
"binary chunks are disabled in safe mode".to_string(),
|
||||
))
|
||||
}
|
||||
Some(ChunkMode::Binary) => cstr!("b"),
|
||||
Some(ChunkMode::Text) => cstr!("t"),
|
||||
None if source.starts_with(ffi::LUA_SIGNATURE) && self.safe => {
|
||||
return Err(Error::SafetyError(
|
||||
"binary chunks are disabled in safe mode".to_string(),
|
||||
))
|
||||
}
|
||||
None => cstr!("bt"),
|
||||
};
|
||||
|
||||
match ffi::luaL_loadbufferx(
|
||||
self.state,
|
||||
source.as_ptr() as *const c_char,
|
||||
source.len(),
|
||||
name.map(|n| n.as_ptr()).unwrap_or_else(ptr::null),
|
||||
mode_str,
|
||||
) {
|
||||
ffi::LUA_OK => {
|
||||
if let Some(env) = env {
|
||||
self.push_value(env)?;
|
||||
@@ -728,7 +767,7 @@ impl Lua {
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates and returns a new table.
|
||||
/// Creates and returns a new empty table.
|
||||
pub fn create_table(&self) -> Result<Table> {
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(self.state);
|
||||
@@ -742,6 +781,21 @@ impl Lua {
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates and returns a new empty table, with the specified capacity.
|
||||
/// `narr` is a hint for how many elements the table will have as a sequence;
|
||||
/// `nrec` is a hint for how many other elements the table will have.
|
||||
/// Lua may use these hints to preallocate memory for the new table.
|
||||
pub fn create_table_with_capacity(&self, narr: c_int, nrec: c_int) -> Result<Table> {
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(self.state);
|
||||
assert_stack(self.state, 4);
|
||||
protect_lua_closure(self.state, 0, 1, |state| {
|
||||
ffi::lua_createtable(state, narr, nrec)
|
||||
})?;
|
||||
Ok(Table(self.pop_ref()))
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a table and fills it with values from an iterator.
|
||||
pub fn create_table_from<'lua, K, V, I>(&'lua self, cont: I) -> Result<Table<'lua>>
|
||||
where
|
||||
@@ -903,8 +957,9 @@ impl Lua {
|
||||
/// ```
|
||||
///
|
||||
/// [`Thread`]: struct.Thread.html
|
||||
/// [`ThreadStream`]: struct.ThreadStream.html
|
||||
/// [`AsyncThread`]: struct.AsyncThread.html
|
||||
#[cfg(feature = "async")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
|
||||
pub fn create_async_function<'lua, 'callback, A, R, F, FR>(
|
||||
&'lua self,
|
||||
func: F,
|
||||
@@ -947,7 +1002,19 @@ impl Lua {
|
||||
where
|
||||
T: 'static + MaybeSend + UserData,
|
||||
{
|
||||
unsafe { self.make_userdata(data) }
|
||||
unsafe { self.make_userdata(UserDataWrapped::new(data)) }
|
||||
}
|
||||
|
||||
/// Create a Lua userdata object from a custom serializable userdata type.
|
||||
///
|
||||
/// Requires `feature = "serialize"`
|
||||
#[cfg(feature = "serialize")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "serialize")))]
|
||||
pub fn create_ser_userdata<T>(&self, data: T) -> Result<AnyUserData>
|
||||
where
|
||||
T: 'static + MaybeSend + UserData + Serialize,
|
||||
{
|
||||
unsafe { self.make_userdata(UserDataWrapped::new_ser(data)) }
|
||||
}
|
||||
|
||||
/// Returns a handle to the global environment.
|
||||
@@ -1008,6 +1075,7 @@ impl Lua {
|
||||
///
|
||||
/// [`scope`]: #method.scope
|
||||
#[cfg(feature = "async")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
|
||||
pub fn async_scope<'lua, 'scope, R, F, FR>(
|
||||
&'lua self,
|
||||
f: F,
|
||||
@@ -1274,11 +1342,10 @@ impl Lua {
|
||||
/// by `Lua::remove_registry_value`.
|
||||
pub fn expire_registry_values(&self) {
|
||||
unsafe {
|
||||
let mut extra = mlua_expect!(self.extra.lock(), "extra is poisoned");
|
||||
let unref_list = mem::replace(
|
||||
&mut *mlua_expect!(extra.registry_unref_list.lock(), "unref list poisoned"),
|
||||
Some(Vec::new()),
|
||||
);
|
||||
let extra = mlua_expect!(self.extra.lock(), "extra is poisoned");
|
||||
let mut unref_list =
|
||||
mlua_expect!(extra.registry_unref_list.lock(), "unref list poisoned");
|
||||
let unref_list = mem::replace(&mut *unref_list, Some(Vec::new()));
|
||||
for id in mlua_expect!(unref_list, "unref list not set") {
|
||||
ffi::luaL_unref(self.state, ffi::LUA_REGISTRYINDEX, id);
|
||||
}
|
||||
@@ -1338,32 +1405,33 @@ impl Lua {
|
||||
|
||||
// Uses 2 stack spaces, does not call checkstack
|
||||
pub(crate) unsafe fn pop_value(&self) -> Value {
|
||||
match ffi::lua_type(self.state, -1) {
|
||||
let state = self.state;
|
||||
match ffi::lua_type(state, -1) {
|
||||
ffi::LUA_TNIL => {
|
||||
ffi::lua_pop(self.state, 1);
|
||||
ffi::lua_pop(state, 1);
|
||||
Nil
|
||||
}
|
||||
|
||||
ffi::LUA_TBOOLEAN => {
|
||||
let b = Value::Boolean(ffi::lua_toboolean(self.state, -1) != 0);
|
||||
ffi::lua_pop(self.state, 1);
|
||||
let b = Value::Boolean(ffi::lua_toboolean(state, -1) != 0);
|
||||
ffi::lua_pop(state, 1);
|
||||
b
|
||||
}
|
||||
|
||||
ffi::LUA_TLIGHTUSERDATA => {
|
||||
let ud = Value::LightUserData(LightUserData(ffi::lua_touserdata(self.state, -1)));
|
||||
ffi::lua_pop(self.state, 1);
|
||||
let ud = Value::LightUserData(LightUserData(ffi::lua_touserdata(state, -1)));
|
||||
ffi::lua_pop(state, 1);
|
||||
ud
|
||||
}
|
||||
|
||||
ffi::LUA_TNUMBER => {
|
||||
if ffi::lua_isinteger(self.state, -1) != 0 {
|
||||
let i = Value::Integer(ffi::lua_tointeger(self.state, -1));
|
||||
ffi::lua_pop(self.state, 1);
|
||||
if ffi::lua_isinteger(state, -1) != 0 {
|
||||
let i = Value::Integer(ffi::lua_tointeger(state, -1));
|
||||
ffi::lua_pop(state, 1);
|
||||
i
|
||||
} else {
|
||||
let n = Value::Number(ffi::lua_tonumber(self.state, -1));
|
||||
ffi::lua_pop(self.state, 1);
|
||||
let n = Value::Number(ffi::lua_tonumber(state, -1));
|
||||
ffi::lua_pop(state, 1);
|
||||
n
|
||||
}
|
||||
}
|
||||
@@ -1375,12 +1443,20 @@ impl Lua {
|
||||
ffi::LUA_TFUNCTION => Value::Function(Function(self.pop_ref())),
|
||||
|
||||
ffi::LUA_TUSERDATA => {
|
||||
// It should not be possible to interact with userdata types other than custom
|
||||
// UserData types OR a WrappedError. WrappedPanic should not be here.
|
||||
if let Some(err) = get_wrapped_error(self.state, -1).as_ref() {
|
||||
// We must prevent interaction with userdata types other than UserData OR a WrappedError.
|
||||
// WrappedPanics are automatically resumed.
|
||||
if let Some(err) = get_wrapped_error(state, -1).as_ref() {
|
||||
let err = err.clone();
|
||||
ffi::lua_pop(self.state, 1);
|
||||
ffi::lua_pop(state, 1);
|
||||
Value::Error(err)
|
||||
} else if let Some(panic) = get_gc_userdata::<WrappedPanic>(state, -1).as_mut() {
|
||||
if let Some(panic) = (*panic).0.take() {
|
||||
ffi::lua_pop(state, 1);
|
||||
resume_unwind(panic);
|
||||
}
|
||||
// Previously resumed panic?
|
||||
ffi::lua_pop(state, 1);
|
||||
Nil
|
||||
} else {
|
||||
Value::UserData(AnyUserData(self.pop_ref()))
|
||||
}
|
||||
@@ -1438,9 +1514,10 @@ impl Lua {
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn userdata_metatable<T: 'static + UserData>(&self) -> Result<c_int> {
|
||||
let type_id = TypeId::of::<T>();
|
||||
if let Some(table_id) = mlua_expect!(self.extra.lock(), "extra is poisoned")
|
||||
.registered_userdata
|
||||
.get(&TypeId::of::<T>())
|
||||
.get(&type_id)
|
||||
{
|
||||
return Ok(*table_id);
|
||||
}
|
||||
@@ -1469,7 +1546,7 @@ impl Lua {
|
||||
let no_methods = methods.methods.is_empty();
|
||||
|
||||
if no_methods {
|
||||
init_userdata_metatable::<RefCell<T>>(self.state, -1, None)?;
|
||||
init_userdata_metatable::<UserDataCell<T>>(self.state, -1, None)?;
|
||||
} else {
|
||||
protect_lua_closure(self.state, 0, 1, |state| {
|
||||
ffi::lua_newtable(state);
|
||||
@@ -1490,21 +1567,58 @@ impl Lua {
|
||||
})?;
|
||||
}
|
||||
|
||||
init_userdata_metatable::<RefCell<T>>(self.state, -2, Some(-1))?;
|
||||
init_userdata_metatable::<UserDataCell<T>>(self.state, -2, Some(-1))?;
|
||||
ffi::lua_pop(self.state, 1);
|
||||
}
|
||||
|
||||
let ptr = ffi::lua_topointer(self.state, -1);
|
||||
let id = protect_lua_closure(self.state, 1, 0, |state| {
|
||||
ffi::luaL_ref(state, ffi::LUA_REGISTRYINDEX)
|
||||
})?;
|
||||
|
||||
mlua_expect!(self.extra.lock(), "extra is poisoned")
|
||||
.registered_userdata
|
||||
.insert(TypeId::of::<T>(), id);
|
||||
let mut extra = mlua_expect!(self.extra.lock(), "extra is poisoned");
|
||||
extra.registered_userdata.insert(type_id, id);
|
||||
extra.registered_userdata_mt.insert(ptr as isize);
|
||||
|
||||
Ok(id)
|
||||
}
|
||||
|
||||
pub(crate) fn register_userdata_metatable(&self, id: isize) {
|
||||
let mut extra = mlua_expect!(self.extra.lock(), "extra is poisoned");
|
||||
extra.registered_userdata_mt.insert(id);
|
||||
}
|
||||
|
||||
pub(crate) fn deregister_userdata_metatable(&self, id: isize) {
|
||||
let mut extra = mlua_expect!(self.extra.lock(), "extra is poisoned");
|
||||
extra.registered_userdata_mt.remove(&id);
|
||||
}
|
||||
|
||||
// Pushes a LuaRef value onto the stack, checking that it's a registered
|
||||
// and not destructed UserData.
|
||||
// Uses 2 stack spaces, does not call checkstack
|
||||
#[cfg(feature = "serialize")]
|
||||
pub(crate) unsafe fn push_userdata_ref(&self, lref: &LuaRef) -> Result<()> {
|
||||
self.push_ref(lref);
|
||||
if ffi::lua_getmetatable(self.state, -1) == 0 {
|
||||
return Err(Error::UserDataTypeMismatch);
|
||||
}
|
||||
// Check that userdata is registered
|
||||
let ptr = ffi::lua_topointer(self.state, -1);
|
||||
let extra = mlua_expect!(self.extra.lock(), "extra is poisoned");
|
||||
if extra.registered_userdata_mt.contains(&(ptr as isize)) {
|
||||
ffi::lua_pop(self.state, 1);
|
||||
return Ok(());
|
||||
}
|
||||
// Maybe userdata was destructed?
|
||||
get_destructed_userdata_metatable(self.state);
|
||||
if ffi::lua_rawequal(self.state, -1, -2) != 0 {
|
||||
ffi::lua_pop(self.state, 2);
|
||||
return Err(Error::UserDataDestructed);
|
||||
}
|
||||
ffi::lua_pop(self.state, 2);
|
||||
Err(Error::UserDataTypeMismatch)
|
||||
}
|
||||
|
||||
// Creates a Function out of a Callback containing a 'static Fn. This is safe ONLY because the
|
||||
// Fn is 'static, otherwise it could capture 'callback arguments improperly. Without ATCs, we
|
||||
// cannot easily deal with the "correct" callback type of:
|
||||
@@ -1522,12 +1636,13 @@ impl Lua {
|
||||
{
|
||||
unsafe extern "C" fn call_callback(state: *mut ffi::lua_State) -> c_int {
|
||||
callback_error(state, |nargs| {
|
||||
let func =
|
||||
get_meta_gc_userdata::<Callback, Callback>(state, ffi::lua_upvalueindex(1));
|
||||
let lua = get_gc_userdata::<Lua>(state, ffi::lua_upvalueindex(2));
|
||||
if func.is_null() || lua.is_null() {
|
||||
if ffi::lua_type(state, ffi::lua_upvalueindex(1)) == ffi::LUA_TNIL
|
||||
|| ffi::lua_type(state, ffi::lua_upvalueindex(2)) == ffi::LUA_TNIL
|
||||
{
|
||||
return Err(Error::CallbackDestructed);
|
||||
}
|
||||
let func = get_userdata::<Callback>(state, ffi::lua_upvalueindex(1));
|
||||
let lua = get_userdata::<Lua>(state, ffi::lua_upvalueindex(2));
|
||||
|
||||
if nargs < ffi::LUA_MINSTACK {
|
||||
check_stack(state, ffi::LUA_MINSTACK - nargs)?;
|
||||
@@ -1578,18 +1693,22 @@ impl Lua {
|
||||
'lua: 'callback,
|
||||
{
|
||||
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
|
||||
self.load_from_std_lib(StdLib::COROUTINE)?;
|
||||
{
|
||||
let libs = mlua_expect!(self.extra.lock(), "extra is poisoned").libs;
|
||||
if !libs.contains(StdLib::COROUTINE) {
|
||||
self.load_from_std_lib(StdLib::COROUTINE)?;
|
||||
}
|
||||
}
|
||||
|
||||
unsafe extern "C" fn call_callback(state: *mut ffi::lua_State) -> c_int {
|
||||
callback_error(state, |nargs| {
|
||||
let func = get_meta_gc_userdata::<AsyncCallback, AsyncCallback>(
|
||||
state,
|
||||
ffi::lua_upvalueindex(1),
|
||||
);
|
||||
let lua = get_gc_userdata::<Lua>(state, ffi::lua_upvalueindex(2));
|
||||
if func.is_null() || lua.is_null() {
|
||||
if ffi::lua_type(state, ffi::lua_upvalueindex(1)) == ffi::LUA_TNIL
|
||||
|| ffi::lua_type(state, ffi::lua_upvalueindex(2)) == ffi::LUA_TNIL
|
||||
{
|
||||
return Err(Error::CallbackDestructed);
|
||||
}
|
||||
let func = get_userdata::<AsyncCallback>(state, ffi::lua_upvalueindex(1));
|
||||
let lua = get_userdata::<Lua>(state, ffi::lua_upvalueindex(2));
|
||||
|
||||
if nargs < ffi::LUA_MINSTACK {
|
||||
check_stack(state, ffi::LUA_MINSTACK - nargs)?;
|
||||
@@ -1616,14 +1735,16 @@ impl Lua {
|
||||
|
||||
unsafe extern "C" fn poll_future(state: *mut ffi::lua_State) -> c_int {
|
||||
callback_error(state, |nargs| {
|
||||
let fut = get_gc_userdata::<LocalBoxFuture<Result<MultiValue>>>(
|
||||
if ffi::lua_type(state, ffi::lua_upvalueindex(1)) == ffi::LUA_TNIL
|
||||
|| ffi::lua_type(state, ffi::lua_upvalueindex(2)) == ffi::LUA_TNIL
|
||||
{
|
||||
return Err(Error::CallbackDestructed);
|
||||
}
|
||||
let fut = get_userdata::<LocalBoxFuture<Result<MultiValue>>>(
|
||||
state,
|
||||
ffi::lua_upvalueindex(1),
|
||||
);
|
||||
let lua = get_gc_userdata::<Lua>(state, ffi::lua_upvalueindex(2));
|
||||
if fut.is_null() || lua.is_null() {
|
||||
return Err(Error::CallbackDestructed);
|
||||
}
|
||||
let lua = get_userdata::<Lua>(state, ffi::lua_upvalueindex(2));
|
||||
|
||||
if nargs < ffi::LUA_MINSTACK {
|
||||
check_stack(state, ffi::LUA_MINSTACK - nargs)?;
|
||||
@@ -1644,17 +1765,19 @@ impl Lua {
|
||||
|
||||
match (*fut).as_mut().poll(&mut ctx) {
|
||||
Poll::Pending => {
|
||||
check_stack(state, 6)?;
|
||||
check_stack(state, 1)?;
|
||||
ffi::lua_pushboolean(state, 0);
|
||||
push_gc_userdata(state, AsyncPollPending)?;
|
||||
Ok(2)
|
||||
Ok(1)
|
||||
}
|
||||
Poll::Ready(results) => {
|
||||
let results = lua.create_sequence_from(results?)?;
|
||||
check_stack(state, 2)?;
|
||||
let results = results?;
|
||||
let nresults = results.len() as Integer;
|
||||
let results = lua.create_sequence_from(results)?;
|
||||
check_stack(state, 3)?;
|
||||
ffi::lua_pushboolean(state, 1);
|
||||
lua.push_value(Value::Table(results))?;
|
||||
Ok(2)
|
||||
lua.push_value(Value::Integer(nresults))?;
|
||||
Ok(3)
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -1681,22 +1804,31 @@ impl Lua {
|
||||
env.set("yield", coroutine.get::<_, Function>("yield")?)?;
|
||||
env.set(
|
||||
"unpack",
|
||||
self.create_function(|_, tbl: Table| {
|
||||
self.create_function(|_, (tbl, len): (Table, Integer)| {
|
||||
Ok(MultiValue::from_vec(
|
||||
tbl.sequence_values().collect::<Result<Vec<Value>>>()?,
|
||||
tbl.raw_sequence_values_by_len(Some(len))
|
||||
.collect::<Result<Vec<Value>>>()?,
|
||||
))
|
||||
})?,
|
||||
)?;
|
||||
env.set("pending", unsafe {
|
||||
let _sg = StackGuard::new(self.state);
|
||||
check_stack(self.state, 5)?;
|
||||
push_gc_userdata(self.state, AsyncPollPending)?;
|
||||
self.pop_value()
|
||||
})?;
|
||||
|
||||
// We set `poll` variable in the env table to be able to destroy upvalues
|
||||
self.load(
|
||||
r#"
|
||||
local poll = get_poll(...)
|
||||
poll = get_poll(...)
|
||||
local poll, pending, yield, unpack = poll, pending, yield, unpack
|
||||
while true do
|
||||
ready, res = poll()
|
||||
local ready, res, nres = poll()
|
||||
if ready then
|
||||
return unpack(res)
|
||||
return unpack(res, nres)
|
||||
end
|
||||
yield(res)
|
||||
yield(pending)
|
||||
end
|
||||
"#,
|
||||
)
|
||||
@@ -1705,7 +1837,7 @@ impl Lua {
|
||||
.into_function()
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn make_userdata<T>(&self, data: T) -> Result<AnyUserData>
|
||||
pub(crate) unsafe fn make_userdata<T>(&self, data: UserDataWrapped<T>) -> Result<AnyUserData>
|
||||
where
|
||||
T: 'static + UserData,
|
||||
{
|
||||
@@ -1713,7 +1845,7 @@ impl Lua {
|
||||
assert_stack(self.state, 4);
|
||||
|
||||
let ud_index = self.userdata_metatable::<T>()?;
|
||||
push_userdata::<RefCell<T>>(self.state, RefCell::new(data))?;
|
||||
push_userdata::<UserDataCell<T>>(self.state, RefCell::new(data))?;
|
||||
|
||||
ffi::lua_rawgeti(
|
||||
self.state,
|
||||
@@ -1802,6 +1934,14 @@ pub struct Chunk<'lua, 'a> {
|
||||
source: &'a [u8],
|
||||
name: Option<CString>,
|
||||
env: Option<Value<'lua>>,
|
||||
mode: Option<ChunkMode>,
|
||||
}
|
||||
|
||||
/// Represents chunk mode (text or binary).
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum ChunkMode {
|
||||
Text,
|
||||
Binary,
|
||||
}
|
||||
|
||||
impl<'lua, 'a> Chunk<'lua, 'a> {
|
||||
@@ -1833,6 +1973,17 @@ impl<'lua, 'a> Chunk<'lua, 'a> {
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
/// Sets whether the chunk is text or binary (autodetected by default).
|
||||
///
|
||||
/// Lua does not check the consistency of binary chunks, therefore this mode is allowed only
|
||||
/// for instances created with [`Lua::unsafe_new`].
|
||||
///
|
||||
/// [`Lua::unsafe_new`]: struct.Lua.html#method.unsafe_new
|
||||
pub fn set_mode(mut self, mode: ChunkMode) -> Chunk<'lua, 'a> {
|
||||
self.mode = Some(mode);
|
||||
self
|
||||
}
|
||||
|
||||
/// Execute this chunk of code.
|
||||
///
|
||||
/// This is equivalent to calling the chunk function with no arguments and no return values.
|
||||
@@ -1849,6 +2000,7 @@ impl<'lua, 'a> Chunk<'lua, 'a> {
|
||||
///
|
||||
/// [`Chunk::exec`]: struct.Chunk.html#method.exec
|
||||
#[cfg(feature = "async")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
|
||||
pub fn exec_async<'fut>(self) -> LocalBoxFuture<'fut, Result<()>>
|
||||
where
|
||||
'lua: 'fut,
|
||||
@@ -1862,13 +2014,17 @@ impl<'lua, 'a> Chunk<'lua, 'a> {
|
||||
/// the value that it evaluates to. Otherwise, the chunk is interpreted as a block as normal,
|
||||
/// and this is equivalent to calling `exec`.
|
||||
pub fn eval<R: FromLuaMulti<'lua>>(self) -> Result<R> {
|
||||
// First, try interpreting the lua as an expression by adding
|
||||
// Bytecode is always interpreted as a statement.
|
||||
// For source code, first try interpreting the lua as an expression by adding
|
||||
// "return", then as a statement. This is the same thing the
|
||||
// actual lua repl does.
|
||||
if let Ok(function) = self.lua.load_chunk(
|
||||
if self.source.starts_with(ffi::LUA_SIGNATURE) {
|
||||
self.call(())
|
||||
} else if let Ok(function) = self.lua.load_chunk(
|
||||
&self.expression_source(),
|
||||
self.name.as_ref(),
|
||||
self.env.clone(),
|
||||
self.mode,
|
||||
) {
|
||||
function.call(())
|
||||
} else {
|
||||
@@ -1884,15 +2040,19 @@ impl<'lua, 'a> Chunk<'lua, 'a> {
|
||||
///
|
||||
/// [`Chunk::eval`]: struct.Chunk.html#method.eval
|
||||
#[cfg(feature = "async")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
|
||||
pub fn eval_async<'fut, R>(self) -> LocalBoxFuture<'fut, Result<R>>
|
||||
where
|
||||
'lua: 'fut,
|
||||
R: FromLuaMulti<'lua> + 'fut,
|
||||
{
|
||||
if let Ok(function) = self.lua.load_chunk(
|
||||
if self.source.starts_with(ffi::LUA_SIGNATURE) {
|
||||
self.call_async(())
|
||||
} else if let Ok(function) = self.lua.load_chunk(
|
||||
&self.expression_source(),
|
||||
self.name.as_ref(),
|
||||
self.env.clone(),
|
||||
self.mode,
|
||||
) {
|
||||
function.call_async(())
|
||||
} else {
|
||||
@@ -1915,6 +2075,7 @@ impl<'lua, 'a> Chunk<'lua, 'a> {
|
||||
///
|
||||
/// [`Chunk::call`]: struct.Chunk.html#method.call
|
||||
#[cfg(feature = "async")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
|
||||
pub fn call_async<'fut, A, R>(self, args: A) -> LocalBoxFuture<'fut, Result<R>>
|
||||
where
|
||||
'lua: 'fut,
|
||||
@@ -1932,7 +2093,7 @@ impl<'lua, 'a> Chunk<'lua, 'a> {
|
||||
/// This simply compiles the chunk without actually executing it.
|
||||
pub fn into_function(self) -> Result<Function<'lua>> {
|
||||
self.lua
|
||||
.load_chunk(self.source, self.name.as_ref(), self.env)
|
||||
.load_chunk(self.source, self.name.as_ref(), self.env, self.mode)
|
||||
}
|
||||
|
||||
fn expression_source(&self) -> Vec<u8> {
|
||||
|
||||
+1
-1
@@ -34,7 +34,7 @@ impl<'lua, T: ToLua<'lua>> ToLuaMulti<'lua> for T {
|
||||
|
||||
impl<'lua, T: FromLua<'lua>> FromLuaMulti<'lua> for T {
|
||||
fn from_lua_multi(mut values: MultiValue<'lua>, lua: &'lua Lua) -> Result<Self> {
|
||||
Ok(T::from_lua(values.pop_front().unwrap_or(Nil), lua)?)
|
||||
T::from_lua(values.pop_front().unwrap_or(Nil), lua)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+91
-29
@@ -1,16 +1,19 @@
|
||||
use std::any::Any;
|
||||
use std::cell::{Cell, RefCell};
|
||||
use std::cell::{Cell, Ref, RefCell, RefMut};
|
||||
use std::marker::PhantomData;
|
||||
use std::mem;
|
||||
use std::os::raw::c_void;
|
||||
use std::rc::Rc;
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
use serde::Serialize;
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
use crate::ffi;
|
||||
use crate::function::Function;
|
||||
use crate::lua::Lua;
|
||||
use crate::types::{Callback, LuaRef, MaybeSend};
|
||||
use crate::userdata::{AnyUserData, MetaMethod, UserData, UserDataMethods};
|
||||
use crate::types::{Callback, LuaRef, MaybeSend, UserDataCell};
|
||||
use crate::userdata::{AnyUserData, MetaMethod, UserData, UserDataMethods, UserDataWrapped};
|
||||
use crate::util::{
|
||||
assert_stack, init_userdata_metatable, protect_lua_closure, push_string, push_userdata,
|
||||
take_userdata, StackGuard,
|
||||
@@ -20,9 +23,8 @@ use crate::value::{FromLuaMulti, MultiValue, ToLuaMulti, Value};
|
||||
#[cfg(feature = "async")]
|
||||
use {
|
||||
crate::types::AsyncCallback,
|
||||
futures_core::future::Future,
|
||||
futures_core::future::{Future, LocalBoxFuture},
|
||||
futures_util::future::{self, TryFutureExt},
|
||||
std::os::raw::c_char,
|
||||
};
|
||||
|
||||
/// Constructed by the [`Lua::scope`] method, allows temporarily creating Lua userdata and
|
||||
@@ -112,6 +114,7 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
|
||||
/// [`Lua::scope`]: struct.Lua.html#method.scope
|
||||
/// [`Lua::async_scope`]: struct.Lua.html#method.async_scope
|
||||
#[cfg(feature = "async")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
|
||||
pub fn create_async_function<'callback, A, R, F, FR>(
|
||||
&'callback self,
|
||||
func: F,
|
||||
@@ -143,6 +146,33 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
|
||||
/// [`Lua::create_userdata`]: struct.Lua.html#method.create_userdata
|
||||
/// [`Lua::scope`]: struct.Lua.html#method.scope
|
||||
pub fn create_userdata<T>(&self, data: T) -> Result<AnyUserData<'lua>>
|
||||
where
|
||||
T: 'static + UserData,
|
||||
{
|
||||
self.create_userdata_inner(UserDataWrapped::new(data))
|
||||
}
|
||||
|
||||
/// Create a Lua userdata object from a custom serializable userdata type.
|
||||
///
|
||||
/// This is a version of [`Lua::create_ser_userdata`] that creates a userdata which expires on
|
||||
/// scope drop, and does not require that the userdata type be Send (but still requires that the
|
||||
/// UserData be 'static).
|
||||
/// See [`Lua::scope`] for more details.
|
||||
///
|
||||
/// Requires `feature = "serialize"`
|
||||
///
|
||||
/// [`Lua::create_ser_userdata`]: struct.Lua.html#method.create_ser_userdata
|
||||
/// [`Lua::scope`]: struct.Lua.html#method.scope
|
||||
#[cfg(feature = "serialize")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "serialize")))]
|
||||
pub fn create_ser_userdata<T>(&self, data: T) -> Result<AnyUserData<'lua>>
|
||||
where
|
||||
T: 'static + UserData + Serialize,
|
||||
{
|
||||
self.create_userdata_inner(UserDataWrapped::new_ser(data))
|
||||
}
|
||||
|
||||
fn create_userdata_inner<T>(&self, data: UserDataWrapped<T>) -> Result<AnyUserData<'lua>>
|
||||
where
|
||||
T: 'static + UserData,
|
||||
{
|
||||
@@ -156,7 +186,7 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
|
||||
u.lua.push_ref(&u);
|
||||
// We know the destructor has not run yet because we hold a reference to the
|
||||
// userdata.
|
||||
vec![Box::new(take_userdata::<RefCell<T>>(state))]
|
||||
vec![Box::new(take_userdata::<UserDataCell<T>>(state))]
|
||||
}));
|
||||
Ok(u)
|
||||
}
|
||||
@@ -189,7 +219,7 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
|
||||
where
|
||||
T: 'scope + UserData,
|
||||
{
|
||||
let data = Rc::new(RefCell::new(data));
|
||||
let data = Rc::new(RefCell::new(UserDataWrapped::new(data)));
|
||||
|
||||
// 'callback outliving 'scope is a lie to make the types work out, required due to the
|
||||
// inability to work with the more correct callback type that is universally quantified over
|
||||
@@ -198,7 +228,7 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
|
||||
// parameters.
|
||||
fn wrap_method<'scope, 'lua, 'callback: 'scope, T: 'scope>(
|
||||
scope: &Scope<'lua, 'scope>,
|
||||
data: Rc<RefCell<T>>,
|
||||
data: Rc<UserDataCell<T>>,
|
||||
method: NonStaticMethod<'callback, T>,
|
||||
) -> Result<Function<'lua>> {
|
||||
// On methods that actually receive the userdata, we fake a type check on the passed in
|
||||
@@ -210,20 +240,18 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
|
||||
// first argument).
|
||||
let check_data = data.clone();
|
||||
let check_ud_type = move |lua: &'callback Lua, value| {
|
||||
if let Some(value) = value {
|
||||
if let Value::UserData(u) = value {
|
||||
unsafe {
|
||||
assert_stack(lua.state, 1);
|
||||
lua.push_ref(&u.0);
|
||||
ffi::lua_getuservalue(lua.state, -1);
|
||||
#[cfg(any(feature = "lua52", feature = "lua51", feature = "luajit"))]
|
||||
{
|
||||
ffi::lua_rawgeti(lua.state, -1, 1);
|
||||
ffi::lua_remove(lua.state, -2);
|
||||
}
|
||||
return ffi::lua_touserdata(lua.state, -1)
|
||||
== check_data.as_ptr() as *mut c_void;
|
||||
if let Some(Value::UserData(ud)) = value {
|
||||
unsafe {
|
||||
assert_stack(lua.state, 1);
|
||||
lua.push_ref(&ud.0);
|
||||
ffi::lua_getuservalue(lua.state, -1);
|
||||
#[cfg(any(feature = "lua52", feature = "lua51", feature = "luajit"))]
|
||||
{
|
||||
ffi::lua_rawgeti(lua.state, -1, 1);
|
||||
ffi::lua_remove(lua.state, -2);
|
||||
}
|
||||
return ffi::lua_touserdata(lua.state, -1)
|
||||
== check_data.as_ptr() as *mut c_void;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -236,7 +264,10 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
|
||||
if !check_ud_type(lua, args.pop_front()) {
|
||||
return Err(Error::UserDataTypeMismatch);
|
||||
}
|
||||
let data = data.try_borrow().map_err(|_| Error::UserDataBorrowError)?;
|
||||
let data = data
|
||||
.try_borrow()
|
||||
.map(|cell| Ref::map(cell, AsRef::as_ref))
|
||||
.map_err(|_| Error::UserDataBorrowError)?;
|
||||
method(lua, &*data, args)
|
||||
});
|
||||
unsafe { scope.create_callback(f) }
|
||||
@@ -252,6 +283,7 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
|
||||
.map_err(|_| Error::RecursiveMutCallback)?;
|
||||
let mut data = data
|
||||
.try_borrow_mut()
|
||||
.map(|cell| RefMut::map(cell, AsMut::as_mut))
|
||||
.map_err(|_| Error::UserDataBorrowMutError)?;
|
||||
(&mut *method)(lua, &mut *data, args)
|
||||
});
|
||||
@@ -280,7 +312,8 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
|
||||
let _sg = StackGuard::new(lua.state);
|
||||
assert_stack(lua.state, 6);
|
||||
|
||||
push_userdata(lua.state, ())?;
|
||||
// We need to wrap dummy userdata because their memory can be accessed by serializer
|
||||
push_userdata(lua.state, UserDataCell::new(UserDataWrapped::new(())))?;
|
||||
#[cfg(any(feature = "lua54", feature = "lua53"))]
|
||||
ffi::lua_pushlightuserdata(lua.state, data.as_ptr() as *mut c_void);
|
||||
#[cfg(any(feature = "lua52", feature = "lua51", feature = "luajit"))]
|
||||
@@ -323,9 +356,22 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
|
||||
ffi::lua_pop(lua.state, 1);
|
||||
}
|
||||
|
||||
let mt_id = ffi::lua_topointer(lua.state, -1);
|
||||
ffi::lua_setmetatable(lua.state, -2);
|
||||
|
||||
Ok(AnyUserData(lua.pop_ref()))
|
||||
let ud = AnyUserData(lua.pop_ref());
|
||||
lua.register_userdata_metatable(mt_id as isize);
|
||||
self.destructors.borrow_mut().push((ud.0.clone(), |ud| {
|
||||
let state = ud.lua.state;
|
||||
assert_stack(state, 2);
|
||||
ud.lua.push_ref(&ud);
|
||||
ffi::lua_getmetatable(state, -1);
|
||||
let mt_id = ffi::lua_topointer(state, -1);
|
||||
ffi::lua_pop(state, 1);
|
||||
ud.lua.deregister_userdata_metatable(mt_id as isize);
|
||||
vec![Box::new(take_userdata::<UserDataCell<()>>(state))]
|
||||
}));
|
||||
Ok(ud)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -387,12 +433,11 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
|
||||
#[cfg(any(feature = "lua51", feature = "luajit"))]
|
||||
ffi::lua_getfenv(state, -1);
|
||||
|
||||
// Then, get the get_poll() closure using the corresponding key
|
||||
let key = "get_poll";
|
||||
ffi::lua_pushlstring(state, key.as_ptr() as *const c_char, key.len());
|
||||
// Second, get the `get_poll()` closure using the corresponding key
|
||||
ffi::lua_pushstring(state, cstr!("get_poll"));
|
||||
ffi::lua_rawget(state, -2);
|
||||
|
||||
// Finally, destroy all upvalues
|
||||
// Destroy all upvalues
|
||||
ffi::lua_getupvalue(state, -1, 1);
|
||||
let ud1 = take_userdata::<AsyncCallback>(state);
|
||||
ffi::lua_pushnil(state);
|
||||
@@ -404,8 +449,25 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
|
||||
ffi::lua_setupvalue(state, -2, 2);
|
||||
|
||||
ffi::lua_pop(state, 1);
|
||||
let mut data: Vec<Box<dyn Any>> = vec![Box::new(ud1), Box::new(ud2)];
|
||||
|
||||
vec![Box::new(ud1), Box::new(ud2)]
|
||||
// Finally, get polled future and destroy it
|
||||
ffi::lua_pushstring(state, cstr!("poll"));
|
||||
if ffi::lua_rawget(state, -2) == ffi::LUA_TFUNCTION {
|
||||
ffi::lua_getupvalue(state, -1, 1);
|
||||
let ud3 = take_userdata::<LocalBoxFuture<Result<MultiValue>>>(state);
|
||||
ffi::lua_pushnil(state);
|
||||
ffi::lua_setupvalue(state, -2, 1);
|
||||
data.push(Box::new(ud3));
|
||||
|
||||
ffi::lua_getupvalue(state, -1, 2);
|
||||
let ud4 = take_userdata::<Lua>(state);
|
||||
ffi::lua_pushnil(state);
|
||||
ffi::lua_setupvalue(state, -2, 2);
|
||||
data.push(Box::new(ud4));
|
||||
}
|
||||
|
||||
data
|
||||
}));
|
||||
|
||||
Ok(f)
|
||||
|
||||
+329
@@ -0,0 +1,329 @@
|
||||
use std::string::String as StdString;
|
||||
|
||||
use serde::de::{self, IntoDeserializer};
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
use crate::table::{TablePairs, TableSequence};
|
||||
use crate::value::Value;
|
||||
|
||||
/// A struct for deserializing Lua values into Rust values.
|
||||
pub struct Deserializer<'lua>(pub Value<'lua>);
|
||||
|
||||
impl<'lua, 'de> serde::Deserializer<'de> for Deserializer<'lua> {
|
||||
type Error = Error;
|
||||
|
||||
#[inline]
|
||||
fn deserialize_any<V>(self, visitor: V) -> Result<V::Value>
|
||||
where
|
||||
V: de::Visitor<'de>,
|
||||
{
|
||||
match self.0 {
|
||||
Value::Nil => visitor.visit_unit(),
|
||||
Value::Boolean(b) => visitor.visit_bool(b),
|
||||
#[allow(clippy::useless_conversion)]
|
||||
Value::Integer(i) => visitor.visit_i64(i.into()),
|
||||
#[allow(clippy::useless_conversion)]
|
||||
Value::Number(n) => visitor.visit_f64(n.into()),
|
||||
Value::String(s) => match s.to_str() {
|
||||
Ok(s) => visitor.visit_str(s),
|
||||
Err(_) => visitor.visit_bytes(s.as_bytes()),
|
||||
},
|
||||
Value::Table(ref t) if t.raw_len() > 0 || t.is_array() => self.deserialize_seq(visitor),
|
||||
Value::Table(_) => self.deserialize_map(visitor),
|
||||
Value::LightUserData(ud) if ud.0.is_null() => visitor.visit_none(),
|
||||
Value::Function(_)
|
||||
| Value::Thread(_)
|
||||
| Value::UserData(_)
|
||||
| Value::LightUserData(_)
|
||||
| Value::Error(_) => Err(de::Error::custom("invalid value type")),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn deserialize_option<V>(self, visitor: V) -> Result<V::Value>
|
||||
where
|
||||
V: de::Visitor<'de>,
|
||||
{
|
||||
match self.0 {
|
||||
Value::Nil => visitor.visit_none(),
|
||||
Value::LightUserData(ud) if ud.0.is_null() => visitor.visit_none(),
|
||||
_ => visitor.visit_some(self),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn deserialize_enum<V>(
|
||||
self,
|
||||
_name: &str,
|
||||
_variants: &'static [&'static str],
|
||||
visitor: V,
|
||||
) -> Result<V::Value>
|
||||
where
|
||||
V: de::Visitor<'de>,
|
||||
{
|
||||
let (variant, value) = match self.0 {
|
||||
Value::Table(value) => {
|
||||
let mut iter = value.pairs::<StdString, Value>();
|
||||
let (variant, value) = match iter.next() {
|
||||
Some(v) => v?,
|
||||
None => {
|
||||
return Err(de::Error::invalid_value(
|
||||
de::Unexpected::Map,
|
||||
&"map with a single key",
|
||||
))
|
||||
}
|
||||
};
|
||||
|
||||
if iter.next().is_some() {
|
||||
return Err(de::Error::invalid_value(
|
||||
de::Unexpected::Map,
|
||||
&"map with a single key",
|
||||
));
|
||||
}
|
||||
(variant, Some(value))
|
||||
}
|
||||
Value::String(variant) => (variant.to_str()?.to_owned(), None),
|
||||
_ => return Err(de::Error::custom("bad enum value")),
|
||||
};
|
||||
|
||||
visitor.visit_enum(EnumDeserializer { variant, value })
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn deserialize_seq<V>(self, visitor: V) -> Result<V::Value>
|
||||
where
|
||||
V: de::Visitor<'de>,
|
||||
{
|
||||
match self.0 {
|
||||
Value::Table(t) => {
|
||||
let len = t.raw_len() as usize;
|
||||
let mut deserializer = SeqDeserializer(t.raw_sequence_values());
|
||||
let seq = visitor.visit_seq(&mut deserializer)?;
|
||||
if deserializer.0.count() == 0 {
|
||||
Ok(seq)
|
||||
} else {
|
||||
Err(de::Error::invalid_length(
|
||||
len,
|
||||
&"fewer elements in the table",
|
||||
))
|
||||
}
|
||||
}
|
||||
_ => Err(de::Error::custom("invalid value type")),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn deserialize_tuple<V>(self, _len: usize, visitor: V) -> Result<V::Value>
|
||||
where
|
||||
V: de::Visitor<'de>,
|
||||
{
|
||||
self.deserialize_seq(visitor)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn deserialize_tuple_struct<V>(
|
||||
self,
|
||||
_name: &'static str,
|
||||
_len: usize,
|
||||
visitor: V,
|
||||
) -> Result<V::Value>
|
||||
where
|
||||
V: de::Visitor<'de>,
|
||||
{
|
||||
self.deserialize_seq(visitor)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn deserialize_map<V>(self, visitor: V) -> Result<V::Value>
|
||||
where
|
||||
V: de::Visitor<'de>,
|
||||
{
|
||||
match self.0 {
|
||||
Value::Table(t) => {
|
||||
let mut deserializer = MapDeserializer::new(t.pairs());
|
||||
let map = visitor.visit_map(&mut deserializer)?;
|
||||
let count = deserializer.pairs.count();
|
||||
if count == 0 {
|
||||
Ok(map)
|
||||
} else {
|
||||
Err(de::Error::invalid_length(
|
||||
deserializer.processed + count,
|
||||
&"fewer elements in the table",
|
||||
))
|
||||
}
|
||||
}
|
||||
_ => Err(de::Error::custom("invalid value type")),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn deserialize_struct<V>(
|
||||
self,
|
||||
_name: &'static str,
|
||||
_fields: &'static [&'static str],
|
||||
visitor: V,
|
||||
) -> Result<V::Value>
|
||||
where
|
||||
V: de::Visitor<'de>,
|
||||
{
|
||||
self.deserialize_map(visitor)
|
||||
}
|
||||
|
||||
serde::forward_to_deserialize_any! {
|
||||
bool i8 i16 i32 i64 u8 u16 u32 u64 f32 f64 char str string bytes
|
||||
byte_buf unit unit_struct newtype_struct
|
||||
identifier ignored_any
|
||||
}
|
||||
}
|
||||
|
||||
struct SeqDeserializer<'lua>(TableSequence<'lua, Value<'lua>>);
|
||||
|
||||
impl<'lua, 'de> de::SeqAccess<'de> for SeqDeserializer<'lua> {
|
||||
type Error = Error;
|
||||
|
||||
fn next_element_seed<T>(&mut self, seed: T) -> Result<Option<T::Value>>
|
||||
where
|
||||
T: de::DeserializeSeed<'de>,
|
||||
{
|
||||
match self.0.next() {
|
||||
Some(value) => seed.deserialize(Deserializer(value?)).map(Some),
|
||||
None => Ok(None),
|
||||
}
|
||||
}
|
||||
|
||||
fn size_hint(&self) -> Option<usize> {
|
||||
match self.0.size_hint() {
|
||||
(lower, Some(upper)) if lower == upper => Some(upper),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct MapDeserializer<'lua> {
|
||||
pairs: TablePairs<'lua, Value<'lua>, Value<'lua>>,
|
||||
value: Option<Value<'lua>>,
|
||||
processed: usize,
|
||||
}
|
||||
|
||||
impl<'lua> MapDeserializer<'lua> {
|
||||
fn new(pairs: TablePairs<'lua, Value<'lua>, Value<'lua>>) -> Self {
|
||||
MapDeserializer {
|
||||
pairs,
|
||||
value: None,
|
||||
processed: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua, 'de> de::MapAccess<'de> for MapDeserializer<'lua> {
|
||||
type Error = Error;
|
||||
|
||||
fn next_key_seed<T>(&mut self, seed: T) -> Result<Option<T::Value>>
|
||||
where
|
||||
T: de::DeserializeSeed<'de>,
|
||||
{
|
||||
match self.pairs.next() {
|
||||
Some(item) => {
|
||||
let (key, value) = item?;
|
||||
self.processed += 1;
|
||||
self.value = Some(value);
|
||||
let key_de = Deserializer(key);
|
||||
seed.deserialize(key_de).map(Some)
|
||||
}
|
||||
None => Ok(None),
|
||||
}
|
||||
}
|
||||
|
||||
fn next_value_seed<T>(&mut self, seed: T) -> Result<T::Value>
|
||||
where
|
||||
T: de::DeserializeSeed<'de>,
|
||||
{
|
||||
match self.value.take() {
|
||||
Some(value) => seed.deserialize(Deserializer(value)),
|
||||
None => Err(de::Error::custom("value is missing")),
|
||||
}
|
||||
}
|
||||
|
||||
fn size_hint(&self) -> Option<usize> {
|
||||
match self.pairs.size_hint() {
|
||||
(lower, Some(upper)) if lower == upper => Some(upper),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct EnumDeserializer<'lua> {
|
||||
variant: StdString,
|
||||
value: Option<Value<'lua>>,
|
||||
}
|
||||
|
||||
impl<'lua, 'de> de::EnumAccess<'de> for EnumDeserializer<'lua> {
|
||||
type Error = Error;
|
||||
type Variant = VariantDeserializer<'lua>;
|
||||
|
||||
fn variant_seed<T>(self, seed: T) -> Result<(T::Value, Self::Variant)>
|
||||
where
|
||||
T: de::DeserializeSeed<'de>,
|
||||
{
|
||||
let variant = self.variant.into_deserializer();
|
||||
let variant_access = VariantDeserializer { value: self.value };
|
||||
seed.deserialize(variant).map(|v| (v, variant_access))
|
||||
}
|
||||
}
|
||||
|
||||
struct VariantDeserializer<'lua> {
|
||||
value: Option<Value<'lua>>,
|
||||
}
|
||||
|
||||
impl<'lua, 'de> de::VariantAccess<'de> for VariantDeserializer<'lua> {
|
||||
type Error = Error;
|
||||
|
||||
fn unit_variant(self) -> Result<()> {
|
||||
match self.value {
|
||||
Some(_) => Err(de::Error::invalid_type(
|
||||
de::Unexpected::NewtypeVariant,
|
||||
&"unit variant",
|
||||
)),
|
||||
None => Ok(()),
|
||||
}
|
||||
}
|
||||
|
||||
fn newtype_variant_seed<T>(self, seed: T) -> Result<T::Value>
|
||||
where
|
||||
T: de::DeserializeSeed<'de>,
|
||||
{
|
||||
match self.value {
|
||||
Some(value) => seed.deserialize(Deserializer(value)),
|
||||
None => Err(de::Error::invalid_type(
|
||||
de::Unexpected::UnitVariant,
|
||||
&"newtype variant",
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
fn tuple_variant<V>(self, _len: usize, visitor: V) -> Result<V::Value>
|
||||
where
|
||||
V: de::Visitor<'de>,
|
||||
{
|
||||
match self.value {
|
||||
Some(value) => serde::Deserializer::deserialize_seq(Deserializer(value), visitor),
|
||||
None => Err(de::Error::invalid_type(
|
||||
de::Unexpected::UnitVariant,
|
||||
&"tuple variant",
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
fn struct_variant<V>(self, _fields: &'static [&'static str], visitor: V) -> Result<V::Value>
|
||||
where
|
||||
V: de::Visitor<'de>,
|
||||
{
|
||||
match self.value {
|
||||
Some(value) => serde::Deserializer::deserialize_map(Deserializer(value), visitor),
|
||||
None => Err(de::Error::invalid_type(
|
||||
de::Unexpected::UnitVariant,
|
||||
&"struct variant",
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,200 @@
|
||||
//! (De)Serialization support using serde.
|
||||
|
||||
use std::os::raw::{c_int, c_void};
|
||||
use std::ptr;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::error::Result;
|
||||
use crate::ffi;
|
||||
use crate::lua::Lua;
|
||||
use crate::table::Table;
|
||||
use crate::util::{assert_stack, protect_lua, StackGuard};
|
||||
use crate::value::Value;
|
||||
|
||||
pub trait LuaSerdeExt<'lua> {
|
||||
/// A special value (lightuserdata) to encode/decode optional (none) values.
|
||||
///
|
||||
/// Requires `feature = "serialize"`
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// use std::collections::HashMap;
|
||||
/// use mlua::{Lua, Result, LuaSerdeExt};
|
||||
///
|
||||
/// fn main() -> Result<()> {
|
||||
/// let lua = Lua::new();
|
||||
/// lua.globals().set("null", lua.null()?)?;
|
||||
///
|
||||
/// let val = lua.load(r#"{a = null}"#).eval()?;
|
||||
/// let map: HashMap<String, Option<String>> = lua.from_value(val)?;
|
||||
/// assert_eq!(map["a"], None);
|
||||
///
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
fn null(&'lua self) -> Result<Value<'lua>>;
|
||||
|
||||
/// A metatable attachable to a Lua table to systematically encode it as Array (instead of Map).
|
||||
/// As result, encoded Array will contain only sequence part of the table, with the same length
|
||||
/// as the `#` operator on that table.
|
||||
///
|
||||
/// Requires `feature = "serialize"`
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// use mlua::{Lua, Result, LuaSerdeExt};
|
||||
/// use serde_json::Value as JsonValue;
|
||||
///
|
||||
/// fn main() -> Result<()> {
|
||||
/// let lua = Lua::new();
|
||||
/// lua.globals().set("array_mt", lua.array_metatable()?)?;
|
||||
///
|
||||
/// // Encode as an empty array (no sequence part in the lua table)
|
||||
/// let val = lua.load("setmetatable({a = 5}, array_mt)").eval()?;
|
||||
/// let j: JsonValue = lua.from_value(val)?;
|
||||
/// assert_eq!(j.to_string(), "[]");
|
||||
///
|
||||
/// // Encode as object
|
||||
/// let val = lua.load("{a = 5}").eval()?;
|
||||
/// let j: JsonValue = lua.from_value(val)?;
|
||||
/// assert_eq!(j.to_string(), r#"{"a":5}"#);
|
||||
///
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
fn array_metatable(&'lua self) -> Result<Table<'lua>>;
|
||||
|
||||
/// Converts `T` into a `Value` instance.
|
||||
///
|
||||
/// Requires `feature = "serialize"`
|
||||
///
|
||||
/// [`Value`]: enum.Value.html
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// use mlua::{Lua, Result, LuaSerdeExt};
|
||||
/// use serde::Serialize;
|
||||
///
|
||||
/// #[derive(Serialize)]
|
||||
/// struct User {
|
||||
/// name: String,
|
||||
/// age: u8,
|
||||
/// }
|
||||
///
|
||||
/// fn main() -> Result<()> {
|
||||
/// let lua = Lua::new();
|
||||
/// let u = User {
|
||||
/// name: "John Smith".into(),
|
||||
/// age: 20,
|
||||
/// };
|
||||
/// lua.globals().set("user", lua.to_value(&u)?)?;
|
||||
/// lua.load(r#"
|
||||
/// assert(user["name"] == "John Smith")
|
||||
/// assert(user["age"] == 20)
|
||||
/// "#).exec()
|
||||
/// }
|
||||
/// ```
|
||||
fn to_value<T: Serialize + ?Sized>(&'lua self, t: &T) -> Result<Value<'lua>>;
|
||||
|
||||
/// Deserializes a `Value` into any serde deserializable object.
|
||||
///
|
||||
/// Requires `feature = "serialize"`
|
||||
///
|
||||
/// [`Value`]: enum.Value.html
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// use mlua::{Lua, Result, LuaSerdeExt};
|
||||
/// use serde::Deserialize;
|
||||
///
|
||||
/// #[derive(Deserialize, Debug, PartialEq)]
|
||||
/// struct User {
|
||||
/// name: String,
|
||||
/// age: u8,
|
||||
/// }
|
||||
///
|
||||
/// fn main() -> Result<()> {
|
||||
/// let lua = Lua::new();
|
||||
/// let val = lua.load(r#"{name = "John Smith", age = 20}"#).eval()?;
|
||||
/// let u: User = lua.from_value(val)?;
|
||||
///
|
||||
/// assert_eq!(u, User { name: "John Smith".into(), age: 20 });
|
||||
///
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
fn from_value<T: Deserialize<'lua>>(&'lua self, value: Value<'lua>) -> Result<T>;
|
||||
}
|
||||
|
||||
impl<'lua> LuaSerdeExt<'lua> for Lua {
|
||||
fn null(&'lua self) -> Result<Value<'lua>> {
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(self.state);
|
||||
assert_stack(self.state, 3);
|
||||
|
||||
unsafe extern "C" fn push_null(state: *mut ffi::lua_State) -> c_int {
|
||||
ffi::lua_pushlightuserdata(state, ptr::null_mut());
|
||||
1
|
||||
}
|
||||
protect_lua(self.state, 0, push_null)?;
|
||||
Ok(self.pop_value())
|
||||
}
|
||||
}
|
||||
|
||||
fn array_metatable(&'lua self) -> Result<Table<'lua>> {
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(self.state);
|
||||
assert_stack(self.state, 3);
|
||||
|
||||
unsafe extern "C" fn get_array_mt(state: *mut ffi::lua_State) -> c_int {
|
||||
push_array_metatable(state);
|
||||
1
|
||||
}
|
||||
protect_lua(self.state, 0, get_array_mt)?;
|
||||
Ok(Table(self.pop_ref()))
|
||||
}
|
||||
}
|
||||
|
||||
fn to_value<T>(&'lua self, t: &T) -> Result<Value<'lua>>
|
||||
where
|
||||
T: Serialize + ?Sized,
|
||||
{
|
||||
t.serialize(ser::Serializer(self))
|
||||
}
|
||||
|
||||
fn from_value<T>(&'lua self, value: Value<'lua>) -> Result<T>
|
||||
where
|
||||
T: Deserialize<'lua>,
|
||||
{
|
||||
T::deserialize(de::Deserializer(value))
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn init_metatables(state: *mut ffi::lua_State) {
|
||||
ffi::lua_pushlightuserdata(
|
||||
state,
|
||||
&ARRAY_METATABLE_REGISTRY_KEY as *const u8 as *mut c_void,
|
||||
);
|
||||
ffi::lua_newtable(state);
|
||||
|
||||
ffi::lua_pushstring(state, cstr!("__metatable"));
|
||||
ffi::lua_pushboolean(state, 0);
|
||||
ffi::lua_rawset(state, -3);
|
||||
|
||||
ffi::lua_rawset(state, ffi::LUA_REGISTRYINDEX);
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn push_array_metatable(state: *mut ffi::lua_State) {
|
||||
let key = &ARRAY_METATABLE_REGISTRY_KEY as *const u8 as *mut c_void;
|
||||
ffi::lua_rawgetp(state, ffi::LUA_REGISTRYINDEX, key);
|
||||
}
|
||||
|
||||
static ARRAY_METATABLE_REGISTRY_KEY: u8 = 0;
|
||||
|
||||
pub mod de;
|
||||
pub mod ser;
|
||||
@@ -0,0 +1,366 @@
|
||||
use std::os::raw::c_int;
|
||||
|
||||
use serde::{ser, Serialize};
|
||||
|
||||
use super::LuaSerdeExt;
|
||||
use crate::error::{Error, Result};
|
||||
use crate::ffi;
|
||||
use crate::lua::Lua;
|
||||
use crate::string::String;
|
||||
use crate::table::Table;
|
||||
use crate::types::Integer;
|
||||
use crate::util::{assert_stack, protect_lua, StackGuard};
|
||||
use crate::value::{ToLua, Value};
|
||||
|
||||
/// A struct for serializing Rust values into Lua values.
|
||||
pub struct Serializer<'lua>(pub &'lua Lua);
|
||||
|
||||
macro_rules! lua_serialize_number {
|
||||
($name:ident, $t:ty) => {
|
||||
#[inline]
|
||||
fn $name(self, value: $t) -> Result<Value<'lua>> {
|
||||
value.to_lua(self.0)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl<'lua> ser::Serializer for Serializer<'lua> {
|
||||
type Ok = Value<'lua>;
|
||||
type Error = Error;
|
||||
|
||||
// Associated types for keeping track of additional state while serializing
|
||||
// compound data structures like sequences and maps.
|
||||
type SerializeSeq = SerializeVec<'lua>;
|
||||
type SerializeTuple = SerializeVec<'lua>;
|
||||
type SerializeTupleStruct = SerializeVec<'lua>;
|
||||
type SerializeTupleVariant = SerializeTupleVariant<'lua>;
|
||||
type SerializeMap = SerializeMap<'lua>;
|
||||
type SerializeStruct = SerializeMap<'lua>;
|
||||
type SerializeStructVariant = SerializeStructVariant<'lua>;
|
||||
|
||||
#[inline]
|
||||
fn serialize_bool(self, value: bool) -> Result<Value<'lua>> {
|
||||
Ok(Value::Boolean(value))
|
||||
}
|
||||
|
||||
lua_serialize_number!(serialize_i8, i8);
|
||||
lua_serialize_number!(serialize_u8, u8);
|
||||
lua_serialize_number!(serialize_i16, i16);
|
||||
lua_serialize_number!(serialize_u16, u16);
|
||||
lua_serialize_number!(serialize_i32, i32);
|
||||
lua_serialize_number!(serialize_u32, u32);
|
||||
lua_serialize_number!(serialize_i64, i64);
|
||||
lua_serialize_number!(serialize_u64, u64);
|
||||
|
||||
lua_serialize_number!(serialize_f32, f32);
|
||||
lua_serialize_number!(serialize_f64, f64);
|
||||
|
||||
#[inline]
|
||||
fn serialize_char(self, value: char) -> Result<Value<'lua>> {
|
||||
self.serialize_str(&value.to_string())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn serialize_str(self, value: &str) -> Result<Value<'lua>> {
|
||||
self.0.create_string(value).map(Value::String)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn serialize_bytes(self, value: &[u8]) -> Result<Value<'lua>> {
|
||||
self.0.create_string(value).map(Value::String)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn serialize_none(self) -> Result<Value<'lua>> {
|
||||
self.0.null()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn serialize_some<T>(self, value: &T) -> Result<Value<'lua>>
|
||||
where
|
||||
T: ?Sized + Serialize,
|
||||
{
|
||||
value.serialize(self)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn serialize_unit(self) -> Result<Value<'lua>> {
|
||||
self.0.null()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn serialize_unit_struct(self, _name: &'static str) -> Result<Value<'lua>> {
|
||||
self.0.null()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn serialize_unit_variant(
|
||||
self,
|
||||
_name: &'static str,
|
||||
_variant_index: u32,
|
||||
variant: &'static str,
|
||||
) -> Result<Value<'lua>> {
|
||||
self.serialize_str(variant)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn serialize_newtype_struct<T>(self, _name: &'static str, value: &T) -> Result<Value<'lua>>
|
||||
where
|
||||
T: ?Sized + Serialize,
|
||||
{
|
||||
value.serialize(self)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn serialize_newtype_variant<T>(
|
||||
self,
|
||||
_name: &'static str,
|
||||
_variant_index: u32,
|
||||
variant: &'static str,
|
||||
value: &T,
|
||||
) -> Result<Value<'lua>>
|
||||
where
|
||||
T: ?Sized + Serialize,
|
||||
{
|
||||
let table = self.0.create_table()?;
|
||||
let variant = self.0.create_string(variant)?;
|
||||
let value = self.0.to_value(value)?;
|
||||
table.raw_set(variant, value)?;
|
||||
Ok(Value::Table(table))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn serialize_seq(self, len: Option<usize>) -> Result<Self::SerializeSeq> {
|
||||
let len = len.unwrap_or(0) as c_int;
|
||||
let table = self.0.create_table_with_capacity(len, 0)?;
|
||||
table.set_metatable(Some(self.0.array_metatable()?));
|
||||
Ok(SerializeVec { table })
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn serialize_tuple(self, len: usize) -> Result<Self::SerializeTuple> {
|
||||
self.serialize_seq(Some(len))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn serialize_tuple_struct(
|
||||
self,
|
||||
_name: &'static str,
|
||||
len: usize,
|
||||
) -> Result<Self::SerializeTupleStruct> {
|
||||
self.serialize_seq(Some(len))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn serialize_tuple_variant(
|
||||
self,
|
||||
_name: &'static str,
|
||||
_variant_index: u32,
|
||||
variant: &'static str,
|
||||
_len: usize,
|
||||
) -> Result<Self::SerializeTupleVariant> {
|
||||
let name = self.0.create_string(variant)?;
|
||||
let table = self.0.create_table()?;
|
||||
Ok(SerializeTupleVariant { name, table })
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn serialize_map(self, len: Option<usize>) -> Result<Self::SerializeMap> {
|
||||
let len = len.unwrap_or(0) as c_int;
|
||||
Ok(SerializeMap {
|
||||
key: None,
|
||||
table: self.0.create_table_with_capacity(0, len)?,
|
||||
})
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn serialize_struct(self, _name: &'static str, len: usize) -> Result<Self::SerializeStruct> {
|
||||
self.serialize_map(Some(len))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn serialize_struct_variant(
|
||||
self,
|
||||
_name: &'static str,
|
||||
_variant_index: u32,
|
||||
variant: &'static str,
|
||||
len: usize,
|
||||
) -> Result<Self::SerializeStructVariant> {
|
||||
let name = self.0.create_string(variant)?;
|
||||
let table = self.0.create_table_with_capacity(0, len as c_int)?;
|
||||
Ok(SerializeStructVariant { name, table })
|
||||
}
|
||||
}
|
||||
|
||||
pub struct SerializeVec<'lua> {
|
||||
table: Table<'lua>,
|
||||
}
|
||||
|
||||
impl<'lua> ser::SerializeSeq for SerializeVec<'lua> {
|
||||
type Ok = Value<'lua>;
|
||||
type Error = Error;
|
||||
|
||||
fn serialize_element<T>(&mut self, value: &T) -> Result<()>
|
||||
where
|
||||
T: ?Sized + Serialize,
|
||||
{
|
||||
let lua = self.table.0.lua;
|
||||
let value = lua.to_value(value)?;
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(lua.state);
|
||||
assert_stack(lua.state, 4);
|
||||
|
||||
lua.push_ref(&self.table.0);
|
||||
lua.push_value(value)?;
|
||||
|
||||
unsafe extern "C" fn push_to_table(state: *mut ffi::lua_State) -> c_int {
|
||||
let len = ffi::lua_rawlen(state, -2) as Integer;
|
||||
ffi::lua_rawseti(state, -2, len + 1);
|
||||
1
|
||||
}
|
||||
|
||||
protect_lua(lua.state, 2, push_to_table)
|
||||
}
|
||||
}
|
||||
|
||||
fn end(self) -> Result<Value<'lua>> {
|
||||
Ok(Value::Table(self.table))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> ser::SerializeTuple for SerializeVec<'lua> {
|
||||
type Ok = Value<'lua>;
|
||||
type Error = Error;
|
||||
|
||||
fn serialize_element<T>(&mut self, value: &T) -> Result<()>
|
||||
where
|
||||
T: ?Sized + Serialize,
|
||||
{
|
||||
ser::SerializeSeq::serialize_element(self, value)
|
||||
}
|
||||
|
||||
fn end(self) -> Result<Value<'lua>> {
|
||||
ser::SerializeSeq::end(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> ser::SerializeTupleStruct for SerializeVec<'lua> {
|
||||
type Ok = Value<'lua>;
|
||||
type Error = Error;
|
||||
|
||||
fn serialize_field<T>(&mut self, value: &T) -> Result<()>
|
||||
where
|
||||
T: ?Sized + Serialize,
|
||||
{
|
||||
ser::SerializeSeq::serialize_element(self, value)
|
||||
}
|
||||
|
||||
fn end(self) -> Result<Value<'lua>> {
|
||||
ser::SerializeSeq::end(self)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct SerializeTupleVariant<'lua> {
|
||||
name: String<'lua>,
|
||||
table: Table<'lua>,
|
||||
}
|
||||
|
||||
impl<'lua> ser::SerializeTupleVariant for SerializeTupleVariant<'lua> {
|
||||
type Ok = Value<'lua>;
|
||||
type Error = Error;
|
||||
|
||||
fn serialize_field<T>(&mut self, value: &T) -> Result<()>
|
||||
where
|
||||
T: ?Sized + Serialize,
|
||||
{
|
||||
let lua = self.table.0.lua;
|
||||
let idx = self.table.raw_len() + 1;
|
||||
self.table.raw_insert(idx, lua.to_value(value)?)
|
||||
}
|
||||
|
||||
fn end(self) -> Result<Value<'lua>> {
|
||||
let lua = self.table.0.lua;
|
||||
let table = lua.create_table()?;
|
||||
table.raw_set(self.name, self.table)?;
|
||||
Ok(Value::Table(table))
|
||||
}
|
||||
}
|
||||
|
||||
pub struct SerializeMap<'lua> {
|
||||
table: Table<'lua>,
|
||||
key: Option<Value<'lua>>,
|
||||
}
|
||||
|
||||
impl<'lua> ser::SerializeMap for SerializeMap<'lua> {
|
||||
type Ok = Value<'lua>;
|
||||
type Error = Error;
|
||||
|
||||
fn serialize_key<T>(&mut self, key: &T) -> Result<()>
|
||||
where
|
||||
T: ?Sized + Serialize,
|
||||
{
|
||||
let lua = self.table.0.lua;
|
||||
self.key = Some(lua.to_value(key)?);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn serialize_value<T>(&mut self, value: &T) -> Result<()>
|
||||
where
|
||||
T: ?Sized + Serialize,
|
||||
{
|
||||
let lua = self.table.0.lua;
|
||||
let key = mlua_expect!(
|
||||
self.key.take(),
|
||||
"serialize_value called before serialize_key"
|
||||
);
|
||||
let value = lua.to_value(value)?;
|
||||
self.table.raw_set(key, value)
|
||||
}
|
||||
|
||||
fn end(self) -> Result<Value<'lua>> {
|
||||
Ok(Value::Table(self.table))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> ser::SerializeStruct for SerializeMap<'lua> {
|
||||
type Ok = Value<'lua>;
|
||||
type Error = Error;
|
||||
|
||||
fn serialize_field<T>(&mut self, key: &'static str, value: &T) -> Result<()>
|
||||
where
|
||||
T: ?Sized + Serialize,
|
||||
{
|
||||
ser::SerializeMap::serialize_key(self, key)?;
|
||||
ser::SerializeMap::serialize_value(self, value)
|
||||
}
|
||||
|
||||
fn end(self) -> Result<Value<'lua>> {
|
||||
ser::SerializeMap::end(self)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct SerializeStructVariant<'lua> {
|
||||
name: String<'lua>,
|
||||
table: Table<'lua>,
|
||||
}
|
||||
|
||||
impl<'lua> ser::SerializeStructVariant for SerializeStructVariant<'lua> {
|
||||
type Ok = Value<'lua>;
|
||||
type Error = Error;
|
||||
|
||||
fn serialize_field<T>(&mut self, key: &'static str, value: &T) -> Result<()>
|
||||
where
|
||||
T: ?Sized + Serialize,
|
||||
{
|
||||
let lua = self.table.0.lua;
|
||||
self.table.raw_set(key, lua.to_value(value)?)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn end(self) -> Result<Value<'lua>> {
|
||||
let lua = self.table.0.lua;
|
||||
let table = lua.create_table()?;
|
||||
table.raw_set(self.name, self.table)?;
|
||||
Ok(Value::Table(table))
|
||||
}
|
||||
}
|
||||
@@ -47,6 +47,8 @@ impl StdLib {
|
||||
/// (unsafe) [`debug`](https://www.lua.org/manual/5.3/manual.html#6.10) library
|
||||
pub const DEBUG: StdLib = StdLib(1 << 31);
|
||||
|
||||
/// No libraries
|
||||
pub const NONE: StdLib = StdLib(0);
|
||||
/// (unsafe) All standard libraries
|
||||
pub const ALL: StdLib = StdLib(u32::MAX);
|
||||
/// The safe subset of the standard libraries
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
use std::{slice, str};
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
use {
|
||||
serde::ser::{Serialize, Serializer},
|
||||
std::result::Result as StdResult,
|
||||
};
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
use crate::ffi;
|
||||
use crate::types::LuaRef;
|
||||
@@ -105,3 +111,16 @@ where
|
||||
self.as_bytes() == other.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
impl<'lua> Serialize for String<'lua> {
|
||||
fn serialize<S>(&self, serializer: S) -> StdResult<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
match self.to_str() {
|
||||
Ok(s) => serializer.serialize_str(s),
|
||||
Err(_) => serializer.serialize_bytes(self.as_bytes()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+89
-3
@@ -1,6 +1,12 @@
|
||||
use std::marker::PhantomData;
|
||||
use std::os::raw::c_int;
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
use {
|
||||
serde::ser::{Serialize, SerializeMap, SerializeSeq, Serializer},
|
||||
std::result::Result as StdResult,
|
||||
};
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
use crate::ffi;
|
||||
use crate::function::Function;
|
||||
@@ -447,9 +453,56 @@ impl<'lua> Table<'lua> {
|
||||
TableSequence {
|
||||
table: self.0,
|
||||
index: Some(1),
|
||||
len: None,
|
||||
raw: false,
|
||||
_phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// Consume this table and return an iterator over all values in the sequence part of the table.
|
||||
///
|
||||
/// Unlike the `sequence_values`, does not invoke `__index` metamethod when iterating.
|
||||
///
|
||||
/// [`sequence_values`]: #method.sequence_values
|
||||
pub fn raw_sequence_values<V: FromLua<'lua>>(self) -> TableSequence<'lua, V> {
|
||||
TableSequence {
|
||||
table: self.0,
|
||||
index: Some(1),
|
||||
len: None,
|
||||
raw: true,
|
||||
_phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "async", feature = "serialize"))]
|
||||
pub(crate) fn raw_sequence_values_by_len<V: FromLua<'lua>>(
|
||||
self,
|
||||
len: Option<Integer>,
|
||||
) -> TableSequence<'lua, V> {
|
||||
let len = len.unwrap_or_else(|| self.raw_len());
|
||||
TableSequence {
|
||||
table: self.0,
|
||||
index: Some(1),
|
||||
len: Some(len),
|
||||
raw: true,
|
||||
_phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
pub(crate) fn is_array(&self) -> bool {
|
||||
let lua = self.0.lua;
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(lua.state);
|
||||
assert_stack(lua.state, 3);
|
||||
lua.push_ref(&self.0);
|
||||
if ffi::lua_getmetatable(lua.state, -1) == 0 {
|
||||
return false;
|
||||
}
|
||||
crate::serde::push_array_metatable(lua.state);
|
||||
ffi::lua_rawequal(lua.state, -1, -2) != 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> PartialEq for Table<'lua> {
|
||||
@@ -500,6 +553,7 @@ pub trait TableExt<'lua> {
|
||||
///
|
||||
/// This might invoke the `__index` metamethod.
|
||||
#[cfg(feature = "async")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
|
||||
fn call_async_method<'fut, K, A, R>(&self, key: K, args: A) -> LocalBoxFuture<'fut, Result<R>>
|
||||
where
|
||||
'lua: 'fut,
|
||||
@@ -514,6 +568,7 @@ pub trait TableExt<'lua> {
|
||||
///
|
||||
/// This might invoke the `__index` metamethod.
|
||||
#[cfg(feature = "async")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
|
||||
fn call_async_function<'fut, K, A, R>(
|
||||
&self,
|
||||
key: K,
|
||||
@@ -580,6 +635,31 @@ impl<'lua> TableExt<'lua> for Table<'lua> {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
impl<'lua> Serialize for Table<'lua> {
|
||||
fn serialize<S>(&self, serializer: S) -> StdResult<S::Ok, S::Error>
|
||||
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();
|
||||
}
|
||||
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
||||
/// An iterator over the pairs of a Lua table.
|
||||
///
|
||||
/// This struct is created by the [`Table::pairs`] method.
|
||||
@@ -651,6 +731,8 @@ where
|
||||
pub struct TableSequence<'lua, V> {
|
||||
table: LuaRef<'lua>,
|
||||
index: Option<Integer>,
|
||||
len: Option<Integer>,
|
||||
raw: bool,
|
||||
_phantom: PhantomData<V>,
|
||||
}
|
||||
|
||||
@@ -669,9 +751,13 @@ where
|
||||
assert_stack(lua.state, 5);
|
||||
|
||||
lua.push_ref(&self.table);
|
||||
match protect_lua_closure(lua.state, 1, 1, |state| ffi::lua_geti(state, -1, index))
|
||||
{
|
||||
Ok(ffi::LUA_TNIL) => None,
|
||||
let lua_geti = if self.raw {
|
||||
ffi::lua_rawgeti
|
||||
} else {
|
||||
ffi::lua_geti
|
||||
};
|
||||
match protect_lua_closure(lua.state, 1, 1, |state| lua_geti(state, -1, index)) {
|
||||
Ok(ffi::LUA_TNIL) if index > self.len.unwrap_or(0) => None,
|
||||
Ok(_) => {
|
||||
let value = lua.pop_value();
|
||||
self.index = Some(index + 1);
|
||||
|
||||
@@ -52,6 +52,7 @@ pub struct Thread<'lua>(pub(crate) LuaRef<'lua>);
|
||||
/// [`Future`]: ../futures_core/future/trait.Future.html
|
||||
/// [`Stream`]: ../futures_core/stream/trait.Stream.html
|
||||
#[cfg(feature = "async")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
|
||||
#[derive(Debug)]
|
||||
pub struct AsyncThread<'lua, R> {
|
||||
thread: Thread<'lua>,
|
||||
@@ -218,6 +219,7 @@ impl<'lua> Thread<'lua> {
|
||||
/// # }
|
||||
/// ```
|
||||
#[cfg(feature = "async")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
|
||||
pub fn into_async<A, R>(self, args: A) -> AsyncThread<'lua, R>
|
||||
where
|
||||
A: ToLuaMulti<'lua>,
|
||||
|
||||
@@ -10,6 +10,7 @@ use crate::error::Result;
|
||||
use crate::ffi;
|
||||
use crate::hook::Debug;
|
||||
use crate::lua::Lua;
|
||||
use crate::userdata::UserDataWrapped;
|
||||
use crate::util::{assert_stack, StackGuard};
|
||||
use crate::value::MultiValue;
|
||||
|
||||
@@ -31,6 +32,8 @@ pub(crate) type AsyncCallback<'lua, 'a> =
|
||||
|
||||
pub(crate) type HookCallback = Arc<RefCell<dyn FnMut(&Lua, Debug) -> Result<()>>>;
|
||||
|
||||
pub(crate) type UserDataCell<T> = RefCell<UserDataWrapped<T>>;
|
||||
|
||||
#[cfg(feature = "send")]
|
||||
pub trait MaybeSend: Send {}
|
||||
#[cfg(feature = "send")]
|
||||
|
||||
+115
-10
@@ -1,15 +1,21 @@
|
||||
use std::cell::{Ref, RefCell, RefMut};
|
||||
use std::cell::{Ref, RefMut};
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
use std::future::Future;
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
use {
|
||||
serde::ser::{self, Serialize, Serializer},
|
||||
std::result::Result as StdResult,
|
||||
};
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
use crate::ffi;
|
||||
use crate::function::Function;
|
||||
use crate::lua::Lua;
|
||||
use crate::table::Table;
|
||||
use crate::types::{LuaRef, MaybeSend};
|
||||
use crate::util::{assert_stack, get_userdata, StackGuard};
|
||||
use crate::types::{LuaRef, MaybeSend, UserDataCell};
|
||||
use crate::util::{assert_stack, get_destructed_userdata_metatable, get_userdata, StackGuard};
|
||||
use crate::value::{FromLua, FromLuaMulti, ToLua, ToLuaMulti, Value};
|
||||
|
||||
/// Kinds of metamethods that can be overridden.
|
||||
@@ -185,6 +191,7 @@ pub trait UserDataMethods<'lua, T: UserData> {
|
||||
///
|
||||
/// [`add_method`]: #method.add_method
|
||||
#[cfg(feature = "async")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
|
||||
fn add_async_method<S, A, R, M, MR>(&mut self, name: &S, method: M)
|
||||
where
|
||||
T: Clone,
|
||||
@@ -231,6 +238,7 @@ pub trait UserDataMethods<'lua, T: UserData> {
|
||||
///
|
||||
/// [`add_function`]: #method.add_function
|
||||
#[cfg(feature = "async")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
|
||||
fn add_async_function<S, A, R, F, FR>(&mut self, name: &S, function: F)
|
||||
where
|
||||
T: Clone,
|
||||
@@ -360,6 +368,71 @@ pub trait UserData: Sized {
|
||||
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(_methods: &mut M) {}
|
||||
}
|
||||
|
||||
pub(crate) struct UserDataWrapped<T> {
|
||||
pub(crate) data: *mut T,
|
||||
#[cfg(feature = "serialize")]
|
||||
ser: *mut dyn erased_serde::Serialize,
|
||||
}
|
||||
|
||||
impl<T> Drop for UserDataWrapped<T> {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
drop(Box::from_raw(self.data));
|
||||
#[cfg(feature = "serialize")]
|
||||
if self.data as *mut () != self.ser as *mut () {
|
||||
drop(Box::from_raw(self.ser));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> UserDataWrapped<T> {
|
||||
pub(crate) fn new(data: T) -> Self {
|
||||
UserDataWrapped {
|
||||
data: Box::into_raw(Box::new(data)),
|
||||
#[cfg(feature = "serialize")]
|
||||
ser: Box::into_raw(Box::new(UserDataSerializeError)),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
pub(crate) fn new_ser(data: T) -> Self
|
||||
where
|
||||
T: 'static + Serialize,
|
||||
{
|
||||
let data_raw = Box::into_raw(Box::new(data));
|
||||
UserDataWrapped {
|
||||
data: data_raw,
|
||||
ser: data_raw,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> AsRef<T> for UserDataWrapped<T> {
|
||||
fn as_ref(&self) -> &T {
|
||||
unsafe { &*self.data }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> AsMut<T> for UserDataWrapped<T> {
|
||||
fn as_mut(&mut self) -> &mut T {
|
||||
unsafe { &mut *self.data }
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
pub(crate) struct UserDataSerializeError;
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
impl Serialize for UserDataSerializeError {
|
||||
fn serialize<S>(&self, _serializer: S) -> StdResult<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
Err(ser::Error::custom("cannot serialize <userdata>"))
|
||||
}
|
||||
}
|
||||
|
||||
/// Handle to an internal Lua userdata for any type that implements [`UserData`].
|
||||
///
|
||||
/// Similar to `std::any::Any`, this provides an interface for dynamic type checking via the [`is`]
|
||||
@@ -382,7 +455,7 @@ pub struct AnyUserData<'lua>(pub(crate) LuaRef<'lua>);
|
||||
impl<'lua> AnyUserData<'lua> {
|
||||
/// Checks whether the type of this userdata is `T`.
|
||||
pub fn is<T: 'static + UserData>(&self) -> bool {
|
||||
match self.inspect(|_: &RefCell<T>| Ok(())) {
|
||||
match self.inspect(|_: &UserDataCell<T>| Ok(())) {
|
||||
Ok(()) => true,
|
||||
Err(Error::UserDataTypeMismatch) => false,
|
||||
Err(_) => unreachable!(),
|
||||
@@ -396,7 +469,10 @@ impl<'lua> AnyUserData<'lua> {
|
||||
/// Returns a `UserDataBorrowError` if the userdata is already mutably borrowed. Returns a
|
||||
/// `UserDataTypeMismatch` if the userdata is not of type `T`.
|
||||
pub fn borrow<T: 'static + UserData>(&self) -> Result<Ref<T>> {
|
||||
self.inspect(|cell| Ok(cell.try_borrow().map_err(|_| Error::UserDataBorrowError)?))
|
||||
self.inspect(|cell| {
|
||||
let cell_ref = cell.try_borrow().map_err(|_| Error::UserDataBorrowError)?;
|
||||
Ok(Ref::map(cell_ref, |x| unsafe { &*x.data }))
|
||||
})
|
||||
}
|
||||
|
||||
/// Borrow this userdata mutably if it is of type `T`.
|
||||
@@ -407,9 +483,10 @@ impl<'lua> AnyUserData<'lua> {
|
||||
/// `UserDataTypeMismatch` if the userdata is not of type `T`.
|
||||
pub fn borrow_mut<T: 'static + UserData>(&self) -> Result<RefMut<T>> {
|
||||
self.inspect(|cell| {
|
||||
Ok(cell
|
||||
let cell_ref = cell
|
||||
.try_borrow_mut()
|
||||
.map_err(|_| Error::UserDataBorrowMutError)?)
|
||||
.map_err(|_| Error::UserDataBorrowMutError)?;
|
||||
Ok(RefMut::map(cell_ref, |x| unsafe { &mut *x.data }))
|
||||
})
|
||||
}
|
||||
|
||||
@@ -515,7 +592,7 @@ impl<'lua> AnyUserData<'lua> {
|
||||
fn inspect<'a, T, R, F>(&'a self, func: F) -> Result<R>
|
||||
where
|
||||
T: 'static + UserData,
|
||||
F: FnOnce(&'a RefCell<T>) -> Result<R>,
|
||||
F: FnOnce(&'a UserDataCell<T>) -> Result<R>,
|
||||
{
|
||||
unsafe {
|
||||
let lua = self.0.lua;
|
||||
@@ -534,9 +611,16 @@ impl<'lua> AnyUserData<'lua> {
|
||||
);
|
||||
|
||||
if ffi::lua_rawequal(lua.state, -1, -2) == 0 {
|
||||
Err(Error::UserDataTypeMismatch)
|
||||
// Maybe UserData destructed?
|
||||
ffi::lua_pop(lua.state, 1);
|
||||
get_destructed_userdata_metatable(lua.state);
|
||||
if ffi::lua_rawequal(lua.state, -1, -2) == 1 {
|
||||
Err(Error::UserDataDestructed)
|
||||
} else {
|
||||
Err(Error::UserDataTypeMismatch)
|
||||
}
|
||||
} else {
|
||||
func(&*get_userdata::<RefCell<T>>(lua.state, -3))
|
||||
func(&*get_userdata::<UserDataCell<T>>(lua.state, -3))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -555,3 +639,24 @@ impl<'lua> AsRef<AnyUserData<'lua>> for AnyUserData<'lua> {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
impl<'lua> Serialize for AnyUserData<'lua> {
|
||||
fn serialize<S>(&self, serializer: S) -> StdResult<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
let f = || unsafe {
|
||||
let lua = self.0.lua;
|
||||
let _sg = StackGuard::new(lua.state);
|
||||
assert_stack(lua.state, 2);
|
||||
|
||||
lua.push_userdata_ref(&self.0)?;
|
||||
let ud = &*get_userdata::<UserDataCell<()>>(lua.state, -1);
|
||||
(*ud.try_borrow().map_err(|_| Error::UserDataBorrowError)?.ser)
|
||||
.serialize(serializer)
|
||||
.map_err(|err| Error::SerializeError(err.to_string()))
|
||||
};
|
||||
f().map_err(ser::Error::custom)
|
||||
}
|
||||
}
|
||||
|
||||
+13
-22
@@ -186,7 +186,7 @@ pub unsafe fn pop_error(state: *mut ffi::lua_State, err_code: c_int) -> Error {
|
||||
if let Some(p) = (*panic).0.take() {
|
||||
resume_unwind(p);
|
||||
} else {
|
||||
mlua_panic!("error during panic handling, panic was resumed twice")
|
||||
Error::PreviouslyResumedPanic
|
||||
}
|
||||
} else {
|
||||
let err_string = to_string(state, -1).into_owned();
|
||||
@@ -279,15 +279,11 @@ pub unsafe fn push_meta_gc_userdata<MT: Any, T>(state: *mut ffi::lua_State, t: T
|
||||
|
||||
// Uses 2 stack spaces, does not call checkstack
|
||||
pub unsafe fn get_gc_userdata<T: Any>(state: *mut ffi::lua_State, index: c_int) -> *mut T {
|
||||
get_meta_gc_userdata::<T, T>(state, index)
|
||||
}
|
||||
|
||||
pub unsafe fn get_meta_gc_userdata<MT: Any, T>(state: *mut ffi::lua_State, index: c_int) -> *mut T {
|
||||
let ud = ffi::lua_touserdata(state, index) as *mut T;
|
||||
if ud.is_null() || ffi::lua_getmetatable(state, index) == 0 {
|
||||
return ptr::null_mut();
|
||||
}
|
||||
get_gc_metatable_for::<MT>(state);
|
||||
get_gc_metatable_for::<T>(state);
|
||||
let res = ffi::lua_rawequal(state, -1, -2) != 0;
|
||||
ffi::lua_pop(state, 2);
|
||||
if !res {
|
||||
@@ -591,27 +587,22 @@ pub unsafe fn init_error_registry(state: *mut ffi::lua_State) {
|
||||
Ok(err_buf)
|
||||
} else if let Some(panic) = get_gc_userdata::<WrappedPanic>(state, -1).as_ref() {
|
||||
if let Some(ref p) = (*panic).0 {
|
||||
ffi::lua_pushlightuserdata(
|
||||
state,
|
||||
&ERROR_PRINT_BUFFER_KEY as *const u8 as *mut c_void,
|
||||
);
|
||||
ffi::lua_rawget(state, ffi::LUA_REGISTRYINDEX);
|
||||
let err_buf_key = &ERROR_PRINT_BUFFER_KEY as *const u8 as *const c_void;
|
||||
ffi::lua_rawgetp(state, ffi::LUA_REGISTRYINDEX, err_buf_key);
|
||||
let err_buf = ffi::lua_touserdata(state, -1) as *mut String;
|
||||
(*err_buf).clear();
|
||||
ffi::lua_pop(state, 2);
|
||||
|
||||
let error = if let Some(x) = p.downcast_ref::<&str>() {
|
||||
x.to_string()
|
||||
} else if let Some(x) = p.downcast_ref::<String>() {
|
||||
x.to_string()
|
||||
if let Some(msg) = p.downcast_ref::<&str>() {
|
||||
let _ = write!(&mut (*err_buf), "{}", msg);
|
||||
} else if let Some(msg) = p.downcast_ref::<String>() {
|
||||
let _ = write!(&mut (*err_buf), "{}", msg);
|
||||
} else {
|
||||
"panic".to_string()
|
||||
let _ = write!(&mut (*err_buf), "<panic>");
|
||||
};
|
||||
|
||||
(*err_buf).clear();
|
||||
let _ = write!(&mut (*err_buf), "{}", error);
|
||||
Ok(err_buf)
|
||||
} else {
|
||||
mlua_panic!("error during panic handling, panic was resumed")
|
||||
Err(Error::PreviouslyResumedPanic)
|
||||
}
|
||||
} else {
|
||||
// I'm not sure whether this is possible to trigger without bugs in mlua?
|
||||
@@ -725,7 +716,7 @@ pub unsafe fn init_error_registry(state: *mut ffi::lua_State) {
|
||||
}
|
||||
|
||||
struct WrappedError(pub Error);
|
||||
struct WrappedPanic(pub Option<Box<dyn Any + Send + 'static>>);
|
||||
pub(crate) struct WrappedPanic(pub Option<Box<dyn Any + Send + 'static>>);
|
||||
|
||||
// Converts the given lua value to a string in a reasonable format without causing a Lua error or
|
||||
// panicking.
|
||||
@@ -759,7 +750,7 @@ unsafe fn to_string<'a>(state: *mut ffi::lua_State, index: c_int) -> Cow<'a, str
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn get_destructed_userdata_metatable(state: *mut ffi::lua_State) {
|
||||
pub(crate) unsafe fn get_destructed_userdata_metatable(state: *mut ffi::lua_State) {
|
||||
ffi::lua_pushlightuserdata(
|
||||
state,
|
||||
&DESTRUCTED_USERDATA_METATABLE as *const u8 as *mut c_void,
|
||||
|
||||
@@ -1,6 +1,12 @@
|
||||
use std::iter::{self, FromIterator};
|
||||
use std::{slice, str, vec};
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
use {
|
||||
serde::ser::{self, Serialize, Serializer},
|
||||
std::result::Result as StdResult,
|
||||
};
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
use crate::ffi;
|
||||
use crate::function::Function;
|
||||
@@ -110,6 +116,31 @@ impl<'lua> AsRef<Value<'lua>> for Value<'lua> {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
impl<'lua> Serialize for Value<'lua> {
|
||||
fn serialize<S>(&self, serializer: S) -> StdResult<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
match self {
|
||||
Value::Nil => serializer.serialize_unit(),
|
||||
Value::Boolean(b) => serializer.serialize_bool(*b),
|
||||
#[allow(clippy::useless_conversion)]
|
||||
Value::Integer(i) => serializer.serialize_i64((*i).into()),
|
||||
#[allow(clippy::useless_conversion)]
|
||||
Value::Number(n) => serializer.serialize_f64((*n).into()),
|
||||
Value::String(s) => s.serialize(serializer),
|
||||
Value::Table(t) => t.serialize(serializer),
|
||||
Value::UserData(ud) => ud.serialize(serializer),
|
||||
Value::LightUserData(ud) if ud.0.is_null() => serializer.serialize_none(),
|
||||
Value::Error(_) | Value::LightUserData(_) | Value::Function(_) | Value::Thread(_) => {
|
||||
let msg = format!("cannot serialize <{}>", self.type_name());
|
||||
Err(ser::Error::custom(msg))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Trait for types convertible to `Value`.
|
||||
pub trait ToLua<'lua> {
|
||||
/// Performs the conversion.
|
||||
|
||||
+36
-12
@@ -1,15 +1,4 @@
|
||||
#![cfg(feature = "async")]
|
||||
#![cfg_attr(
|
||||
all(feature = "luajit", target_os = "macos", target_arch = "x86_64"),
|
||||
feature(link_args)
|
||||
)]
|
||||
|
||||
#[cfg_attr(
|
||||
all(feature = "luajit", target_os = "macos", target_arch = "x86_64"),
|
||||
link_args = "-pagezero_size 10000 -image_base 100000000",
|
||||
allow(unused_attributes)
|
||||
)]
|
||||
extern "system" {}
|
||||
|
||||
use std::cell::Cell;
|
||||
use std::rc::Rc;
|
||||
@@ -22,7 +11,9 @@ use std::time::Duration;
|
||||
use futures_timer::Delay;
|
||||
use futures_util::stream::TryStreamExt;
|
||||
|
||||
use mlua::{Error, Function, Lua, Result, Table, TableExt, UserData, UserDataMethods};
|
||||
use mlua::{
|
||||
Error, Function, Lua, Result, Table, TableExt, Thread, UserData, UserDataMethods, Value,
|
||||
};
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_async_function() -> Result<()> {
|
||||
@@ -134,6 +125,24 @@ async fn test_async_handle_yield() -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_async_multi_return_nil() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
lua.globals().set(
|
||||
"func",
|
||||
lua.create_async_function(|_, _: ()| async { Ok((Option::<String>::None, "error")) })?,
|
||||
)?;
|
||||
|
||||
lua.load(
|
||||
r#"
|
||||
local ok, err = func()
|
||||
assert(err == "error")
|
||||
"#,
|
||||
)
|
||||
.exec_async()
|
||||
.await
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_async_return_async_closure() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
@@ -332,11 +341,18 @@ async fn test_async_scope() -> Result<()> {
|
||||
let _ = f.call_async::<u64, ()>(10).await?;
|
||||
assert_eq!(Rc::strong_count(rc), 1);
|
||||
|
||||
// Create future in partialy polled state (Poll::Pending)
|
||||
let g = lua.create_thread(f)?;
|
||||
g.resume::<u64, ()>(10)?;
|
||||
lua.globals().set("g", g)?;
|
||||
assert_eq!(Rc::strong_count(rc), 2);
|
||||
|
||||
Ok(())
|
||||
});
|
||||
|
||||
assert_eq!(Rc::strong_count(rc), 1);
|
||||
let _ = fut.await?;
|
||||
assert_eq!(Rc::strong_count(rc), 1);
|
||||
|
||||
match lua
|
||||
.globals()
|
||||
@@ -351,6 +367,14 @@ async fn test_async_scope() -> Result<()> {
|
||||
r => panic!("improper return for destructed function: {:?}", r),
|
||||
};
|
||||
|
||||
match lua.globals().get::<_, Thread>("g")?.resume::<_, Value>(()) {
|
||||
Err(Error::CallbackError { ref cause, .. }) => match cause.as_ref() {
|
||||
Error::CallbackDestructed => {}
|
||||
e => panic!("expected `CallbackDestructed` error cause, got {:?}", e),
|
||||
},
|
||||
r => panic!("improper return for destructed function: {:?}", r),
|
||||
};
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
@@ -1,15 +1,3 @@
|
||||
#![cfg_attr(
|
||||
all(feature = "luajit", target_os = "macos", target_arch = "x86_64"),
|
||||
feature(link_args)
|
||||
)]
|
||||
|
||||
#[cfg_attr(
|
||||
all(feature = "luajit", target_os = "macos", target_arch = "x86_64"),
|
||||
link_args = "-pagezero_size 10000 -image_base 100000000",
|
||||
allow(unused_attributes)
|
||||
)]
|
||||
extern "system" {}
|
||||
|
||||
use bstr::{BStr, BString};
|
||||
use mlua::{Lua, Result};
|
||||
|
||||
|
||||
@@ -22,14 +22,14 @@ note: ...so that the types are compatible
|
||||
13 | | Ok(())
|
||||
14 | | });
|
||||
| |_____________^
|
||||
= note: expected `main::MyUserData<'_>`
|
||||
found `main::MyUserData<'a>`
|
||||
= note: expected `(MyUserData<'_>,)`
|
||||
found `(MyUserData<'a>,)`
|
||||
note: but, the lifetime must be valid for the lifetime `'lua` as defined on the method body at 10:24...
|
||||
--> $DIR/async_nonstatic_userdata.rs:10:24
|
||||
|
|
||||
10 | fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
|
||||
| ^^^^
|
||||
note: ...so that the type `impl std::future::Future` will meet its required lifetime bounds
|
||||
note: ...so that the type `impl Future` will meet its required lifetime bounds
|
||||
--> $DIR/async_nonstatic_userdata.rs:11:21
|
||||
|
|
||||
11 | methods.add_async_method("print", |_, data, ()| async move {
|
||||
|
||||
@@ -1,11 +1,16 @@
|
||||
error[E0277]: the type `std::cell::UnsafeCell<()>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
|
||||
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
|
||||
|
|
||||
7 | catch_unwind(|| lua.create_table().unwrap());
|
||||
| ^^^^^^^^^^^^ `std::cell::UnsafeCell<()>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
|
||||
| ^^^^^^^^^^^^ `UnsafeCell<()>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
|
||||
|
|
||||
= help: within `mlua::lua::Lua`, the trait `std::panic::RefUnwindSafe` is not implemented for `std::cell::UnsafeCell<()>`
|
||||
= note: required because it appears within the type `std::marker::PhantomData<std::cell::UnsafeCell<()>>`
|
||||
= note: required because it appears within the type `mlua::lua::Lua`
|
||||
= note: required because of the requirements on the impl of `std::panic::UnwindSafe` for `&mlua::lua::Lua`
|
||||
= note: required because it appears within the type `[closure@$DIR/tests/compile/lua_norefunwindsafe.rs:7:18: 7:48 lua:&mlua::lua::Lua]`
|
||||
::: $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]`
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
error[E0277]: `std::rc::Rc<std::cell::Cell<i32>>` cannot be sent between threads safely
|
||||
error[E0277]: `Rc<Cell<i32>>` cannot be sent between threads safely
|
||||
--> $DIR/non_send.rs:11:9
|
||||
|
|
||||
11 | lua.create_function(move |_, ()| {
|
||||
| _________^^^^^^^^^^^^^^^_-
|
||||
| | |
|
||||
| | `std::rc::Rc<std::cell::Cell<i32>>` cannot be sent between threads safely
|
||||
| | `Rc<Cell<i32>>` cannot be sent between threads safely
|
||||
12 | | Ok(data.get())
|
||||
13 | | })?
|
||||
| |_____- within this `[closure@$DIR/tests/compile/non_send.rs:11:25: 13:6 data:std::rc::Rc<std::cell::Cell<i32>>]`
|
||||
| |_____- within this `[closure@$DIR/tests/compile/non_send.rs:11:25: 13:6]`
|
||||
|
|
||||
= help: within `[closure@$DIR/tests/compile/non_send.rs:11:25: 13:6 data:std::rc::Rc<std::cell::Cell<i32>>]`, the trait `std::marker::Send` is not implemented for `std::rc::Rc<std::cell::Cell<i32>>`
|
||||
= note: required because it appears within the type `[closure@$DIR/tests/compile/non_send.rs:11:25: 13:6 data:std::rc::Rc<std::cell::Cell<i32>>]`
|
||||
= note: required because of the requirements on the impl of `mlua::types::MaybeSend` for `[closure@$DIR/tests/compile/non_send.rs:11:25: 13:6 data:std::rc::Rc<std::cell::Cell<i32>>]`
|
||||
= help: within `[closure@$DIR/tests/compile/non_send.rs:11:25: 13:6]`, the trait `Send` is not implemented for `Rc<Cell<i32>>`
|
||||
= note: required because it appears within the type `[closure@$DIR/tests/compile/non_send.rs:11:25: 13:6]`
|
||||
= note: required because of the requirements on the impl of `mlua::types::MaybeSend` for `[closure@$DIR/tests/compile/non_send.rs:11:25: 13:6]`
|
||||
|
||||
@@ -1,13 +1,18 @@
|
||||
error[E0277]: the type `std::cell::UnsafeCell<()>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
|
||||
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
|
||||
|
|
||||
8 | catch_unwind(move || table.set("a", "b").unwrap());
|
||||
| ^^^^^^^^^^^^ `std::cell::UnsafeCell<()>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
|
||||
| ^^^^^^^^^^^^ `UnsafeCell<()>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
|
||||
|
|
||||
= help: within `mlua::lua::Lua`, the trait `std::panic::RefUnwindSafe` is not implemented for `std::cell::UnsafeCell<()>`
|
||||
= note: required because it appears within the type `std::marker::PhantomData<std::cell::UnsafeCell<()>>`
|
||||
= note: required because it appears within the type `mlua::lua::Lua`
|
||||
= note: required because of the requirements on the impl of `std::panic::UnwindSafe` for `&mlua::lua::Lua`
|
||||
::: $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 `mlua::types::LuaRef<'_>`
|
||||
= note: required because it appears within the type `mlua::table::Table<'_>`
|
||||
= note: required because it appears within the type `[closure@$DIR/tests/compile/ref_nounwindsafe.rs:8:18: 8:54 table:mlua::table::Table<'_>]`
|
||||
= 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]`
|
||||
|
||||
@@ -1,45 +1,26 @@
|
||||
error[E0495]: cannot infer an appropriate lifetime for autoref due to conflicting requirements
|
||||
--> $DIR/scope_callback_capture.rs:8:14
|
||||
|
|
||||
8 | .create_function_mut(move |_, t: Table| {
|
||||
| ^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
note: first, the lifetime cannot outlive the anonymous lifetime #2 defined on the body at 5:15...
|
||||
--> $DIR/scope_callback_capture.rs:5:15
|
||||
|
|
||||
5 | lua.scope(|scope| {
|
||||
| _______________^
|
||||
6 | | let mut inner: Option<Table> = None;
|
||||
7 | | let f = scope
|
||||
8 | | .create_function_mut(move |_, t: Table| {
|
||||
... |
|
||||
16 | | Ok(())
|
||||
17 | | });
|
||||
| |_____^
|
||||
note: ...so that reference does not outlive borrowed content
|
||||
warning: unused variable: `old`
|
||||
--> $DIR/scope_callback_capture.rs:9:29
|
||||
|
|
||||
9 | if let Some(old) = inner.take() {
|
||||
| ^^^ help: if this is intentional, prefix it with an underscore: `_old`
|
||||
|
|
||||
= note: `#[warn(unused_variables)]` on by default
|
||||
|
||||
error[E0521]: borrowed data escapes outside of closure
|
||||
--> $DIR/scope_callback_capture.rs:7:17
|
||||
|
|
||||
7 | let f = scope
|
||||
| ^^^^^
|
||||
note: but, the lifetime must be valid for the method call at 5:5...
|
||||
--> $DIR/scope_callback_capture.rs:5:5
|
||||
|
|
||||
5 | / lua.scope(|scope| {
|
||||
6 | | let mut inner: Option<Table> = None;
|
||||
7 | | let f = scope
|
||||
5 | lua.scope(|scope| {
|
||||
| -----
|
||||
| |
|
||||
| `scope` declared here, outside of the closure body
|
||||
| `scope` is a reference that is only valid in the closure body
|
||||
6 | let mut inner: Option<Table> = None;
|
||||
7 | let f = scope
|
||||
| _________________^
|
||||
8 | | .create_function_mut(move |_, t: Table| {
|
||||
9 | | if let Some(old) = inner.take() {
|
||||
10 | | // Access old callback `Lua`.
|
||||
... |
|
||||
16 | | Ok(())
|
||||
17 | | });
|
||||
| |______^
|
||||
note: ...so that a type/lifetime parameter is in scope here
|
||||
--> $DIR/scope_callback_capture.rs:5:5
|
||||
|
|
||||
5 | / lua.scope(|scope| {
|
||||
6 | | let mut inner: Option<Table> = None;
|
||||
7 | | let f = scope
|
||||
8 | | .create_function_mut(move |_, t: Table| {
|
||||
... |
|
||||
16 | | Ok(())
|
||||
17 | | });
|
||||
| |______^
|
||||
13 | | Ok(())
|
||||
14 | | })?;
|
||||
| |______________^ `scope` escapes the closure body here
|
||||
|
||||
@@ -1,45 +1,42 @@
|
||||
error[E0495]: cannot infer an appropriate lifetime for autoref due to conflicting requirements
|
||||
--> $DIR/scope_callback_inner.rs:8:14
|
||||
|
|
||||
8 | .create_function_mut(|_, t: Table| {
|
||||
| ^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
note: first, the lifetime cannot outlive the anonymous lifetime #2 defined on the body at 5:15...
|
||||
--> $DIR/scope_callback_inner.rs:5:15
|
||||
|
|
||||
5 | lua.scope(|scope| {
|
||||
| _______________^
|
||||
6 | | let mut inner: Option<Table> = None;
|
||||
7 | | let f = scope
|
||||
8 | | .create_function_mut(|_, t: Table| {
|
||||
... |
|
||||
13 | | Ok(())
|
||||
14 | | });
|
||||
| |_____^
|
||||
note: ...so that reference does not outlive borrowed content
|
||||
error[E0521]: borrowed data escapes outside of closure
|
||||
--> $DIR/scope_callback_inner.rs:7:17
|
||||
|
|
||||
7 | let f = scope
|
||||
| ^^^^^
|
||||
note: but, the lifetime must be valid for the method call at 5:5...
|
||||
--> $DIR/scope_callback_inner.rs:5:5
|
||||
|
|
||||
5 | / lua.scope(|scope| {
|
||||
6 | | let mut inner: Option<Table> = None;
|
||||
7 | | let f = scope
|
||||
5 | lua.scope(|scope| {
|
||||
| -----
|
||||
| |
|
||||
| `scope` declared here, outside of the closure body
|
||||
| `scope` is a reference that is only valid in the closure body
|
||||
6 | let mut inner: Option<Table> = None;
|
||||
7 | let f = scope
|
||||
| _________________^
|
||||
8 | | .create_function_mut(|_, t: Table| {
|
||||
... |
|
||||
13 | | Ok(())
|
||||
14 | | });
|
||||
| |______^
|
||||
note: ...so that a type/lifetime parameter is in scope here
|
||||
--> $DIR/scope_callback_inner.rs:5:5
|
||||
9 | | inner = Some(t);
|
||||
10 | | Ok(())
|
||||
11 | | })?;
|
||||
| |______________^ `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
|
||||
|
|
||||
5 | / lua.scope(|scope| {
|
||||
6 | | let mut inner: Option<Table> = None;
|
||||
7 | | let f = scope
|
||||
5 | lua.scope(|scope| {
|
||||
| ----- has type `&Scope<'_, '2>`
|
||||
...
|
||||
8 | .create_function_mut(|_, t: Table| {
|
||||
| ^^^^^^^^^^^^^ may outlive borrowed value `inner`
|
||||
9 | inner = Some(t);
|
||||
| ----- `inner` is borrowed here
|
||||
|
|
||||
note: function requires argument type to outlive `'2`
|
||||
--> $DIR/scope_callback_inner.rs:7:17
|
||||
|
|
||||
7 | let f = scope
|
||||
| _________________^
|
||||
8 | | .create_function_mut(|_, t: Table| {
|
||||
... |
|
||||
13 | | Ok(())
|
||||
14 | | });
|
||||
| |______^
|
||||
9 | | inner = Some(t);
|
||||
10 | | Ok(())
|
||||
11 | | })?;
|
||||
| |______________^
|
||||
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,11 +1,30 @@
|
||||
error: borrowed data cannot be stored outside of its closure
|
||||
--> $DIR/scope_callback_outer.rs:7:17
|
||||
|
|
||||
5 | let mut outer: Option<Table> = None;
|
||||
| --------- ...so that variable is valid at time of its declaration
|
||||
6 | lua.scope(|scope| {
|
||||
| ------- borrowed data cannot outlive this closure
|
||||
7 | let f = scope
|
||||
| ^^^^^ cannot be stored outside of its closure
|
||||
8 | .create_function_mut(|_, t: Table| {
|
||||
| ------------------- cannot infer an appropriate lifetime...
|
||||
error[E0521]: borrowed data escapes outside of closure
|
||||
--> $DIR/scope_callback_outer.rs:7:17
|
||||
|
|
||||
6 | lua.scope(|scope| {
|
||||
| -----
|
||||
| |
|
||||
| `scope` declared here, outside of the closure body
|
||||
| `scope` is a reference that is only valid in the closure body
|
||||
7 | let f = scope
|
||||
| _________________^
|
||||
8 | | .create_function_mut(|_, t: Table| {
|
||||
9 | | outer = Some(t);
|
||||
10 | | Ok(())
|
||||
11 | | })?;
|
||||
| |______________^ `scope` escapes the closure body here
|
||||
|
||||
error[E0597]: `outer` does not live long enough
|
||||
--> $DIR/scope_callback_outer.rs:9:17
|
||||
|
|
||||
6 | lua.scope(|scope| {
|
||||
| ------- value captured here
|
||||
...
|
||||
9 | outer = Some(t);
|
||||
| ^^^^^ borrowed value does not live long enough
|
||||
...
|
||||
15 | }
|
||||
| -
|
||||
| |
|
||||
| `outer` dropped here while still borrowed
|
||||
| borrow might be used here, when `outer` is dropped and runs the destructor for type `Option<LuaTable<'_>>`
|
||||
|
||||
@@ -2,7 +2,7 @@ error[E0373]: closure may outlive the current function, but it borrows `test`, w
|
||||
--> $DIR/scope_invariance.rs:14:38
|
||||
|
|
||||
9 | lua.scope(|scope| {
|
||||
| ----- has type `&mlua::scope::Scope<'_, '1>`
|
||||
| ----- has type `&Scope<'_, '1>`
|
||||
...
|
||||
14 | .create_function_mut(|_, ()| {
|
||||
| ^^^^^^^ may outlive borrowed value `test`
|
||||
|
||||
@@ -2,7 +2,7 @@ error[E0597]: `ibad` does not live long enough
|
||||
--> $DIR/scope_userdata_borrow.rs:15:56
|
||||
|
|
||||
11 | lua.scope(|scope| {
|
||||
| ----- has type `&mlua::scope::Scope<'_, '1>`
|
||||
| ----- has type `&Scope<'_, '1>`
|
||||
...
|
||||
15 | scope.create_nonstatic_userdata(MyUserData(&ibad)).unwrap();
|
||||
| -------------------------------------------^^^^^--
|
||||
|
||||
+14
-12
@@ -1,15 +1,3 @@
|
||||
#![cfg_attr(
|
||||
all(feature = "luajit", target_os = "macos", target_arch = "x86_64"),
|
||||
feature(link_args)
|
||||
)]
|
||||
|
||||
#[cfg_attr(
|
||||
all(feature = "luajit", target_os = "macos", target_arch = "x86_64"),
|
||||
link_args = "-pagezero_size 10000 -image_base 100000000",
|
||||
allow(unused_attributes)
|
||||
)]
|
||||
extern "system" {}
|
||||
|
||||
use mlua::{Function, Lua, Result, String};
|
||||
|
||||
#[test]
|
||||
@@ -87,3 +75,17 @@ fn test_rust_function() -> Result<()> {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_dump() -> Result<()> {
|
||||
let lua = unsafe { Lua::unsafe_new() };
|
||||
|
||||
let concat_lua = lua
|
||||
.load(r#"function(arg1, arg2) return arg1 .. arg2 end"#)
|
||||
.eval::<Function>()?;
|
||||
let concat = lua.load(&concat_lua.dump(false)?).into_function()?;
|
||||
|
||||
assert_eq!(concat.call::<_, String>(("foo", "bar"))?, "foobar");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1,15 +1,3 @@
|
||||
#![cfg_attr(
|
||||
all(feature = "luajit", target_os = "macos", target_arch = "x86_64"),
|
||||
feature(link_args)
|
||||
)]
|
||||
|
||||
#[cfg_attr(
|
||||
all(feature = "luajit", target_os = "macos", target_arch = "x86_64"),
|
||||
link_args = "-pagezero_size 10000 -image_base 100000000",
|
||||
allow(unused_attributes)
|
||||
)]
|
||||
extern "system" {}
|
||||
|
||||
use std::cell::RefCell;
|
||||
use std::ops::Deref;
|
||||
use std::str;
|
||||
|
||||
@@ -1,15 +1,3 @@
|
||||
#![cfg_attr(
|
||||
all(feature = "luajit", target_os = "macos", target_arch = "x86_64"),
|
||||
feature(link_args)
|
||||
)]
|
||||
|
||||
#[cfg_attr(
|
||||
all(feature = "luajit", target_os = "macos", target_arch = "x86_64"),
|
||||
link_args = "-pagezero_size 10000 -image_base 100000000",
|
||||
allow(unused_attributes)
|
||||
)]
|
||||
extern "system" {}
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
use mlua::{Lua, Result, UserData};
|
||||
|
||||
+51
-16
@@ -1,19 +1,9 @@
|
||||
#![cfg_attr(
|
||||
all(feature = "luajit", target_os = "macos", target_arch = "x86_64"),
|
||||
feature(link_args)
|
||||
)]
|
||||
|
||||
#[cfg_attr(
|
||||
all(feature = "luajit", target_os = "macos", target_arch = "x86_64"),
|
||||
link_args = "-pagezero_size 10000 -image_base 100000000",
|
||||
allow(unused_attributes)
|
||||
)]
|
||||
extern "system" {}
|
||||
|
||||
use std::cell::Cell;
|
||||
use std::rc::Rc;
|
||||
|
||||
use mlua::{Error, Function, Lua, MetaMethod, Result, String, UserData, UserDataMethods};
|
||||
use mlua::{
|
||||
AnyUserData, Error, Function, Lua, MetaMethod, Result, String, UserData, UserDataMethods,
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn scope_func() -> Result<()> {
|
||||
@@ -57,17 +47,62 @@ fn scope_drop() -> Result<()> {
|
||||
|
||||
lua.scope(|scope| {
|
||||
lua.globals()
|
||||
.set("test", scope.create_userdata(MyUserdata(rc.clone()))?)?;
|
||||
.set("static_ud", scope.create_userdata(MyUserdata(rc.clone()))?)?;
|
||||
assert_eq!(Rc::strong_count(&rc), 2);
|
||||
Ok(())
|
||||
})?;
|
||||
assert_eq!(Rc::strong_count(&rc), 1);
|
||||
|
||||
match lua.load("test:method()").exec() {
|
||||
Err(Error::CallbackError { .. }) => {}
|
||||
match lua.load("static_ud:method()").exec() {
|
||||
Err(Error::CallbackError { ref cause, .. }) => match cause.as_ref() {
|
||||
Error::CallbackDestructed => {}
|
||||
e => panic!("expected CallbackDestructed, got {:?}", e),
|
||||
},
|
||||
r => panic!("improper return for destructed userdata: {:?}", r),
|
||||
};
|
||||
|
||||
let static_ud = lua.globals().get::<_, AnyUserData>("static_ud")?;
|
||||
match static_ud.borrow::<MyUserdata>() {
|
||||
Ok(_) => panic!("borrowed destructed userdata"),
|
||||
Err(Error::UserDataDestructed) => {}
|
||||
Err(e) => panic!("expected UserDataDestructed, got {:?}", e),
|
||||
}
|
||||
|
||||
// Check non-static UserData drop
|
||||
struct MyUserDataRef<'a>(&'a Cell<i64>);
|
||||
|
||||
impl<'a> UserData for MyUserDataRef<'a> {
|
||||
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
|
||||
methods.add_method("inc", |_, data, ()| {
|
||||
data.0.set(data.0.get() + 1);
|
||||
Ok(())
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
let i = Cell::new(1);
|
||||
lua.scope(|scope| {
|
||||
lua.globals().set(
|
||||
"nonstatic_ud",
|
||||
scope.create_nonstatic_userdata(MyUserDataRef(&i))?,
|
||||
)
|
||||
})?;
|
||||
|
||||
match lua.load("nonstatic_ud:inc(1)").exec() {
|
||||
Err(Error::CallbackError { ref cause, .. }) => match cause.as_ref() {
|
||||
Error::CallbackDestructed => {}
|
||||
e => panic!("expected CallbackDestructed, got {:?}", e),
|
||||
},
|
||||
r => panic!("improper return for destructed userdata: {:?}", r),
|
||||
};
|
||||
|
||||
let nonstatic_ud = lua.globals().get::<_, AnyUserData>("nonstatic_ud")?;
|
||||
match nonstatic_ud.borrow::<MyUserDataRef>() {
|
||||
Ok(_) => panic!("borrowed destructed userdata"),
|
||||
Err(Error::UserDataDestructed) => {}
|
||||
Err(e) => panic!("expected UserDataDestructed, got {:?}", e),
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
+343
@@ -0,0 +1,343 @@
|
||||
#![cfg(feature = "serialize")]
|
||||
|
||||
use mlua::{Error, Lua, LuaSerdeExt, Result as LuaResult, UserData, Value};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[test]
|
||||
fn test_serialize() -> Result<(), Box<dyn std::error::Error>> {
|
||||
#[derive(Serialize)]
|
||||
struct MyUserData(i64, String);
|
||||
|
||||
impl UserData for MyUserData {}
|
||||
|
||||
let lua = Lua::new();
|
||||
let globals = lua.globals();
|
||||
|
||||
let ud = lua.create_ser_userdata(MyUserData(123, "test userdata".into()))?;
|
||||
globals.set("ud", ud)?;
|
||||
globals.set("null", lua.null()?)?;
|
||||
|
||||
let empty_array = lua.create_table()?;
|
||||
empty_array.set_metatable(Some(lua.array_metatable()?));
|
||||
globals.set("empty_array", empty_array)?;
|
||||
|
||||
let val = lua
|
||||
.load(
|
||||
r#"
|
||||
{
|
||||
_bool = true,
|
||||
_integer = 123,
|
||||
_number = 321.99,
|
||||
_string = "test string serialization",
|
||||
_table_arr = {nil, "value 1", nil, "value 2", {}},
|
||||
_table_map = {["table"] = "map", ["null"] = null},
|
||||
_bytes = "\240\040\140\040",
|
||||
_userdata = ud,
|
||||
_null = null,
|
||||
_empty_map = {},
|
||||
_empty_array = empty_array,
|
||||
}
|
||||
"#,
|
||||
)
|
||||
.eval::<Value>()?;
|
||||
|
||||
let json = serde_json::json!({
|
||||
"_bool": true,
|
||||
"_integer": 123,
|
||||
"_number": 321.99,
|
||||
"_string": "test string serialization",
|
||||
"_table_arr": [null, "value 1", null, "value 2", {}],
|
||||
"_table_map": {"table": "map", "null": null},
|
||||
"_bytes": [240, 40, 140, 40],
|
||||
"_userdata": [123, "test userdata"],
|
||||
"_null": null,
|
||||
"_empty_map": {},
|
||||
"_empty_array": [],
|
||||
});
|
||||
|
||||
assert_eq!(serde_json::to_value(&val)?, json);
|
||||
|
||||
// Test to-from loop
|
||||
let val = lua.to_value(&json)?;
|
||||
let expected_json = lua.from_value::<serde_json::Value>(val)?;
|
||||
assert_eq!(expected_json, json);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_serialize_in_scope() -> LuaResult<()> {
|
||||
#[derive(Serialize, Clone)]
|
||||
struct MyUserData(i64, String);
|
||||
|
||||
impl UserData for MyUserData {}
|
||||
|
||||
let lua = Lua::new();
|
||||
lua.scope(|scope| {
|
||||
let ud = scope.create_ser_userdata(MyUserData(-5, "test userdata".into()))?;
|
||||
assert_eq!(
|
||||
serde_json::to_value(&ud).unwrap(),
|
||||
serde_json::json!((-5, "test userdata"))
|
||||
);
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
lua.scope(|scope| {
|
||||
let ud = scope.create_ser_userdata(MyUserData(-5, "test userdata".into()))?;
|
||||
lua.globals().set("ud", ud)
|
||||
})?;
|
||||
let val = lua.load("ud").eval::<Value>()?;
|
||||
match serde_json::to_value(&val) {
|
||||
Ok(v) => panic!("expected destructed error, got {}", v),
|
||||
Err(e) if e.to_string().contains("destructed") => {}
|
||||
Err(e) => panic!("expected destructed error, got {}", e),
|
||||
}
|
||||
|
||||
struct MyUserDataRef<'a>(&'a ());
|
||||
|
||||
impl<'a> UserData for MyUserDataRef<'a> {}
|
||||
|
||||
lua.scope(|scope| {
|
||||
let ud = scope.create_nonstatic_userdata(MyUserDataRef(&()))?;
|
||||
match serde_json::to_value(&ud) {
|
||||
Ok(v) => panic!("expected serialization error, got {}", v),
|
||||
Err(serde_json::Error { .. }) => {}
|
||||
};
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_serialize_failure() -> Result<(), Box<dyn std::error::Error>> {
|
||||
#[derive(Serialize)]
|
||||
struct MyUserData(i64);
|
||||
|
||||
impl UserData for MyUserData {}
|
||||
|
||||
let lua = Lua::new();
|
||||
|
||||
let ud = Value::UserData(lua.create_userdata(MyUserData(123))?);
|
||||
match serde_json::to_value(&ud) {
|
||||
Ok(v) => panic!("expected serialization error, got {}", v),
|
||||
Err(serde_json::Error { .. }) => {}
|
||||
}
|
||||
|
||||
let func = lua.create_function(|_, _: ()| Ok(()))?;
|
||||
match serde_json::to_value(&Value::Function(func.clone())) {
|
||||
Ok(v) => panic!("expected serialization error, got {}", v),
|
||||
Err(serde_json::Error { .. }) => {}
|
||||
}
|
||||
|
||||
let thr = lua.create_thread(func)?;
|
||||
match serde_json::to_value(&Value::Thread(thr)) {
|
||||
Ok(v) => panic!("expected serialization error, got {}", v),
|
||||
Err(serde_json::Error { .. }) => {}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_to_value_struct() -> 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()
|
||||
}
|
||||
|
||||
#[test]
|
||||
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 {
|
||||
Unit,
|
||||
Integer(u32),
|
||||
Tuple(u32, u32),
|
||||
Struct { a: u32 },
|
||||
}
|
||||
|
||||
let u = E::Unit;
|
||||
globals.set("value", lua.to_value(&u)?)?;
|
||||
lua.load(r#"assert(value == "Unit")"#).exec()?;
|
||||
|
||||
let n = E::Integer(1);
|
||||
globals.set("value", lua.to_value(&n)?)?;
|
||||
lua.load(r#"assert(value["Integer"] == 1)"#).exec()?;
|
||||
|
||||
let t = E::Tuple(1, 2);
|
||||
globals.set("value", lua.to_value(&t)?)?;
|
||||
lua.load(
|
||||
r#"
|
||||
assert(value["Tuple"][1] == 1)
|
||||
assert(value["Tuple"][2] == 2)
|
||||
"#,
|
||||
)
|
||||
.exec()?;
|
||||
|
||||
let s = E::Struct { a: 1 };
|
||||
globals.set("value", lua.to_value(&s)?)?;
|
||||
lua.load(r#"assert(value["Struct"]["a"] == 1)"#).exec()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_from_value_struct() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let lua = Lua::new();
|
||||
|
||||
#[derive(Deserialize, PartialEq, Debug)]
|
||||
struct Test {
|
||||
int: u32,
|
||||
seq: Vec<String>,
|
||||
map: std::collections::HashMap<i32, i32>,
|
||||
empty: Vec<()>,
|
||||
tuple: (u8, u8, u8),
|
||||
}
|
||||
|
||||
let value = lua
|
||||
.load(
|
||||
r#"
|
||||
{
|
||||
int = 1,
|
||||
seq = {"a", "b"},
|
||||
map = {2, [4] = 1},
|
||||
empty = {},
|
||||
tuple = {10, 20, 30},
|
||||
}
|
||||
"#,
|
||||
)
|
||||
.eval::<Value>()?;
|
||||
let got = lua.from_value(value)?;
|
||||
assert_eq!(
|
||||
Test {
|
||||
int: 1,
|
||||
seq: vec!["a".into(), "b".into()],
|
||||
map: vec![(1, 2), (4, 1)].into_iter().collect(),
|
||||
empty: vec![],
|
||||
tuple: (10, 20, 30),
|
||||
},
|
||||
got
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_from_value_enum() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let lua = Lua::new();
|
||||
|
||||
#[derive(Deserialize, PartialEq, Debug)]
|
||||
enum E {
|
||||
Unit,
|
||||
Integer(u32),
|
||||
Tuple(u32, u32),
|
||||
Struct { a: u32 },
|
||||
}
|
||||
|
||||
let value = lua.load(r#""Unit""#).eval()?;
|
||||
let got = lua.from_value(value)?;
|
||||
assert_eq!(E::Unit, got);
|
||||
|
||||
let value = lua.load(r#"{Integer = 1}"#).eval()?;
|
||||
let got = lua.from_value(value)?;
|
||||
assert_eq!(E::Integer(1), got);
|
||||
|
||||
let value = lua.load(r#"{Tuple = {1, 2}}"#).eval()?;
|
||||
let got = lua.from_value(value)?;
|
||||
assert_eq!(E::Tuple(1, 2), got);
|
||||
|
||||
let value = lua.load(r#"{Struct = {a = 3}}"#).eval()?;
|
||||
let got = lua.from_value(value)?;
|
||||
assert_eq!(E::Struct { a: 3 }, got);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_from_value_enum_untagged() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let lua = Lua::new();
|
||||
lua.globals().set("null", lua.null()?)?;
|
||||
|
||||
#[derive(Deserialize, PartialEq, Debug)]
|
||||
#[serde(untagged)]
|
||||
enum Eut {
|
||||
Unit,
|
||||
Integer(u64),
|
||||
Tuple(u32, u32),
|
||||
Struct { a: u32 },
|
||||
}
|
||||
|
||||
let value = lua.load(r#"null"#).eval()?;
|
||||
let got = lua.from_value(value)?;
|
||||
assert_eq!(Eut::Unit, got);
|
||||
|
||||
let value = lua.load(r#"1"#).eval()?;
|
||||
let got = lua.from_value(value)?;
|
||||
assert_eq!(Eut::Integer(1), got);
|
||||
|
||||
let value = lua.load(r#"{3, 1}"#).eval()?;
|
||||
let got = lua.from_value(value)?;
|
||||
assert_eq!(Eut::Tuple(3, 1), got);
|
||||
|
||||
let value = lua.load(r#"{a = 10}"#).eval()?;
|
||||
let got = lua.from_value(value)?;
|
||||
assert_eq!(Eut::Struct { a: 10 }, got);
|
||||
|
||||
let value = lua.load(r#"{b = 12}"#).eval()?;
|
||||
match lua.from_value::<Eut>(value) {
|
||||
Ok(v) => panic!("expected Error::DeserializeError, got {:?}", v),
|
||||
Err(Error::DeserializeError(_)) => {}
|
||||
Err(e) => panic!("expected Error::DeserializeError, got {}", e),
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -1,15 +1,3 @@
|
||||
#![cfg_attr(
|
||||
all(feature = "luajit", target_os = "macos", target_arch = "x86_64"),
|
||||
feature(link_args)
|
||||
)]
|
||||
|
||||
#[cfg_attr(
|
||||
all(feature = "luajit", target_os = "macos", target_arch = "x86_64"),
|
||||
link_args = "-pagezero_size 10000 -image_base 100000000",
|
||||
allow(unused_attributes)
|
||||
)]
|
||||
extern "system" {}
|
||||
|
||||
use std::borrow::Cow;
|
||||
|
||||
use mlua::{Lua, Result, String};
|
||||
|
||||
@@ -1,15 +1,3 @@
|
||||
#![cfg_attr(
|
||||
all(feature = "luajit", target_os = "macos", target_arch = "x86_64"),
|
||||
feature(link_args)
|
||||
)]
|
||||
|
||||
#[cfg_attr(
|
||||
all(feature = "luajit", target_os = "macos", target_arch = "x86_64"),
|
||||
link_args = "-pagezero_size 10000 -image_base 100000000",
|
||||
allow(unused_attributes)
|
||||
)]
|
||||
extern "system" {}
|
||||
|
||||
use mlua::{Lua, Nil, Result, Table, TableExt, Value};
|
||||
|
||||
#[test]
|
||||
|
||||
+149
-63
@@ -1,23 +1,11 @@
|
||||
#![cfg_attr(
|
||||
all(feature = "luajit", target_os = "macos", target_arch = "x86_64"),
|
||||
feature(link_args)
|
||||
)]
|
||||
|
||||
#[cfg_attr(
|
||||
all(feature = "luajit", target_os = "macos", target_arch = "x86_64"),
|
||||
link_args = "-pagezero_size 10000 -image_base 100000000",
|
||||
allow(unused_attributes)
|
||||
)]
|
||||
extern "system" {}
|
||||
|
||||
use std::iter::FromIterator;
|
||||
use std::panic::catch_unwind;
|
||||
use std::panic::{catch_unwind, AssertUnwindSafe};
|
||||
use std::sync::Arc;
|
||||
use std::{error, f32, f64, fmt};
|
||||
|
||||
use mlua::{
|
||||
Error, ExternalError, Function, Lua, Nil, Result, StdLib, String, Table, UserData, Value,
|
||||
Variadic,
|
||||
ChunkMode, Error, ExternalError, Function, Lua, Nil, Result, StdLib, String, Table, UserData,
|
||||
Value, Variadic,
|
||||
};
|
||||
|
||||
#[test]
|
||||
@@ -56,6 +44,37 @@ fn test_safety() -> Result<()> {
|
||||
Ok(_) => panic!("expected RuntimeError, got no error"),
|
||||
}
|
||||
|
||||
match lua.load("1 + 1").set_mode(ChunkMode::Binary).exec() {
|
||||
Err(Error::SafetyError(msg)) => {
|
||||
assert!(msg.contains("binary chunks are disabled in safe mode"))
|
||||
}
|
||||
Err(e) => panic!("expected SafetyError, got {:?}", e),
|
||||
Ok(_) => panic!("expected SafetyError, got no error"),
|
||||
}
|
||||
|
||||
let bytecode = lua.load("return 1 + 1").into_function()?.dump(true)?;
|
||||
match lua.load(&bytecode).exec() {
|
||||
Err(Error::SafetyError(msg)) => {
|
||||
assert!(msg.contains("binary chunks are disabled in safe mode"))
|
||||
}
|
||||
Err(e) => panic!("expected SafetyError, got {:?}", e),
|
||||
Ok(_) => panic!("expected SafetyError, got no error"),
|
||||
}
|
||||
drop(lua);
|
||||
|
||||
// Test safety rules after dynamically loading `package` library
|
||||
let lua = Lua::new_with(StdLib::NONE)?;
|
||||
assert!(lua.globals().get::<_, Option<Value>>("require")?.is_none());
|
||||
lua.load_from_std_lib(StdLib::PACKAGE)?;
|
||||
match lua.load(r#"package.loadlib()"#).exec() {
|
||||
Err(Error::CallbackError { ref cause, .. }) => match cause.as_ref() {
|
||||
Error::SafetyError(_) => {}
|
||||
e => panic!("expected SafetyError cause, got {:?}", e),
|
||||
},
|
||||
Err(e) => panic!("expected CallbackError, got {:?}", e),
|
||||
Ok(_) => panic!("expected CallbackError, got no error"),
|
||||
};
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -127,6 +146,41 @@ fn test_eval() -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_load_mode() -> Result<()> {
|
||||
let lua = unsafe { Lua::unsafe_new() };
|
||||
|
||||
assert_eq!(
|
||||
lua.load("1 + 1").set_mode(ChunkMode::Text).eval::<i32>()?,
|
||||
2
|
||||
);
|
||||
match lua.load("1 + 1").set_mode(ChunkMode::Binary).exec() {
|
||||
Ok(_) => panic!("expected SyntaxError, got no error"),
|
||||
Err(Error::SyntaxError { message: msg, .. }) => {
|
||||
assert!(msg.contains("attempt to load a text chunk"))
|
||||
}
|
||||
Err(e) => panic!("expected SyntaxError, got {:?}", e),
|
||||
};
|
||||
|
||||
let bytecode = lua.load("return 1 + 1").into_function()?.dump(true)?;
|
||||
assert_eq!(lua.load(&bytecode).eval::<i32>()?, 2);
|
||||
assert_eq!(
|
||||
lua.load(&bytecode)
|
||||
.set_mode(ChunkMode::Binary)
|
||||
.eval::<i32>()?,
|
||||
2
|
||||
);
|
||||
match lua.load(&bytecode).set_mode(ChunkMode::Text).exec() {
|
||||
Ok(_) => panic!("expected SyntaxError, got no error"),
|
||||
Err(Error::SyntaxError { message: msg, .. }) => {
|
||||
assert!(msg.contains("attempt to load a binary chunk"))
|
||||
}
|
||||
Err(e) => panic!("expected SyntaxError, got {:?}", e),
|
||||
};
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_lua_multi() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
@@ -318,62 +372,92 @@ fn test_error() -> Result<()> {
|
||||
|
||||
assert!(understand_recursion.call::<_, ()>(()).is_err());
|
||||
|
||||
match catch_unwind(|| -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
let globals = lua.globals();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_panic() -> Result<()> {
|
||||
fn make_lua() -> Result<Lua> {
|
||||
let lua = Lua::new();
|
||||
let rust_panic_function =
|
||||
lua.create_function(|_, ()| -> Result<()> { panic!("rust panic") })?;
|
||||
lua.globals()
|
||||
.set("rust_panic_function", rust_panic_function)?;
|
||||
Ok(lua)
|
||||
}
|
||||
|
||||
// Test triggerting Lua error passing Rust panic (must be resumed)
|
||||
{
|
||||
let lua = make_lua()?;
|
||||
|
||||
match catch_unwind(AssertUnwindSafe(|| -> Result<()> {
|
||||
lua.load(
|
||||
r#"
|
||||
_, err = pcall(rust_panic_function)
|
||||
error(err)
|
||||
"#,
|
||||
)
|
||||
.exec()
|
||||
})) {
|
||||
Ok(Ok(_)) => panic!("no panic was detected"),
|
||||
Ok(Err(e)) => panic!("error during panic test {:?}", e),
|
||||
Err(p) => assert!(*p.downcast::<&str>().unwrap() == "rust panic"),
|
||||
};
|
||||
|
||||
// Trigger same panic again
|
||||
match lua.load("error(err)").exec() {
|
||||
Ok(_) => panic!("no error was detected"),
|
||||
Err(Error::PreviouslyResumedPanic) => {}
|
||||
Err(e) => panic!("expected PreviouslyResumedPanic, got {:?}", e),
|
||||
}
|
||||
}
|
||||
|
||||
// Test returning Rust panic (must be resumed)
|
||||
{
|
||||
let lua = make_lua()?;
|
||||
match catch_unwind(AssertUnwindSafe(|| -> Result<()> {
|
||||
let _catched_panic = lua
|
||||
.load(
|
||||
r#"
|
||||
-- Set global
|
||||
_, err = pcall(rust_panic_function)
|
||||
return err
|
||||
"#,
|
||||
)
|
||||
.eval::<Value>()?;
|
||||
Ok(())
|
||||
})) {
|
||||
Ok(_) => panic!("no panic was detected"),
|
||||
Err(_) => {}
|
||||
};
|
||||
|
||||
assert!(lua.globals().get::<_, Value>("err")? == Value::Nil);
|
||||
match lua.load("tostring(err)").exec() {
|
||||
Ok(_) => panic!("no error was detected"),
|
||||
Err(Error::CallbackError { ref cause, .. }) => match cause.as_ref() {
|
||||
Error::PreviouslyResumedPanic => {}
|
||||
e => panic!("expected PreviouslyResumedPanic, got {:?}", e),
|
||||
},
|
||||
Err(e) => panic!("expected CallbackError, got {:?}", e),
|
||||
}
|
||||
}
|
||||
|
||||
// Test representing rust panic as a string
|
||||
match catch_unwind(|| -> Result<()> {
|
||||
let lua = make_lua()?;
|
||||
lua.load(
|
||||
r#"
|
||||
function rust_panic()
|
||||
local _, err = pcall(function () rust_panic_function() end)
|
||||
if err ~= nil then
|
||||
error(err)
|
||||
end
|
||||
end
|
||||
local _, err = pcall(rust_panic_function)
|
||||
error(tostring(err))
|
||||
"#,
|
||||
)
|
||||
.exec()?;
|
||||
let rust_panic_function =
|
||||
lua.create_function(|_, ()| -> Result<()> { panic!("test_panic") })?;
|
||||
globals.set("rust_panic_function", rust_panic_function)?;
|
||||
|
||||
let rust_panic = globals.get::<_, Function>("rust_panic")?;
|
||||
|
||||
rust_panic.call::<_, ()>(())
|
||||
}) {
|
||||
Ok(Ok(_)) => panic!("no panic was detected"),
|
||||
Ok(Err(e)) => panic!("error during panic test {:?}", e),
|
||||
Err(p) => assert!(*p.downcast::<&str>().unwrap() == "test_panic"),
|
||||
};
|
||||
|
||||
match catch_unwind(|| -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
let globals = lua.globals();
|
||||
|
||||
lua.load(
|
||||
r#"
|
||||
function rust_panic()
|
||||
local _, err = pcall(function () rust_panic_function() end)
|
||||
if err ~= nil then
|
||||
error(tostring(err))
|
||||
end
|
||||
end
|
||||
"#,
|
||||
)
|
||||
.exec()?;
|
||||
let rust_panic_function =
|
||||
lua.create_function(|_, ()| -> Result<()> { panic!("test_panic") })?;
|
||||
globals.set("rust_panic_function", rust_panic_function)?;
|
||||
|
||||
let rust_panic = globals.get::<_, Function>("rust_panic")?;
|
||||
|
||||
rust_panic.call::<_, ()>(())
|
||||
.exec()
|
||||
}) {
|
||||
Ok(Ok(_)) => panic!("no error was detected"),
|
||||
Ok(Err(Error::RuntimeError(_))) => {}
|
||||
Ok(Err(e)) => panic!("unexpected error during panic test {:?}", e),
|
||||
Ok(Err(e)) => panic!("expected RuntimeError, got {:?}", e),
|
||||
Err(_) => panic!("panic was detected"),
|
||||
};
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -457,7 +541,9 @@ fn test_num_conversion() -> Result<()> {
|
||||
assert!(lua.load("math.huge").eval::<i64>().is_err());
|
||||
|
||||
assert_eq!(lua.unpack::<f64>(lua.pack(f32::MAX)?)?, f32::MAX as f64);
|
||||
assert!(lua.unpack::<f32>(lua.pack(f64::MAX)?).is_err());
|
||||
assert_eq!(lua.unpack::<f64>(lua.pack(f32::MIN)?)?, f32::MIN as f64);
|
||||
assert_eq!(lua.unpack::<f32>(lua.pack(f64::MAX)?)?, f32::INFINITY);
|
||||
assert_eq!(lua.unpack::<f32>(lua.pack(f64::MIN)?)?, f32::NEG_INFINITY);
|
||||
|
||||
assert_eq!(lua.unpack::<i128>(lua.pack(1i128 << 64)?)?, 1i128 << 64);
|
||||
|
||||
|
||||
@@ -1,15 +1,3 @@
|
||||
#![cfg_attr(
|
||||
all(feature = "luajit", target_os = "macos", target_arch = "x86_64"),
|
||||
feature(link_args)
|
||||
)]
|
||||
|
||||
#[cfg_attr(
|
||||
all(feature = "luajit", target_os = "macos", target_arch = "x86_64"),
|
||||
link_args = "-pagezero_size 10000 -image_base 100000000",
|
||||
allow(unused_attributes)
|
||||
)]
|
||||
extern "system" {}
|
||||
|
||||
use std::panic::catch_unwind;
|
||||
|
||||
use mlua::{Error, Function, Lua, Result, Thread, ThreadStatus};
|
||||
|
||||
@@ -1,15 +1,3 @@
|
||||
#![cfg_attr(
|
||||
all(feature = "luajit", target_os = "macos", target_arch = "x86_64"),
|
||||
feature(link_args)
|
||||
)]
|
||||
|
||||
#[cfg_attr(
|
||||
all(feature = "luajit", target_os = "macos", target_arch = "x86_64"),
|
||||
link_args = "-pagezero_size 10000 -image_base 100000000",
|
||||
allow(unused_attributes)
|
||||
)]
|
||||
extern "system" {}
|
||||
|
||||
use std::os::raw::c_void;
|
||||
|
||||
use mlua::{Function, LightUserData, Lua, Result};
|
||||
|
||||
+2
-14
@@ -1,15 +1,3 @@
|
||||
#![cfg_attr(
|
||||
all(feature = "luajit", target_os = "macos", target_arch = "x86_64"),
|
||||
feature(link_args)
|
||||
)]
|
||||
|
||||
#[cfg_attr(
|
||||
all(feature = "luajit", target_os = "macos", target_arch = "x86_64"),
|
||||
link_args = "-pagezero_size 10000 -image_base 100000000",
|
||||
allow(unused_attributes)
|
||||
)]
|
||||
extern "system" {}
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
#[cfg(feature = "lua54")]
|
||||
@@ -25,8 +13,8 @@ fn test_user_data() -> Result<()> {
|
||||
struct UserData1(i64);
|
||||
struct UserData2(Box<i64>);
|
||||
|
||||
impl UserData for UserData1 {};
|
||||
impl UserData for UserData2 {};
|
||||
impl UserData for UserData1 {}
|
||||
impl UserData for UserData2 {}
|
||||
|
||||
let lua = Lua::new();
|
||||
let userdata1 = lua.create_userdata(UserData1(1))?;
|
||||
|
||||
@@ -1,15 +1,3 @@
|
||||
#![cfg_attr(
|
||||
all(feature = "luajit", target_os = "macos", target_arch = "x86_64"),
|
||||
feature(link_args)
|
||||
)]
|
||||
|
||||
#[cfg_attr(
|
||||
all(feature = "luajit", target_os = "macos", target_arch = "x86_64"),
|
||||
link_args = "-pagezero_size 10000 -image_base 100000000",
|
||||
allow(unused_attributes)
|
||||
)]
|
||||
extern "system" {}
|
||||
|
||||
use mlua::{Lua, Result, Value};
|
||||
|
||||
#[test]
|
||||
|
||||
Reference in New Issue
Block a user