mirror of
https://github.com/mlua-rs/mlua
synced 2026-06-08 16:05:43 +00:00
Compare commits
123 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| fc84e8623e | |||
| 8aae9a7c3e | |||
| 24bbd62564 | |||
| 30af919931 | |||
| 89b5981c59 | |||
| 7feabffc37 | |||
| f24fdfdb80 | |||
| c702c5eff2 | |||
| e4daff8c16 | |||
| 8d474bbf8d | |||
| 9e3b0ecc1e | |||
| 42b396d0d1 | |||
| 14d5c2c887 | |||
| b84c10fde6 | |||
| b49f9539b6 | |||
| a208156ed2 | |||
| 242bdafa75 | |||
| b84d1bd65f | |||
| 4e92ea341b | |||
| bf286751fa | |||
| 3b94b4e86f | |||
| 9f0378b77e | |||
| d3f44354e0 | |||
| fca21d56d3 | |||
| 6e52bb7e65 | |||
| 08ffeb0ca9 | |||
| 3b9d8a7b5f | |||
| 0fe898c0dd | |||
| 821f1125b6 | |||
| 6c0096d8ac | |||
| abb95c3c56 | |||
| 64faebf407 | |||
| a944f4ad6f | |||
| bae424672a | |||
| 6f9eb82649 | |||
| e8de2a458a | |||
| 973b5c3bf5 | |||
| b610a79d66 | |||
| fe39ae09bf | |||
| 01714d2510 | |||
| 0bad4a0ff9 | |||
| 35b7504076 | |||
| c9b8eb5418 | |||
| ef7d123f80 | |||
| 20cba5de5b | |||
| 3e03f4201c | |||
| 5199b02346 | |||
| 5293b8d6d2 | |||
| 7541b6f3f3 | |||
| 205510a540 | |||
| 2250421438 | |||
| 8e57e6fa5a | |||
| f2dbbb091f | |||
| 13cfb4bd51 | |||
| 33ebacab49 | |||
| 3829b72212 | |||
| af67971e0d | |||
| 0f4bcca7ce | |||
| 585c0a25d8 | |||
| 64346ce56c | |||
| 108682cc71 | |||
| 4af7bcf0d9 | |||
| 1bb3c5c19f | |||
| a4567cb5f7 | |||
| 26d8d899f2 | |||
| 67bc0b1196 | |||
| e8505b5239 | |||
| 3f55958bdd | |||
| 463fc646bc | |||
| b5f1325f2f | |||
| 0625991a48 | |||
| 269ef9c55d | |||
| f5b88624ce | |||
| 2fae94586d | |||
| bc81d1016f | |||
| c19f12898d | |||
| c7541ef7d3 | |||
| 41a1a0d15a | |||
| c10169a380 | |||
| ced808d5ab | |||
| c95ac32741 | |||
| 14169eadb1 | |||
| 5a7ad9f7cd | |||
| decb5b9e37 | |||
| 1635903d3f | |||
| 2b2df708f9 | |||
| 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 |
@@ -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,macros --out xml --exclude-files benches --exclude-files build --exclude-files mlua_derive --exclude-files src/ffi --exclude-files tests
|
||||
|
||||
- name: Upload to codecov.io
|
||||
uses: codecov/codecov-action@v1
|
||||
with:
|
||||
token: ${{secrets.CODECOV_TOKEN}}
|
||||
fail_ci_if_error: false
|
||||
+77
-36
@@ -26,16 +26,79 @@ jobs:
|
||||
override: true
|
||||
- name: Build ${{ matrix.lua }} vendored
|
||||
run: |
|
||||
cargo build --release --features "${{ matrix.lua }} vendored"
|
||||
cargo build --release --features "${{ matrix.lua }} vendored async send serialize"
|
||||
cargo build --release --features "${{ matrix.lua }},vendored"
|
||||
cargo build --release --features "${{ matrix.lua }},vendored,async,send,serialize,macros"
|
||||
shell: bash
|
||||
- name: Build ${{ matrix.lua }} pkg-config
|
||||
if: ${{ matrix.os == 'ubuntu-18.04' && matrix.lua != 'lua54' }}
|
||||
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 }},vendored,async,send,serialize,macros"
|
||||
|
||||
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 }},vendored,async,send,serialize,macros"
|
||||
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 }},vendored,async,send,serialize,macros"
|
||||
shell: bash
|
||||
|
||||
test:
|
||||
name: Test
|
||||
runs-on: ${{ matrix.os }}
|
||||
@@ -43,7 +106,7 @@ jobs:
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-18.04, macos-latest, windows-latest]
|
||||
rust: [stable]
|
||||
rust: [stable, nightly]
|
||||
lua: [lua54, lua53, lua52, lua51, luajit]
|
||||
include:
|
||||
- os: ubuntu-18.04
|
||||
@@ -60,47 +123,25 @@ 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 serialize"
|
||||
cargo test --release --features "${{ matrix.lua }},vendored"
|
||||
cargo test --release --features "${{ matrix.lua }},vendored,async,send,serialize,macros"
|
||||
shell: bash
|
||||
- name: Run compile tests (macos lua53)
|
||||
if: ${{ matrix.os == 'macos-latest' && matrix.lua == 'lua53' }}
|
||||
run: |
|
||||
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_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
|
||||
cargo test --tests --release --features "luajit async send serialize" -- --test-threads=1
|
||||
shell: bash
|
||||
- name: Run LuaJIT vendored tests
|
||||
run: |
|
||||
cargo test --release --features "luajit vendored async send serialize"
|
||||
TRYBUILD=overwrite cargo test --release --features "${{ matrix.lua }},vendored" -- --ignored
|
||||
TRYBUILD=overwrite cargo test --release --features "${{ matrix.lua }},vendored,async,send,serialize,macros" -- --ignored
|
||||
shell: bash
|
||||
|
||||
test_modules:
|
||||
name: Test modules on Linux and macOS
|
||||
name: Test modules
|
||||
runs-on: ${{ matrix.os }}
|
||||
needs: build
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-18.04, macos-latest]
|
||||
rust: [stable]
|
||||
rust: [stable, nightly]
|
||||
lua: [lua54, lua53, lua52, lua51, luajit]
|
||||
include:
|
||||
- os: ubuntu-18.04
|
||||
@@ -116,8 +157,8 @@ jobs:
|
||||
override: true
|
||||
- name: Run ${{ matrix.lua }} module tests
|
||||
run: |
|
||||
(cd examples/module && cargo build --release --features "${{ matrix.lua }} vendored")
|
||||
(cd tests/module && cargo test --release --features "${{ matrix.lua }} vendored")
|
||||
(cd examples/module && cargo build --release --features "${{ matrix.lua }},vendored")
|
||||
(cd tests/module && cargo test --release --features "${{ matrix.lua }},vendored")
|
||||
shell: bash
|
||||
|
||||
test_modules_windows:
|
||||
@@ -126,7 +167,7 @@ jobs:
|
||||
needs: build
|
||||
strategy:
|
||||
matrix:
|
||||
lua: [lua53, luajit]
|
||||
lua: [lua54, luajit]
|
||||
defaults:
|
||||
run:
|
||||
shell: msys2 {0}
|
||||
@@ -169,4 +210,4 @@ jobs:
|
||||
- uses: actions-rs/clippy-check@v1
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
args: --features "${{ matrix.lua }},vendored,async,send,serialize
|
||||
args: --features "${{ matrix.lua }},vendored,async,send,serialize,macros"
|
||||
|
||||
@@ -1,7 +1,87 @@
|
||||
## v0.6.1
|
||||
|
||||
- Update `chunk!` documentation (stable Rust limitations)
|
||||
- Fixed Lua sequence table conversion to HashSet/BTreeSet
|
||||
- `once_cell` dependency lowered to 1.0
|
||||
|
||||
## v0.6.0
|
||||
Changes since 0.5.4
|
||||
- New `UserDataFields` API
|
||||
- Full access to `UserData` metatables with support of setting arbitrary fields.
|
||||
- Implement `UserData` for `Rc<RefCell<T>>`/`Arc<Mutex<T>>`/`Arc<RwLock<T>>` where `T: UserData`.
|
||||
- Added `SerializeOptions` to to change default Lua serializer behaviour (eg. `nil/null/array` serialization)
|
||||
- Added `LuaOptions` to customize Lua/Rust behaviour (currently panic handling)
|
||||
- Added `ToLua`/`FromLua` implementation for `Box<str>` and `Box<[T]>`.
|
||||
- Added `Thread::reset()` for luajit/lua54 to recycle threads (coroutines) with attaching a new function.
|
||||
- Added `chunk!` macro support to load chunks of Lua code using the Rust tokenizer and optionally capturing Rust variables.
|
||||
- Improved errors reporting (`Error`'s `__tostring` method formats full stacktraces). This is useful in a module mode.
|
||||
- Added `String::to_string_lossy`
|
||||
- Various bugfixes and improvements
|
||||
|
||||
Breaking changes:
|
||||
- Errors are always `Send + Sync` to be compatible with the anyhow crate.
|
||||
- Removed `Result` from `LuaSerdeExt::null()` and `LuaSerdeExt::array_metatable()` (never fails)
|
||||
- Removed `Result` from `Function::dump()` (never fails)
|
||||
- Removed `AnyUserData::has_metamethod()` (in favour of full access to metatables)
|
||||
|
||||
## v0.6.0-beta.3
|
||||
|
||||
- Errors are always `Send + Sync` to be compatible with anyhow crate
|
||||
- Implement `UserData` for `Rc<RefCell>`/`Arc<Mutex>`/`Arc<RwLock>`
|
||||
- Added `__ipairs` metamethod for Lua 5.2
|
||||
- Added `String::to_string_lossy`
|
||||
- Various bugfixes and improvements
|
||||
|
||||
## v0.6.0-beta.2
|
||||
|
||||
- [**Breaking**] Removed `AnyUserData::has_metamethod()`
|
||||
- Added `Thread::reset()` for luajit/lua54 to recycle threads.
|
||||
It's possible to attach a new function to a thread (coroutine).
|
||||
- Added `chunk!` macro support to load chunks of Lua code using the Rust tokenizer and optinally capturing Rust variables.
|
||||
- Improved error reporting (`Error`'s `__tostring` method formats full stacktraces). This is useful in the module mode.
|
||||
|
||||
## v0.6.0-beta.1
|
||||
|
||||
- New `UserDataFields` API
|
||||
- Allow to define arbitrary MetaMethods
|
||||
- `MetaMethods::name()` is public
|
||||
- Do not trigger longjmp in Rust to prevent unwinding across FFI boundaries. See https://github.com/rust-lang/rust/issues/83541
|
||||
- Added `SerializeOptions` to to change default Lua serializer behaviour (eg. nil/null/array serialization)
|
||||
- [**Breaking**] Removed `Result` from `LuaSerdeExt::null()` and `LuaSerdeExt::array_metatable()` (never fails)
|
||||
- [**Breaking**] Removed `Result` from `Function::dump()` (never fails)
|
||||
- `ToLua`/`FromLua` implementation for `Box<str>` and `Box<[T]>`
|
||||
- [**Breaking**] Added `LuaOptions` to customize Lua/Rust behaviour (currently panic handling)
|
||||
- Various bugfixes and performance improvements
|
||||
|
||||
## 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
|
||||
|
||||
|
||||
+27
-16
@@ -1,12 +1,12 @@
|
||||
[package]
|
||||
name = "mlua"
|
||||
version = "0.5.0" # remember to update html_root_url and mlua_derive
|
||||
version = "0.6.1" # 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"
|
||||
@@ -17,7 +17,7 @@ with async/await features and support of writing native lua modules in Rust.
|
||||
"""
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
features = ["lua53", "async", "send", "serialize"]
|
||||
features = ["lua53", "async", "send", "serialize", "macros"]
|
||||
rustdoc-args = ["--cfg", "docsrs"]
|
||||
|
||||
[workspace]
|
||||
@@ -38,11 +38,12 @@ module = ["mlua_derive"]
|
||||
async = ["futures-core", "futures-task", "futures-util"]
|
||||
send = []
|
||||
serialize = ["serde", "erased-serde"]
|
||||
macros = ["mlua_derive/macros"]
|
||||
|
||||
[dependencies]
|
||||
mlua_derive = { version = "0.5", optional = true, path = "mlua_derive" }
|
||||
mlua_derive = { version = "=0.6.0", optional = true, path = "mlua_derive" }
|
||||
bstr = { version = "0.2", features = ["std"], default_features = false }
|
||||
lazy_static = { version = "1.4" }
|
||||
once_cell = { version = "1.0" }
|
||||
num-traits = { version = "0.2.14" }
|
||||
futures-core = { version = "0.3.5", optional = true }
|
||||
futures-task = { version = "0.3.5", optional = true }
|
||||
@@ -54,30 +55,32 @@ erased-serde = { version = "0.3", optional = true }
|
||||
cc = { version = "1.0" }
|
||||
pkg-config = { version = "0.3.17" }
|
||||
lua-src = { version = ">= 540.0.0, < 550.0.0", optional = true }
|
||||
luajit-src = { version = ">= 210.1.0, < 220.0.0", optional = true }
|
||||
luajit-src = { version = ">= 210.1.2, < 220.0.0", optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
rustyline = "7.0"
|
||||
criterion = "0.3"
|
||||
rustyline = "8.0"
|
||||
criterion = { version = "0.3.4", features = ["html_reports", "async_tokio"] }
|
||||
trybuild = "1.0"
|
||||
futures = "0.3.5"
|
||||
hyper = "0.13"
|
||||
reqwest = { version = "0.10", features = ["json"] }
|
||||
tokio = { version = "0.2", features = ["full"] }
|
||||
hyper = { version = "0.14", features = ["client", "server"] }
|
||||
reqwest = { version = "0.11", features = ["json"] }
|
||||
tokio = { version = "1.0", features = ["full"] }
|
||||
futures-timer = "3.0"
|
||||
serde_json = "1.0"
|
||||
maplit = "1.0"
|
||||
|
||||
[[bench]]
|
||||
name = "benchmark"
|
||||
harness = false
|
||||
|
||||
[[example]]
|
||||
name = "async_http_client"
|
||||
required-features = ["async"]
|
||||
|
||||
[[example]]
|
||||
name = "async_http_client"
|
||||
required-features = ["async", "macros"]
|
||||
|
||||
[[example]]
|
||||
name = "async_http_reqwest"
|
||||
required-features = ["async", "serialize"]
|
||||
required-features = ["async", "serialize", "macros"]
|
||||
|
||||
[[example]]
|
||||
name = "async_http_server"
|
||||
@@ -85,8 +88,16 @@ required-features = ["async", "send"]
|
||||
|
||||
[[example]]
|
||||
name = "async_tcp_server"
|
||||
required-features = ["async"]
|
||||
required-features = ["async", "macros"]
|
||||
|
||||
[[example]]
|
||||
name = "guided_tour"
|
||||
required-features = ["macros"]
|
||||
|
||||
[[example]]
|
||||
name = "serialize"
|
||||
required-features = ["serialize"]
|
||||
|
||||
[[example]]
|
||||
name = "userdata"
|
||||
required-features = ["macros"]
|
||||
|
||||
@@ -3,7 +3,7 @@ below:
|
||||
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2019 A. Orlenko
|
||||
Copyright (c) 2019-2021 A. Orlenko
|
||||
Copyright (c) 2017 rlua
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# mlua
|
||||
[![Build Status]][github-actions] [![Latest Version]][crates.io] [![API Documentation]][docs.rs]
|
||||
[![Build Status]][github-actions] [![Latest Version]][crates.io] [![API Documentation]][docs.rs] [![Coverage Status]][codecov.io]
|
||||
|
||||
[Build Status]: https://github.com/khvzak/mlua/workflows/CI/badge.svg
|
||||
[github-actions]: https://github.com/khvzak/mlua/actions
|
||||
@@ -7,29 +7,54 @@
|
||||
[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 targets 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 dependencies, 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 using [serde] framework
|
||||
* `macros`: enable procedural macros (such as `chunk!`)
|
||||
|
||||
[5.4]: https://www.lua.org/manual/5.4/manual.html
|
||||
[5.3]: https://www.lua.org/manual/5.3/manual.html
|
||||
[5.2]: https://www.lua.org/manual/5.2/manual.html
|
||||
[5.1]: https://www.lua.org/manual/5.1/manual.html
|
||||
[LuaJIT]: https://luajit.org/
|
||||
[lua-src]: https://github.com/khvzak/lua-src-rs
|
||||
[luajit-src]: https://github.com/khvzak/luajit-src-rs
|
||||
[tokio]: https://github.com/tokio-rs/tokio
|
||||
[async-std]: https://github.com/async-rs/async-std
|
||||
[`Send`]: https://doc.rust-lang.org/std/marker/trait.Send.html
|
||||
[serde]: https://github.com/serde-rs/serde
|
||||
|
||||
### Async/await support
|
||||
|
||||
Starting from v0.3, `mlua` supports async/await for all Lua versions. This works using Lua [coroutines](https://www.lua.org/manual/5.3/manual.html#2.6) and require running [Thread](https://docs.rs/mlua/latest/mlua/struct.Thread.html) along with enabling `feature = "async"` in `Cargo.toml`.
|
||||
`mlua` supports async/await for all Lua versions. This works using Lua [coroutines](https://www.lua.org/manual/5.3/manual.html#2.6) and require running [Thread](https://docs.rs/mlua/latest/mlua/struct.Thread.html) along with enabling `feature = "async"` in `Cargo.toml`.
|
||||
|
||||
**Examples**:
|
||||
- [HTTP Client](examples/async_http_client.rs)
|
||||
@@ -39,7 +64,7 @@ Starting from v0.3, `mlua` supports async/await for all Lua versions. This works
|
||||
|
||||
### Serialization (serde) support
|
||||
|
||||
With `serialize` feature flag enabled, `mlua` allows you to serialize/deserialize any type that implements [`serde::Serialize`] and [`serde::Deserialize`] into/from [`mlua::Value`]. In addition `mlua` provides [`serde::Serialize`] trait implementation for it (including user data support).
|
||||
With `serialize` feature flag enabled, `mlua` allows you to serialize/deserialize any type that implements [`serde::Serialize`] and [`serde::Deserialize`] into/from [`mlua::Value`]. In addition `mlua` provides [`serde::Serialize`] trait implementation for it (including `UserData` support).
|
||||
|
||||
[Example](examples/serialize.rs)
|
||||
|
||||
@@ -49,7 +74,7 @@ With `serialize` feature flag enabled, `mlua` allows you to serialize/deserializ
|
||||
|
||||
### Compiling
|
||||
|
||||
You have to enable one of the features `lua54`, `lua53`, `lua52`, `lua51` or `luajit`, according to the choosen Lua version.
|
||||
You have to enable one of the features `lua54`, `lua53`, `lua52`, `lua51` or `luajit`, according to the chosen Lua version.
|
||||
|
||||
By default `mlua` uses `pkg-config` tool to find lua includes and libraries for the chosen Lua version.
|
||||
In most cases it works as desired, although sometimes could be more preferable to use a custom lua library.
|
||||
@@ -61,16 +86,18 @@ An example how to use them:
|
||||
my_project $ LUA_INC=$HOME/tmp/lua-5.2.4/src LUA_LIB=$HOME/tmp/lua-5.2.4/src LUA_LIB_NAME=lua LUA_LINK=static cargo build
|
||||
```
|
||||
|
||||
`mlua` also supports vendored lua/luajit using the auxilary crates [lua-src](https://crates.io/crates/lua-src) and
|
||||
`mlua` also supports vendored lua/luajit using the auxiliary crates [lua-src](https://crates.io/crates/lua-src) and
|
||||
[luajit-src](https://crates.io/crates/luajit-src).
|
||||
Just enable the `vendored` feature and cargo will automatically build and link specified lua/luajit version. This is the easiest way to get started with `mlua`.
|
||||
|
||||
### Standalone mode
|
||||
In a standalone mode `mlua` allows to add to your application scripting support with a gently configured Lua runtime to ensure safety and soundness.
|
||||
|
||||
Add to `Cargo.toml` :
|
||||
|
||||
``` toml
|
||||
[dependencies]
|
||||
mlua = { version = "0.5", features = ["lua53"] }
|
||||
mlua = { version = "0.6", features = ["lua53", "vendored"] }
|
||||
```
|
||||
|
||||
`main.rs`
|
||||
@@ -94,6 +121,7 @@ fn main() -> LuaResult<()> {
|
||||
```
|
||||
|
||||
### Module mode
|
||||
In a module mode `mlua` allows to create a compiled Lua module that can be loaded from Lua code using [`require`](https://www.lua.org/manual/5.3/manual.html#pdf-require). In this case `mlua` uses an external Lua runtime which could lead to potential unsafety due to unpredictability of the Lua environment and usage of libraries such as [`debug`](https://www.lua.org/manual/5.3/manual.html#6.10).
|
||||
|
||||
[Example](examples/module)
|
||||
|
||||
@@ -104,7 +132,7 @@ Add to `Cargo.toml` :
|
||||
crate-type = ["cdylib"]
|
||||
|
||||
[dependencies]
|
||||
mlua = { version = "0.5", features = ["lua53", "module"] }
|
||||
mlua = { version = "0.6", features = ["lua53", "vendored", "module"] }
|
||||
```
|
||||
|
||||
`lib.rs` :
|
||||
@@ -141,16 +169,31 @@ 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.
|
||||
|
||||
More details about compiling and linking Lua modules can be found on the [Building Modules](http://lua-users.org/wiki/BuildingModules) page.
|
||||
|
||||
### Publishing to luarocks.org
|
||||
|
||||
There is a LuaRocks build backend for mlua modules [`luarocks-build-rust-mlua`].
|
||||
|
||||
Modules written in Rust and published to luarocks:
|
||||
- [`lua-ryaml`](https://github.com/khvzak/lua-ryaml)
|
||||
|
||||
[`luarocks-build-rust-mlua`]: https://luarocks.org/modules/khvzak/luarocks-build-rust-mlua
|
||||
|
||||
## Safety
|
||||
|
||||
One of the `mlua` goals is to provide *safe* API between Rust and Lua.
|
||||
@@ -165,7 +208,7 @@ It is surprisingly, fiendishly difficult to use the Lua C API without the potent
|
||||
## Panic handling
|
||||
|
||||
`mlua` wraps panics that are generated inside Rust callbacks in a regular Lua error. Panics could be
|
||||
resumed then by propagating the Lua error to Rust code.
|
||||
resumed then by returning or propagating the Lua error to Rust code.
|
||||
|
||||
For example:
|
||||
``` rust
|
||||
@@ -184,6 +227,10 @@ let _ = lua.load(r#"
|
||||
unreachable!()
|
||||
```
|
||||
|
||||
Optionally `mlua` can disable Rust panics catching in Lua via `pcall`/`xpcall` and automatically resume
|
||||
them across the Lua API boundary. This is controlled via `LuaOptions` and done by wrapping the Lua `pcall`/`xpcall`
|
||||
functions on a way to prevent catching errors that are wrapped Rust panics.
|
||||
|
||||
`mlua` should also be panic safe in another way as well, which is that any `Lua` instances or handles
|
||||
remains usable after a user generated panic, and such panics should not break internal invariants or
|
||||
leak Lua stack space. This is mostly important to safely use `mlua` types in Drop impls, as you should not be
|
||||
@@ -192,15 +239,14 @@ using panics for general error handling.
|
||||
Below is a list of `mlua` behaviors that should be considered a bug.
|
||||
If you encounter them, a bug report would be very welcome:
|
||||
|
||||
+ If your program panics with a message that contains the string "mlua internal error", this is a bug.
|
||||
+ If you can cause UB with `mlua` without typing the word "unsafe", this is a bug.
|
||||
|
||||
+ The above is true even for the internal panic about running out of stack space! There are a few ways to generate normal script errors by running out of stack, but if you encounter a *panic* based on running out of stack, this is a bug.
|
||||
+ If your program panics with a message that contains the string "mlua internal error", this is a bug.
|
||||
|
||||
+ Lua C API errors are handled by lonjmp. All instances where the Lua C API would otherwise longjmp over calling stack frames should be guarded against, except in internal callbacks where this is intentional. If you detect that `mlua` is triggering a longjmp over your Rust stack frames, this is a bug!
|
||||
+ Lua C API errors are handled by longjmp. All instances where the Lua C API would otherwise longjmp over calling stack frames should be guarded against, except in internal callbacks where this is intentional. If you detect that `mlua` is triggering a longjmp over your Rust stack frames, this is a bug!
|
||||
|
||||
+ If you detect that, after catching a panic or during a Drop triggered from a panic, a `Lua` or handle method is triggering other bugs or there is a Lua stack space leak, this is a bug. `mlua` instances are supposed to remain fully usable in the face of user generated panics. This guarantee does not extend to panics marked with "mlua internal error" simply because that is already indicative of a separate bug.
|
||||
|
||||
## License
|
||||
|
||||
This project is licensed under the [MIT license](LICENSE)
|
||||
|
||||
|
||||
+171
-111
@@ -1,23 +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",
|
||||
allow(unused_attributes)
|
||||
)]
|
||||
extern "system" {}
|
||||
|
||||
use criterion::{criterion_group, criterion_main, BatchSize, Criterion};
|
||||
use std::time::Duration;
|
||||
use tokio::runtime::Runtime;
|
||||
use tokio::task;
|
||||
|
||||
use mlua::prelude::*;
|
||||
|
||||
fn collect_gc_twice(lua: &Lua) {
|
||||
lua.gc_collect().unwrap();
|
||||
lua.gc_collect().unwrap();
|
||||
}
|
||||
|
||||
fn create_table(c: &mut Criterion) {
|
||||
c.bench_function("create table", |b| {
|
||||
b.iter_batched_ref(
|
||||
|| Lua::new(),
|
||||
|lua| {
|
||||
let lua = Lua::new();
|
||||
|
||||
c.bench_function("create [table empty]", |b| {
|
||||
b.iter_batched(
|
||||
|| collect_gc_twice(&lua),
|
||||
|_| {
|
||||
lua.create_table().unwrap();
|
||||
},
|
||||
BatchSize::SmallInput,
|
||||
@@ -26,12 +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();
|
||||
}
|
||||
},
|
||||
@@ -41,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();
|
||||
@@ -56,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,
|
||||
@@ -87,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,
|
||||
);
|
||||
@@ -160,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();
|
||||
}
|
||||
@@ -178,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();
|
||||
@@ -192,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.is_empty() {
|
||||
if need_lua_lib {
|
||||
if lib_dir.is_empty() {
|
||||
panic!("LUA_LIB is not set");
|
||||
}
|
||||
if lua_lib.is_empty() {
|
||||
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");
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
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};
|
||||
use mlua::{chunk, ExternalResult, Lua, Result, UserData, UserDataMethods};
|
||||
|
||||
#[derive(Clone)]
|
||||
struct BodyReader(Arc<Mutex<HyperBody>>);
|
||||
@@ -18,11 +18,11 @@ impl BodyReader {
|
||||
|
||||
impl UserData for BodyReader {
|
||||
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
|
||||
methods.add_async_method("read", |_, reader, ()| async move {
|
||||
methods.add_async_method("read", |lua, 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 {
|
||||
return Ok(Some(BString::from(bytes.as_ref())));
|
||||
if let Some(bytes) = reader.data().await {
|
||||
let bytes = bytes.to_lua_err()?;
|
||||
return Some(lua.create_string(&bytes)).transpose();
|
||||
}
|
||||
Ok(None)
|
||||
});
|
||||
@@ -35,18 +35,18 @@ async fn main() -> Result<()> {
|
||||
|
||||
let fetch_url = lua.create_async_function(|lua, uri: String| async move {
|
||||
let client = HyperClient::new();
|
||||
let uri = uri.parse().map_err(Error::external)?;
|
||||
let resp = client.get(uri).await.map_err(Error::external)?;
|
||||
let uri = uri.parse().to_lua_err()?;
|
||||
let resp = client.get(uri).await.to_lua_err()?;
|
||||
|
||||
let lua_resp = lua.create_table()?;
|
||||
lua_resp.set("status", resp.status().as_u16())?;
|
||||
|
||||
let mut headers = HashMap::new();
|
||||
for (key, value) in resp.headers().iter() {
|
||||
for (key, value) in resp.headers() {
|
||||
headers
|
||||
.entry(key.as_str())
|
||||
.or_insert(Vec::new())
|
||||
.push(value.to_str().unwrap());
|
||||
.push(value.to_str().to_lua_err()?);
|
||||
}
|
||||
|
||||
lua_resp.set("headers", headers)?;
|
||||
@@ -55,13 +55,9 @@ async fn main() -> Result<()> {
|
||||
Ok(lua_resp)
|
||||
})?;
|
||||
|
||||
let globals = lua.globals();
|
||||
globals.set("fetch_url", fetch_url)?;
|
||||
|
||||
let f = lua
|
||||
.load(
|
||||
r#"
|
||||
local res = fetch_url(...)
|
||||
.load(chunk! {
|
||||
local res = $fetch_url(...)
|
||||
print(res.status)
|
||||
for key, vals in pairs(res.headers) do
|
||||
for _, val in ipairs(vals) do
|
||||
@@ -74,8 +70,7 @@ async fn main() -> Result<()> {
|
||||
print(body)
|
||||
end
|
||||
until not body
|
||||
"#,
|
||||
)
|
||||
})
|
||||
.into_function()?;
|
||||
|
||||
f.call_async("http://httpbin.org/ip").await
|
||||
|
||||
@@ -1,40 +1,34 @@
|
||||
use mlua::{Error, Lua, LuaSerdeExt, Result};
|
||||
use mlua::{chunk, ExternalResult, Lua, LuaSerdeExt, Result};
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
let globals = lua.globals();
|
||||
globals.set("null", lua.null()?)?;
|
||||
|
||||
let 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)?;
|
||||
.to_lua_err()?;
|
||||
let json = resp.json::<serde_json::Value>().await.to_lua_err()?;
|
||||
lua.to_value(&json)
|
||||
})?;
|
||||
globals.set("fetch_json", fetch_json)?;
|
||||
|
||||
let f = lua
|
||||
.load(
|
||||
r#"
|
||||
.load(chunk! {
|
||||
function print_r(t, indent)
|
||||
local indent = indent or ''
|
||||
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
|
||||
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(...)
|
||||
local res = $fetch_json(...)
|
||||
print_r(res)
|
||||
"#,
|
||||
)
|
||||
})
|
||||
.into_function()?;
|
||||
|
||||
f.call_async("https://httpbin.org/anything?arg0=val0").await
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
use std::net::SocketAddr;
|
||||
use std::sync::Arc;
|
||||
|
||||
use hyper::server::conn::AddrStream;
|
||||
use hyper::service::{make_service_fn, service_fn};
|
||||
@@ -7,13 +6,12 @@ use hyper::{Body, Request, Response, Server};
|
||||
|
||||
use mlua::{Error, Function, Lua, Result, Table, UserData, UserDataMethods};
|
||||
|
||||
#[derive(Clone)]
|
||||
struct LuaRequest(Arc<(SocketAddr, Request<Body>)>);
|
||||
struct LuaRequest(SocketAddr, Request<Body>);
|
||||
|
||||
impl UserData for LuaRequest {
|
||||
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
|
||||
methods.add_method("remote_addr", |_lua, req, ()| Ok((req.0).0.to_string()));
|
||||
methods.add_method("method", |_lua, req, ()| Ok((req.0).1.method().to_string()));
|
||||
methods.add_method("remote_addr", |_lua, req, ()| Ok((req.0).to_string()));
|
||||
methods.add_method("method", |_lua, req, ()| Ok((req.1).method().to_string()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,7 +23,7 @@ async fn run_server(handler: Function<'static>) -> Result<()> {
|
||||
Ok::<_, Error>(service_fn(move |req: Request<Body>| {
|
||||
let handler = handler.clone();
|
||||
async move {
|
||||
let lua_req = LuaRequest(Arc::new((remote_addr, req)));
|
||||
let lua_req = LuaRequest(remote_addr, req);
|
||||
let lua_resp: Table = handler.call_async(lua_req).await?;
|
||||
let body = lua_resp
|
||||
.get::<_, Option<String>>("body")?
|
||||
@@ -72,7 +70,7 @@ async fn main() -> Result<()> {
|
||||
["X-Req-Method"] = req:method(),
|
||||
["X-Remote-Addr"] = req:remote_addr(),
|
||||
},
|
||||
body = "Hello, World!"
|
||||
body = "Hello, World!\n"
|
||||
}
|
||||
end
|
||||
"#,
|
||||
|
||||
@@ -1,15 +1,12 @@
|
||||
use std::net::Shutdown;
|
||||
use std::sync::Arc;
|
||||
|
||||
use bstr::BString;
|
||||
use tokio::io::{AsyncReadExt, AsyncWriteExt};
|
||||
use tokio::net::{TcpListener, TcpStream};
|
||||
use tokio::sync::Mutex;
|
||||
use tokio::task;
|
||||
|
||||
use mlua::{Function, Lua, Result, UserData, UserDataMethods};
|
||||
use mlua::{chunk, Function, Lua, Result, String as LuaString, UserData, UserDataMethods};
|
||||
|
||||
#[derive(Clone)]
|
||||
struct LuaTcp;
|
||||
|
||||
#[derive(Clone)]
|
||||
@@ -42,20 +39,20 @@ impl UserData for LuaTcpStream {
|
||||
Ok(stream.0.lock().await.peer_addr()?.to_string())
|
||||
});
|
||||
|
||||
methods.add_async_method("read", |_, stream, size: usize| async move {
|
||||
methods.add_async_method("read", |lua, stream, size: usize| async move {
|
||||
let mut buf = vec![0; size];
|
||||
let n = stream.0.lock().await.read(&mut buf).await?;
|
||||
buf.truncate(n);
|
||||
Ok(BString::from(buf))
|
||||
lua.create_string(&buf)
|
||||
});
|
||||
|
||||
methods.add_async_method("write", |_, stream, data: BString| async move {
|
||||
let n = stream.0.lock().await.write(&data).await?;
|
||||
methods.add_async_method("write", |_, stream, data: LuaString| async move {
|
||||
let n = stream.0.lock().await.write(&data.as_bytes()).await?;
|
||||
Ok(n)
|
||||
});
|
||||
|
||||
methods.add_async_method("close", |_, stream, ()| async move {
|
||||
stream.0.lock().await.shutdown(Shutdown::Both)?;
|
||||
stream.0.lock().await.shutdown().await?;
|
||||
Ok(())
|
||||
});
|
||||
}
|
||||
@@ -67,15 +64,12 @@ async fn run_server(lua: &'static Lua) -> Result<()> {
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
let globals = lua.globals();
|
||||
globals.set("tcp", LuaTcp)?;
|
||||
globals.set("spawn", spawn)?;
|
||||
let tcp = LuaTcp;
|
||||
|
||||
let server = lua
|
||||
.load(
|
||||
r#"
|
||||
.load(chunk! {
|
||||
local addr = ...
|
||||
local listener = tcp.bind(addr)
|
||||
local listener = $tcp.bind(addr)
|
||||
print("listening on "..addr)
|
||||
|
||||
local accept_new = true
|
||||
@@ -88,7 +82,7 @@ async fn run_server(lua: &'static Lua) -> Result<()> {
|
||||
return
|
||||
end
|
||||
|
||||
spawn(function()
|
||||
$spawn(function()
|
||||
while true do
|
||||
local data = stream:read(100)
|
||||
data = data:match("^%s*(.-)%s*$") -- trim
|
||||
@@ -107,8 +101,7 @@ async fn run_server(lua: &'static Lua) -> Result<()> {
|
||||
end
|
||||
end)
|
||||
end
|
||||
"#,
|
||||
)
|
||||
})
|
||||
.into_function()?;
|
||||
|
||||
task::LocalSet::new()
|
||||
|
||||
+24
-13
@@ -1,15 +1,15 @@
|
||||
use std::f32;
|
||||
use std::iter::FromIterator;
|
||||
|
||||
use mlua::{Function, Lua, MetaMethod, Result, UserData, UserDataMethods, Variadic};
|
||||
use mlua::{chunk, Function, Lua, MetaMethod, Result, UserData, UserDataMethods, Variadic};
|
||||
|
||||
fn main() -> Result<()> {
|
||||
// You can create a new Lua state with `Lua::new()`. This loads the default Lua std library
|
||||
// You can create a new Lua state with `Lua::new()`. This loads the default Lua std library
|
||||
// *without* the debug library.
|
||||
let lua = Lua::new();
|
||||
|
||||
// You can get and set global variables. Notice that the globals table here is a permanent
|
||||
// reference to _G, and it is mutated behind the scenes as Lua code is loaded. This API is
|
||||
// You can get and set global variables. Notice that the globals table here is a permanent
|
||||
// reference to _G, and it is mutated behind the scenes as Lua code is loaded. This API is
|
||||
// based heavily around sharing and internal mutation (just like Lua itself).
|
||||
|
||||
let globals = lua.globals();
|
||||
@@ -20,8 +20,8 @@ fn main() -> Result<()> {
|
||||
assert_eq!(globals.get::<_, String>("string_var")?, "hello");
|
||||
assert_eq!(globals.get::<_, i64>("int_var")?, 42);
|
||||
|
||||
// You can load and evaluate Lua code. The returned type of `Lua::load` is a builder
|
||||
// that allows you to change settings before running Lua code. Here, we are using it to set
|
||||
// You can load and evaluate Lua code. The returned type of `Lua::load` is a builder
|
||||
// that allows you to change settings before running Lua code. Here, we are using it to set
|
||||
// the name of the laoded chunk to "example code", which will be used when Lua error
|
||||
// messages are printed.
|
||||
|
||||
@@ -38,6 +38,17 @@ fn main() -> Result<()> {
|
||||
assert_eq!(lua.load("false == false").eval::<bool>()?, true);
|
||||
assert_eq!(lua.load("return 1 + 2").eval::<i32>()?, 3);
|
||||
|
||||
// Use can use special `chunk!` macro to use Rust tokenizer and automatically capture variables
|
||||
|
||||
let a = 1;
|
||||
let b = 2;
|
||||
let name = "world";
|
||||
lua.load(chunk! {
|
||||
print($a + $b)
|
||||
print("hello, " .. $name)
|
||||
})
|
||||
.exec()?;
|
||||
|
||||
// You can create and manage Lua tables
|
||||
|
||||
let array_table = lua.create_table()?;
|
||||
@@ -76,7 +87,7 @@ fn main() -> Result<()> {
|
||||
let print: Function = globals.get("print")?;
|
||||
print.call::<_, ()>("hello from rust")?;
|
||||
|
||||
// This API generally handles variadics using tuples. This is one way to call a function with
|
||||
// This API generally handles variadics using tuples. This is one way to call a function with
|
||||
// multiple parameters:
|
||||
|
||||
print.call::<_, ()>(("hello", "again", "from", "rust"))?;
|
||||
@@ -87,15 +98,15 @@ fn main() -> Result<()> {
|
||||
["hello", "yet", "again", "from", "rust"].iter().cloned(),
|
||||
))?;
|
||||
|
||||
// You can bind rust functions to Lua as well. Callbacks receive the Lua state inself as their
|
||||
// first parameter, and the arguments given to the function as the second parameter. The type
|
||||
// You can bind rust functions to Lua as well. Callbacks receive the Lua state inself as their
|
||||
// first parameter, and the arguments given to the function as the second parameter. The type
|
||||
// of the arguments can be anything that is convertible from the parameters given by Lua, in
|
||||
// this case, the function expects two string sequences.
|
||||
|
||||
let check_equal = lua.create_function(|_, (list1, list2): (Vec<String>, Vec<String>)| {
|
||||
// This function just checks whether two string lists are equal, and in an inefficient way.
|
||||
// Lua callbacks return `mlua::Result`, an Ok value is a normal return, and an Err return
|
||||
// turns into a Lua 'error'. Again, any type that is convertible to Lua may be returned.
|
||||
// turns into a Lua 'error'. Again, any type that is convertible to Lua may be returned.
|
||||
Ok(list1 == list2)
|
||||
})?;
|
||||
globals.set("check_equal", check_equal)?;
|
||||
@@ -174,7 +185,7 @@ fn main() -> Result<()> {
|
||||
|
||||
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
|
||||
// `rust_val`. Outside of a `Lua::scope` call, this would not be allowed
|
||||
// because it could be unsafe.
|
||||
|
||||
lua.globals().set(
|
||||
@@ -191,9 +202,9 @@ fn main() -> Result<()> {
|
||||
assert_eq!(rust_val, 42);
|
||||
}
|
||||
|
||||
// We were able to run our 'sketchy' function inside the scope just fine. However, if we
|
||||
// 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
|
||||
// been invalidated and we will generate an error. If our function wasn't invalidated, we
|
||||
// might be able to improperly access the freed `rust_val` which would be unsafe.
|
||||
assert!(lua.load("sketchy()").exec().is_err());
|
||||
|
||||
|
||||
@@ -8,10 +8,32 @@ fn used_memory(lua: &Lua, _: ()) -> LuaResult<usize> {
|
||||
Ok(lua.used_memory())
|
||||
}
|
||||
|
||||
fn check_userdata(_: &Lua, ud: MyUserData) -> LuaResult<i32> {
|
||||
Ok(ud.0)
|
||||
}
|
||||
|
||||
#[mlua::lua_module]
|
||||
fn rust_module(lua: &Lua) -> LuaResult<LuaTable> {
|
||||
let exports = lua.create_table()?;
|
||||
exports.set("sum", lua.create_function(sum)?)?;
|
||||
exports.set("used_memory", lua.create_function(used_memory)?)?;
|
||||
exports.set("check_userdata", lua.create_function(check_userdata)?)?;
|
||||
Ok(exports)
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
struct MyUserData(i32);
|
||||
|
||||
impl LuaUserData for MyUserData {}
|
||||
|
||||
#[mlua::lua_module]
|
||||
fn rust_module_second(lua: &Lua) -> LuaResult<LuaTable> {
|
||||
let exports = lua.create_table()?;
|
||||
exports.set("userdata", lua.create_userdata(MyUserData(123))?)?;
|
||||
Ok(exports)
|
||||
}
|
||||
|
||||
#[mlua::lua_module]
|
||||
fn rust_module_error(_: &Lua) -> LuaResult<LuaTable> {
|
||||
Err("custom module error".to_lua_err())
|
||||
}
|
||||
|
||||
@@ -33,8 +33,8 @@ fn main() -> Result<()> {
|
||||
"#).eval()?)?;
|
||||
|
||||
// Set it as (serializable) userdata
|
||||
globals.set("null", lua.null()?)?;
|
||||
globals.set("array_mt", lua.array_metatable()?)?;
|
||||
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
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
use mlua::{chunk, Lua, MetaMethod, Result, UserData};
|
||||
|
||||
#[derive(Default)]
|
||||
struct Rectangle {
|
||||
length: u32,
|
||||
width: u32,
|
||||
}
|
||||
|
||||
impl UserData for Rectangle {
|
||||
fn add_fields<'lua, F: mlua::UserDataFields<'lua, Self>>(fields: &mut F) {
|
||||
fields.add_field_method_get("length", |_, this| Ok(this.length));
|
||||
fields.add_field_method_set("length", |_, this, val| {
|
||||
this.length = val;
|
||||
Ok(())
|
||||
});
|
||||
fields.add_field_method_get("width", |_, this| Ok(this.width));
|
||||
fields.add_field_method_set("width", |_, this, val| {
|
||||
this.width = val;
|
||||
Ok(())
|
||||
});
|
||||
}
|
||||
|
||||
fn add_methods<'lua, M: mlua::UserDataMethods<'lua, Self>>(methods: &mut M) {
|
||||
methods.add_method("area", |_, this, ()| Ok(this.length * this.width));
|
||||
methods.add_method("diagonal", |_, this, ()| {
|
||||
Ok((this.length.pow(2) as f64 + this.width.pow(2) as f64).sqrt())
|
||||
});
|
||||
|
||||
// Constructor
|
||||
methods.add_meta_function(MetaMethod::Call, |_, ()| Ok(Rectangle::default()));
|
||||
}
|
||||
}
|
||||
|
||||
fn main() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
let rectangle = Rectangle::default();
|
||||
lua.load(chunk! {
|
||||
local rect = $rectangle()
|
||||
rect.width = 10
|
||||
rect.length = 5
|
||||
assert(rect:area() == 50)
|
||||
assert(rect:diagonal() - 11.1803 < 0.0001)
|
||||
})
|
||||
.exec()
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "mlua_derive"
|
||||
version = "0.5.0"
|
||||
version = "0.6.0"
|
||||
authors = ["Aleksandr Orlenko <zxteam@pm.me>"]
|
||||
edition = "2018"
|
||||
description = "Procedural macros for the mlua crate."
|
||||
@@ -11,7 +11,14 @@ license = "MIT"
|
||||
[lib]
|
||||
proc-macro = true
|
||||
|
||||
[features]
|
||||
macros = ["proc-macro-error", "itertools", "regex", "once_cell"]
|
||||
|
||||
[dependencies]
|
||||
proc-macro2 = "1.0"
|
||||
quote = "1.0"
|
||||
proc-macro2 = { version = "1.0", features = ["span-locations"] }
|
||||
proc-macro-error = { version = "1.0", optional = true }
|
||||
syn = { version = "1.0", features = ["full"] }
|
||||
itertools = { version = "0.10", optional = true }
|
||||
regex = { version = "1.4", optional = true }
|
||||
once_cell = { version = "1.5", optional = true }
|
||||
|
||||
@@ -0,0 +1,105 @@
|
||||
use proc_macro::{TokenStream, TokenTree};
|
||||
|
||||
use crate::token::{Pos, Token, Tokens};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub(crate) struct Capture {
|
||||
key: Token,
|
||||
rust: TokenTree,
|
||||
}
|
||||
|
||||
impl Capture {
|
||||
fn new(key: Token, rust: TokenTree) -> Self {
|
||||
Self { key, rust }
|
||||
}
|
||||
|
||||
/// Token string inside `chunk!`
|
||||
pub(crate) fn key(&self) -> &Token {
|
||||
&self.key
|
||||
}
|
||||
|
||||
/// As rust variable, e.g. `x`
|
||||
pub(crate) fn as_rust(&self) -> &TokenTree {
|
||||
&self.rust
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct Captures(Vec<Capture>);
|
||||
|
||||
impl Captures {
|
||||
pub(crate) fn new() -> Self {
|
||||
Self(Vec::new())
|
||||
}
|
||||
|
||||
pub(crate) fn add(&mut self, token: &Token) -> Capture {
|
||||
let tt = token.tree();
|
||||
let key = token.clone();
|
||||
|
||||
match self.0.iter().find(|arg| arg.key() == &key) {
|
||||
Some(arg) => arg.clone(),
|
||||
None => {
|
||||
let arg = Capture::new(key, tt.clone());
|
||||
self.0.push(arg.clone());
|
||||
arg
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn captures(&self) -> &[Capture] {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct Chunk {
|
||||
source: String,
|
||||
caps: Captures,
|
||||
}
|
||||
|
||||
impl Chunk {
|
||||
pub(crate) fn new(tokens: TokenStream) -> Self {
|
||||
let tokens = Tokens::retokenize(tokens);
|
||||
|
||||
let mut source = String::new();
|
||||
let mut caps = Captures::new();
|
||||
|
||||
let mut pos: Option<Pos> = None;
|
||||
for t in tokens {
|
||||
if t.is_cap() {
|
||||
caps.add(&t);
|
||||
}
|
||||
|
||||
let (line, col) = (t.start().line, t.start().column);
|
||||
let (prev_line, prev_col) = pos
|
||||
.take()
|
||||
.map(|lc| (lc.line, lc.column))
|
||||
.unwrap_or_else(|| (line, col));
|
||||
|
||||
#[allow(clippy::comparison_chain)]
|
||||
if line > prev_line {
|
||||
source.push('\n');
|
||||
} else if line == prev_line {
|
||||
for _ in 0..col.saturating_sub(prev_col) {
|
||||
source.push(' ');
|
||||
}
|
||||
}
|
||||
source.push_str(&t.to_string());
|
||||
|
||||
pos = Some(t.end());
|
||||
}
|
||||
|
||||
Self {
|
||||
source: source.trim_end().to_string(),
|
||||
caps,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn source(&self) -> &str {
|
||||
&self.source
|
||||
}
|
||||
|
||||
pub(crate) fn captures(&self) -> &[Capture] {
|
||||
self.caps.captures()
|
||||
}
|
||||
}
|
||||
+100
-18
@@ -1,37 +1,119 @@
|
||||
extern crate proc_macro;
|
||||
|
||||
use proc_macro::TokenStream;
|
||||
use proc_macro2::{Ident, Span};
|
||||
use quote::quote_spanned;
|
||||
use syn::{parse_macro_input, spanned::Spanned, AttributeArgs, Error, ItemFn};
|
||||
use quote::quote;
|
||||
use syn::{parse_macro_input, AttributeArgs, Error, ItemFn};
|
||||
|
||||
#[cfg(feature = "macros")]
|
||||
use {
|
||||
crate::chunk::Chunk, proc_macro::TokenTree, proc_macro2::TokenStream as TokenStream2,
|
||||
proc_macro_error::proc_macro_error,
|
||||
};
|
||||
|
||||
#[proc_macro_attribute]
|
||||
pub fn lua_module(attr: TokenStream, item: TokenStream) -> TokenStream {
|
||||
let args = parse_macro_input!(attr as AttributeArgs);
|
||||
let item = parse_macro_input!(item as ItemFn);
|
||||
let func = parse_macro_input!(item as ItemFn);
|
||||
|
||||
if args.len() > 0 {
|
||||
let err = Error::new(Span::call_site(), "the number of arguments must be zero")
|
||||
if !args.is_empty() {
|
||||
let err = Error::new(Span::call_site(), "the macro does not support arguments")
|
||||
.to_compile_error();
|
||||
return err.into();
|
||||
}
|
||||
|
||||
let span = item.span();
|
||||
let item_name = item.sig.ident.clone();
|
||||
let ext_entrypoint_name = Ident::new(&format!("luaopen_{}", item.sig.ident), Span::call_site());
|
||||
let func_name = func.sig.ident.clone();
|
||||
let ext_entrypoint_name = Ident::new(&format!("luaopen_{}", func_name), Span::call_site());
|
||||
|
||||
let wrapped = quote_spanned! { span =>
|
||||
mlua::require_module_feature!();
|
||||
let wrapped = quote! {
|
||||
::mlua::require_module_feature!();
|
||||
|
||||
#func
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn #ext_entrypoint_name(state: *mut mlua::lua_State) -> std::os::raw::c_int {
|
||||
#item
|
||||
|
||||
mlua::Lua::init_from_ptr(state)
|
||||
.entrypoint1(#item_name)
|
||||
.unwrap()
|
||||
unsafe extern "C" fn #ext_entrypoint_name(state: *mut ::mlua::lua_State) -> ::std::os::raw::c_int {
|
||||
::mlua::Lua::init_from_ptr(state)
|
||||
.entrypoint1(#func_name)
|
||||
.expect("cannot initialize module")
|
||||
}
|
||||
};
|
||||
|
||||
wrapped.into()
|
||||
}
|
||||
|
||||
#[cfg(feature = "macros")]
|
||||
fn to_ident(tt: &TokenTree) -> TokenStream2 {
|
||||
let s: TokenStream = tt.clone().into();
|
||||
s.into()
|
||||
}
|
||||
|
||||
#[cfg(feature = "macros")]
|
||||
#[proc_macro]
|
||||
#[proc_macro_error]
|
||||
pub fn chunk(input: TokenStream) -> TokenStream {
|
||||
let chunk = Chunk::new(input);
|
||||
|
||||
let source = chunk.source();
|
||||
|
||||
let caps_len = chunk.captures().len();
|
||||
let caps = chunk.captures().iter().map(|cap| {
|
||||
let cap_name = cap.as_rust().to_string();
|
||||
let cap = to_ident(cap.as_rust());
|
||||
quote! { env.raw_set(#cap_name, #cap)?; }
|
||||
});
|
||||
|
||||
let wrapped_code = quote! {{
|
||||
use ::mlua::{AsChunk, ChunkMode, Lua, Result, Value};
|
||||
use ::std::marker::PhantomData;
|
||||
use ::std::sync::Mutex;
|
||||
|
||||
fn annotate<'a, F: FnOnce(&'a Lua) -> Result<Value<'a>>>(f: F) -> F { f }
|
||||
|
||||
struct InnerChunk<'a, F: FnOnce(&'a Lua) -> Result<Value<'a>>>(Mutex<Option<F>>, PhantomData<&'a ()>);
|
||||
|
||||
impl<'lua, F> AsChunk<'lua> for InnerChunk<'lua, F>
|
||||
where
|
||||
F: FnOnce(&'lua Lua) -> Result<Value<'lua>>,
|
||||
{
|
||||
fn source(&self) -> &[u8] {
|
||||
(#source).as_bytes()
|
||||
}
|
||||
|
||||
fn env(&self, lua: &'lua Lua) -> Result<Option<Value<'lua>>> {
|
||||
if #caps_len > 0 {
|
||||
if let Ok(mut make_env) = self.0.lock() {
|
||||
if let Some(make_env) = make_env.take() {
|
||||
return make_env(lua).map(Some);
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
fn mode(&self) -> Option<ChunkMode> {
|
||||
Some(ChunkMode::Text)
|
||||
}
|
||||
}
|
||||
|
||||
let make_env = annotate(move |lua: &Lua| -> Result<Value> {
|
||||
let globals = lua.globals();
|
||||
let env = lua.create_table()?;
|
||||
let meta = lua.create_table()?;
|
||||
meta.raw_set("__index", globals.clone())?;
|
||||
meta.raw_set("__newindex", globals)?;
|
||||
|
||||
// Add captured variables
|
||||
#(#caps)*
|
||||
|
||||
env.set_metatable(Some(meta));
|
||||
Ok(Value::Table(env))
|
||||
});
|
||||
|
||||
&InnerChunk(Mutex::new(Some(make_env)), PhantomData)
|
||||
}};
|
||||
|
||||
wrapped_code.into()
|
||||
}
|
||||
|
||||
#[cfg(feature = "macros")]
|
||||
mod chunk;
|
||||
#[cfg(feature = "macros")]
|
||||
mod token;
|
||||
|
||||
@@ -0,0 +1,234 @@
|
||||
use std::{
|
||||
cmp::{Eq, PartialEq},
|
||||
fmt::{self, Display, Formatter},
|
||||
iter::IntoIterator,
|
||||
vec::IntoIter,
|
||||
};
|
||||
|
||||
use itertools::Itertools;
|
||||
use once_cell::sync::Lazy;
|
||||
use proc_macro::{Delimiter, Span, TokenStream, TokenTree};
|
||||
use proc_macro2::Span as Span2;
|
||||
use regex::Regex;
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub(crate) struct Pos {
|
||||
pub(crate) line: usize,
|
||||
pub(crate) column: usize,
|
||||
}
|
||||
|
||||
impl Pos {
|
||||
fn new(line: usize, column: usize) -> Self {
|
||||
Self { line, column }
|
||||
}
|
||||
|
||||
fn left(&self) -> Self {
|
||||
Self {
|
||||
line: self.line,
|
||||
column: self.column.saturating_sub(1),
|
||||
}
|
||||
}
|
||||
|
||||
fn right(&self) -> Self {
|
||||
Self {
|
||||
line: self.line,
|
||||
column: self.column.saturating_add(1),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn span_pos(span: &Span) -> (Pos, Pos) {
|
||||
let span2: Span2 = (*span).into();
|
||||
let start = span2.start();
|
||||
let end = span2.end();
|
||||
|
||||
// In stable, line/column information is not provided
|
||||
// and set to 0 (line is 1-indexed)
|
||||
if start.line == 0 || end.line == 0 {
|
||||
return fallback_span_pos(span);
|
||||
}
|
||||
|
||||
(
|
||||
Pos::new(start.line, start.column),
|
||||
Pos::new(end.line, end.column),
|
||||
)
|
||||
}
|
||||
|
||||
fn parse_pos(span: &Span) -> Option<(usize, usize)> {
|
||||
// Workaround to somehow retrieve location information in span in stable rust :(
|
||||
|
||||
static RE: Lazy<Regex> = Lazy::new(|| Regex::new(r"bytes\(([0-9]+)\.\.([0-9]+)\)").unwrap());
|
||||
|
||||
match RE.captures(&format!("{:?}", span)) {
|
||||
Some(caps) => match (caps.get(1), caps.get(2)) {
|
||||
(Some(start), Some(end)) => Some((
|
||||
match start.as_str().parse() {
|
||||
Ok(v) => v,
|
||||
_ => return None,
|
||||
},
|
||||
match end.as_str().parse() {
|
||||
Ok(v) => v,
|
||||
_ => return None,
|
||||
},
|
||||
)),
|
||||
_ => None,
|
||||
},
|
||||
None => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn fallback_span_pos(span: &Span) -> (Pos, Pos) {
|
||||
let (start, end) = match parse_pos(span) {
|
||||
Some(v) => v,
|
||||
None => proc_macro_error::abort_call_site!(
|
||||
"Cannot retrieve span information; please use nightly"
|
||||
),
|
||||
};
|
||||
(Pos::new(1, start), Pos::new(1, end))
|
||||
}
|
||||
|
||||
/// Attribute of token.
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
enum TokenAttr {
|
||||
/// No attribute
|
||||
None,
|
||||
/// Starts with `$`
|
||||
Cap,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub(crate) struct Token {
|
||||
source: String,
|
||||
tree: TokenTree,
|
||||
start: Pos,
|
||||
end: Pos,
|
||||
attr: TokenAttr,
|
||||
}
|
||||
|
||||
impl PartialEq for Token {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.source == other.source && self.attr == other.attr
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for Token {}
|
||||
|
||||
impl Token {
|
||||
fn new(tree: TokenTree) -> Self {
|
||||
let (start, end) = span_pos(&tree.span());
|
||||
Self {
|
||||
source: tree.to_string(),
|
||||
start,
|
||||
end,
|
||||
tree,
|
||||
attr: TokenAttr::None,
|
||||
}
|
||||
}
|
||||
|
||||
fn new_delim(source: String, tree: TokenTree, open: bool) -> Self {
|
||||
let (start, end) = span_pos(&tree.span());
|
||||
let (start, end) = if open {
|
||||
(start, start.right())
|
||||
} else {
|
||||
(end.left(), end)
|
||||
};
|
||||
|
||||
Self {
|
||||
source,
|
||||
tree,
|
||||
start,
|
||||
end,
|
||||
attr: TokenAttr::None,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn tree(&self) -> &TokenTree {
|
||||
&self.tree
|
||||
}
|
||||
|
||||
pub(crate) fn is_cap(&self) -> bool {
|
||||
self.attr == TokenAttr::Cap
|
||||
}
|
||||
|
||||
pub(crate) fn start(&self) -> Pos {
|
||||
self.start
|
||||
}
|
||||
|
||||
pub(crate) fn end(&self) -> Pos {
|
||||
self.end
|
||||
}
|
||||
|
||||
fn is(&self, s: &str) -> bool {
|
||||
self.source == s
|
||||
}
|
||||
|
||||
fn attr(mut self, attr: TokenAttr) -> Self {
|
||||
self.attr = attr;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct Tokens(pub(crate) Vec<Token>);
|
||||
|
||||
impl Tokens {
|
||||
pub(crate) fn retokenize(tt: TokenStream) -> Tokens {
|
||||
Tokens(
|
||||
tt.into_iter()
|
||||
.map(Tokens::from)
|
||||
.flatten()
|
||||
.peekable()
|
||||
.batching(|iter| {
|
||||
// Find variable tokens
|
||||
let t = iter.next()?;
|
||||
if t.is("$") {
|
||||
// `$` + `ident` => `$ident`
|
||||
let t = iter.next().expect("$ must trail an identifier");
|
||||
Some(t.attr(TokenAttr::Cap))
|
||||
} else {
|
||||
Some(t)
|
||||
}
|
||||
})
|
||||
.collect(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoIterator for Tokens {
|
||||
type Item = Token;
|
||||
type IntoIter = IntoIter<Token>;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.0.into_iter()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<TokenTree> for Tokens {
|
||||
fn from(tt: TokenTree) -> Self {
|
||||
let tts = match tt.clone() {
|
||||
TokenTree::Group(g) => {
|
||||
let (b, e) = match g.delimiter() {
|
||||
Delimiter::Parenthesis => ("(", ")"),
|
||||
Delimiter::Brace => ("{", "}"),
|
||||
Delimiter::Bracket => ("[", "]"),
|
||||
Delimiter::None => ("", ""),
|
||||
};
|
||||
let (b, e) = (b.into(), e.into());
|
||||
|
||||
vec![Token::new_delim(b, tt.clone(), true)]
|
||||
.into_iter()
|
||||
.chain(g.stream().into_iter().map(Tokens::from).flatten())
|
||||
.chain(vec![Token::new_delim(e, tt, false)])
|
||||
.collect()
|
||||
}
|
||||
_ => vec![Token::new(tt)],
|
||||
};
|
||||
Tokens(tts)
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Token {
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
write!(f, "{}", self.source)
|
||||
}
|
||||
}
|
||||
+77
-14
@@ -1,3 +1,6 @@
|
||||
#![allow(clippy::wrong_self_convention)]
|
||||
|
||||
use std::borrow::Cow;
|
||||
use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet};
|
||||
use std::ffi::{CStr, CString};
|
||||
use std::hash::{BuildHasher, Hash};
|
||||
@@ -12,7 +15,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 +225,34 @@ 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 Box<str> {
|
||||
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
|
||||
Ok(Value::String(lua.create_string(&*self)?))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> FromLua<'lua> for Box<str> {
|
||||
fn from_lua(value: Value<'lua>, lua: &'lua Lua) -> Result<Self> {
|
||||
let ty = value.type_name();
|
||||
Ok(lua
|
||||
.coerce_string(value)?
|
||||
.ok_or_else(|| Error::FromLuaConversionError {
|
||||
from: ty,
|
||||
to: "Box<str>",
|
||||
message: Some("expected string or number".to_string()),
|
||||
})?
|
||||
.to_str()?
|
||||
.to_owned()
|
||||
.into_boxed_str())
|
||||
}
|
||||
}
|
||||
|
||||
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 +287,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 +382,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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -417,6 +460,26 @@ lua_convert_array! {
|
||||
30 31 32
|
||||
}
|
||||
|
||||
impl<'lua, T: ToLua<'lua>> ToLua<'lua> for Box<[T]> {
|
||||
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
|
||||
Ok(Value::Table(lua.create_sequence_from(self.into_vec())?))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua, T: FromLua<'lua>> FromLua<'lua> for Box<[T]> {
|
||||
fn from_lua(value: Value<'lua>, _: &'lua Lua) -> Result<Self> {
|
||||
if let Value::Table(table) = value {
|
||||
table.sequence_values().collect()
|
||||
} else {
|
||||
Err(Error::FromLuaConversionError {
|
||||
from: value.type_name(),
|
||||
to: "Box<[T]>",
|
||||
message: Some("expected table".to_string()),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua, T: ToLua<'lua>> ToLua<'lua> for Vec<T> {
|
||||
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
|
||||
Ok(Value::Table(lua.create_sequence_from(self)?))
|
||||
@@ -491,17 +554,17 @@ impl<'lua, T: Eq + Hash + ToLua<'lua>, S: BuildHasher> ToLua<'lua> for HashSet<T
|
||||
|
||||
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
|
||||
match value {
|
||||
Value::Table(table) if table.len()? > 0 => table.sequence_values().collect(),
|
||||
Value::Table(table) => table
|
||||
.pairs::<T, Value<'lua>>()
|
||||
.map(|res| res.map(|(k, _)| k))
|
||||
.collect()
|
||||
} else {
|
||||
Err(Error::FromLuaConversionError {
|
||||
.collect(),
|
||||
_ => Err(Error::FromLuaConversionError {
|
||||
from: value.type_name(),
|
||||
to: "HashSet",
|
||||
message: Some("expected table".to_string()),
|
||||
})
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -516,17 +579,17 @@ impl<'lua, T: Ord + ToLua<'lua>> ToLua<'lua> for BTreeSet<T> {
|
||||
|
||||
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
|
||||
match value {
|
||||
Value::Table(table) if table.len()? > 0 => table.sequence_values().collect(),
|
||||
Value::Table(table) => table
|
||||
.pairs::<T, Value<'lua>>()
|
||||
.map(|res| res.map(|(k, _)| k))
|
||||
.collect()
|
||||
} else {
|
||||
Err(Error::FromLuaConversionError {
|
||||
.collect(),
|
||||
_ => Err(Error::FromLuaConversionError {
|
||||
from: value.type_name(),
|
||||
to: "BTreeSet",
|
||||
message: Some("expected table".to_string()),
|
||||
})
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+36
-29
@@ -1,3 +1,5 @@
|
||||
#![allow(clippy::wrong_self_convention)]
|
||||
|
||||
use std::error::Error as StdError;
|
||||
use std::fmt;
|
||||
use std::io::Error as IoError;
|
||||
@@ -9,6 +11,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 {
|
||||
@@ -87,7 +90,7 @@ pub enum Error {
|
||||
},
|
||||
/// [`Thread::resume`] was called on an inactive coroutine.
|
||||
///
|
||||
/// A coroutine is inactive if its main function has returned or if an error has occured inside
|
||||
/// A coroutine is inactive if its main function has returned or if an error has occurred inside
|
||||
/// the coroutine.
|
||||
///
|
||||
/// [`Thread::status`] can be used to check if the coroutine can be resumed without causing this
|
||||
@@ -130,6 +133,18 @@ pub enum Error {
|
||||
/// [`AnyUserData`]: struct.AnyUserData.html
|
||||
/// [`UserData`]: trait.UserData.html
|
||||
UserDataBorrowMutError,
|
||||
/// A [`MetaMethod`] operation is restricted (typically for `__gc` or `__metatable`).
|
||||
///
|
||||
/// [`MetaMethod`]: enum.MetaMethod.html
|
||||
MetaMethodRestricted(StdString),
|
||||
/// A [`MetaMethod`] (eg. `__index` or `__newindex`) has invalid type.
|
||||
///
|
||||
/// [`MetaMethod`]: enum.MetaMethod.html
|
||||
MetaMethodTypeError {
|
||||
method: StdString,
|
||||
type_name: &'static str,
|
||||
message: Option<StdString>,
|
||||
},
|
||||
/// A `RegistryKey` produced from a different Lua state was used.
|
||||
MismatchedRegistryKey,
|
||||
/// A Rust callback returned `Err`, raising the contained `Error` as a Lua error.
|
||||
@@ -139,6 +154,11 @@ pub enum Error {
|
||||
/// Original error returned by the Rust code.
|
||||
cause: Arc<Error>,
|
||||
},
|
||||
/// A Rust panic that was previously 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")))]
|
||||
@@ -154,15 +174,13 @@ pub enum Error {
|
||||
/// Returning `Err(ExternalError(...))` from a Rust callback will raise the error as a Lua
|
||||
/// error. The Rust code that originally invoked the Lua code then receives a `CallbackError`,
|
||||
/// from which the original error (and a stack traceback) can be recovered.
|
||||
#[cfg(feature = "send")]
|
||||
ExternalError(Arc<dyn StdError + Send + Sync>),
|
||||
#[cfg(not(feature = "send"))]
|
||||
ExternalError(Arc<dyn StdError>),
|
||||
}
|
||||
|
||||
/// A specialized `Result` type used by `mlua`'s API.
|
||||
pub type Result<T> = StdResult<T, Error>;
|
||||
|
||||
#[cfg(not(tarpaulin_include))]
|
||||
impl fmt::Display for Error {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
match *self {
|
||||
@@ -197,22 +215,14 @@ impl fmt::Display for Error {
|
||||
fmt,
|
||||
"too many arguments to Function::bind"
|
||||
),
|
||||
Error::ToLuaConversionError {
|
||||
from,
|
||||
to,
|
||||
ref message,
|
||||
} => {
|
||||
Error::ToLuaConversionError { from, to, ref message } => {
|
||||
write!(fmt, "error converting {} to Lua {}", from, to)?;
|
||||
match *message {
|
||||
None => Ok(()),
|
||||
Some(ref message) => write!(fmt, " ({})", message),
|
||||
}
|
||||
}
|
||||
Error::FromLuaConversionError {
|
||||
from,
|
||||
to,
|
||||
ref message,
|
||||
} => {
|
||||
Error::FromLuaConversionError { from, to, ref message } => {
|
||||
write!(fmt, "error converting Lua {} to {}", from, to)?;
|
||||
match *message {
|
||||
None => Ok(()),
|
||||
@@ -224,11 +234,22 @@ impl fmt::Display for Error {
|
||||
Error::UserDataDestructed => write!(fmt, "userdata has been destructed"),
|
||||
Error::UserDataBorrowError => write!(fmt, "userdata already mutably borrowed"),
|
||||
Error::UserDataBorrowMutError => write!(fmt, "userdata already borrowed"),
|
||||
Error::MetaMethodRestricted(ref method) => write!(fmt, "metamethod {} is restricted", method),
|
||||
Error::MetaMethodTypeError { ref method, type_name, ref message } => {
|
||||
write!(fmt, "metamethod {} has unsupported type {}", method, type_name)?;
|
||||
match *message {
|
||||
None => Ok(()),
|
||||
Some(ref message) => write!(fmt, " ({})", message),
|
||||
}
|
||||
}
|
||||
Error::MismatchedRegistryKey => {
|
||||
write!(fmt, "RegistryKey used from different Lua state")
|
||||
}
|
||||
Error::CallbackError { ref traceback, .. } => {
|
||||
write!(fmt, "callback error: {}", traceback)
|
||||
write!(fmt, "callback error\n{}", traceback)
|
||||
}
|
||||
Error::PreviouslyResumedPanic => {
|
||||
write!(fmt, "previously resumed panic returned again")
|
||||
}
|
||||
#[cfg(feature = "serialize")]
|
||||
Error::SerializeError(ref err) => {
|
||||
@@ -254,35 +275,21 @@ impl StdError for Error {
|
||||
}
|
||||
|
||||
impl Error {
|
||||
#[cfg(feature = "send")]
|
||||
pub fn external<T: Into<Box<dyn StdError + Send + Sync>>>(err: T) -> Error {
|
||||
Error::ExternalError(err.into().into())
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "send"))]
|
||||
pub fn external<T: Into<Box<dyn StdError>>>(err: T) -> Error {
|
||||
Error::ExternalError(err.into().into())
|
||||
}
|
||||
}
|
||||
|
||||
pub trait ExternalError {
|
||||
fn to_lua_err(self) -> Error;
|
||||
}
|
||||
|
||||
#[cfg(feature = "send")]
|
||||
impl<E: Into<Box<dyn StdError + Send + Sync>>> ExternalError for E {
|
||||
fn to_lua_err(self) -> Error {
|
||||
Error::external(self)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "send"))]
|
||||
impl<E: Into<Box<dyn StdError>>> ExternalError for E {
|
||||
fn to_lua_err(self) -> Error {
|
||||
Error::external(self)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait ExternalResult<T> {
|
||||
fn to_lua_err(self) -> Result<T>;
|
||||
}
|
||||
|
||||
+40
-3
@@ -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
|
||||
@@ -228,6 +228,7 @@ pub fn lua_upvalueindex(i: c_int) -> c_int {
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "lua51", feature = "luajit"))]
|
||||
#[inline(always)]
|
||||
pub unsafe fn lua_absindex(L: *mut lua_State, mut idx: c_int) -> c_int {
|
||||
if idx < 0 && idx > lua::LUA_REGISTRYINDEX {
|
||||
idx += lua_gettop(L) + 1;
|
||||
@@ -250,6 +251,7 @@ end
|
||||
|
||||
#[cfg(any(feature = "lua51", feature = "luajit"))]
|
||||
pub unsafe fn lua_arith(L: *mut lua_State, op: c_int) {
|
||||
#[allow(clippy::manual_range_contains)]
|
||||
if op < LUA_OPADD || op > LUA_OPUNM {
|
||||
luaL_error(L, cstr!("invalid 'op' argument for lua_arith"));
|
||||
}
|
||||
@@ -278,6 +280,7 @@ pub unsafe fn lua_rotate(L: *mut lua_State, mut idx: c_int, mut n: c_int) {
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "lua51", feature = "luajit"))]
|
||||
#[inline(always)]
|
||||
pub unsafe fn lua_copy(L: *mut lua_State, fromidx: c_int, toidx: c_int) {
|
||||
let abs_to = lua_absindex(L, toidx);
|
||||
luaL_checkstack(L, 1, cstr!("not enough stack slots"));
|
||||
@@ -285,6 +288,7 @@ pub unsafe fn lua_copy(L: *mut lua_State, fromidx: c_int, toidx: c_int) {
|
||||
lua_replace(L, abs_to);
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn lua_isinteger(L: *mut lua_State, idx: c_int) -> c_int {
|
||||
if lua_type(L, idx) == lua::LUA_TNUMBER {
|
||||
let n = lua_tonumber(L, idx);
|
||||
@@ -297,6 +301,7 @@ pub unsafe fn lua_isinteger(L: *mut lua_State, idx: c_int) -> c_int {
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "lua51", feature = "luajit"))]
|
||||
#[inline(always)]
|
||||
pub unsafe fn lua_tonumberx(L: *mut lua_State, i: c_int, isnum: *mut c_int) -> lua_Number {
|
||||
let n = lua_tonumber(L, i);
|
||||
if !isnum.is_null() {
|
||||
@@ -311,6 +316,7 @@ pub unsafe fn lua_tonumberx(L: *mut lua_State, i: c_int, isnum: *mut c_int) -> l
|
||||
|
||||
// Implemented for Lua 5.2 as well
|
||||
// See https://github.com/keplerproject/lua-compat-5.3/issues/40
|
||||
#[inline(always)]
|
||||
pub unsafe fn lua_tointegerx(L: *mut lua_State, i: c_int, isnum: *mut c_int) -> lua_Integer {
|
||||
let mut ok = 0;
|
||||
let n = lua_tonumberx(L, i, &mut ok);
|
||||
@@ -328,11 +334,13 @@ pub unsafe fn lua_tointegerx(L: *mut lua_State, i: c_int, isnum: *mut c_int) ->
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "lua51", feature = "luajit"))]
|
||||
#[inline(always)]
|
||||
pub unsafe fn lua_rawlen(L: *mut lua_State, idx: c_int) -> usize {
|
||||
lua_objlen(L, idx)
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "lua51", feature = "luajit"))]
|
||||
#[inline(always)]
|
||||
pub unsafe fn lua_compare(L: *mut lua_State, mut idx1: c_int, mut idx2: c_int, op: c_int) -> c_int {
|
||||
match op {
|
||||
lua::LUA_OPEQ => lua_equal(L, idx1, idx2),
|
||||
@@ -353,6 +361,7 @@ pub unsafe fn lua_compare(L: *mut lua_State, mut idx1: c_int, mut idx2: c_int, o
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "lua51", feature = "luajit"))]
|
||||
#[inline(always)]
|
||||
pub unsafe fn lua_pushlstring(L: *mut lua_State, s: *const c_char, l: usize) -> *const c_char {
|
||||
if l == 0 {
|
||||
lua_pushlstring_old(L, cstr!(""), 0);
|
||||
@@ -363,6 +372,7 @@ pub unsafe fn lua_pushlstring(L: *mut lua_State, s: *const c_char, l: usize) ->
|
||||
}
|
||||
|
||||
#[cfg(feature = "lua52")]
|
||||
#[inline(always)]
|
||||
pub unsafe fn lua_pushlstring(L: *mut lua_State, s: *const c_char, l: usize) -> *const c_char {
|
||||
if l == 0 {
|
||||
lua_pushlstring_old(L, cstr!(""), 0)
|
||||
@@ -372,27 +382,32 @@ pub unsafe fn lua_pushlstring(L: *mut lua_State, s: *const c_char, l: usize) ->
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "lua51", feature = "luajit"))]
|
||||
#[inline(always)]
|
||||
pub unsafe fn lua_pushstring(L: *mut lua_State, s: *const c_char) -> *const c_char {
|
||||
lua_pushstring_old(L, s);
|
||||
lua_tostring(L, -1)
|
||||
}
|
||||
|
||||
#[cfg(feature = "lua52")]
|
||||
#[inline(always)]
|
||||
pub unsafe fn lua_getglobal(L: *mut lua_State, var: *const c_char) -> c_int {
|
||||
lua_getglobal_old(L, var);
|
||||
lua_type(L, -1)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn lua_gettable(L: *mut lua_State, idx: c_int) -> c_int {
|
||||
lua_gettable_old(L, idx);
|
||||
lua_type(L, -1)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn lua_getfield(L: *mut lua_State, idx: c_int, k: *const c_char) -> c_int {
|
||||
lua_getfield_old(L, idx, k);
|
||||
lua_type(L, -1)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn lua_geti(L: *mut lua_State, mut idx: c_int, n: lua_Integer) -> c_int {
|
||||
idx = lua_absindex(L, idx);
|
||||
lua_pushinteger(L, n);
|
||||
@@ -401,18 +416,21 @@ pub unsafe fn lua_geti(L: *mut lua_State, mut idx: c_int, n: lua_Integer) -> c_i
|
||||
}
|
||||
|
||||
// A new version which returns c_int
|
||||
#[inline(always)]
|
||||
pub unsafe fn lua_rawget(L: *mut lua_State, idx: c_int) -> c_int {
|
||||
lua_rawget_old(L, idx);
|
||||
lua_type(L, -1)
|
||||
}
|
||||
|
||||
// A new version which returns c_int
|
||||
#[inline(always)]
|
||||
pub unsafe fn lua_rawgeti(L: *mut lua_State, idx: c_int, n: lua_Integer) -> c_int {
|
||||
lua_rawgeti_old(L, idx, n);
|
||||
lua_type(L, -1)
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "lua51", feature = "luajit"))]
|
||||
#[inline(always)]
|
||||
pub unsafe fn lua_rawgetp(L: *mut lua_State, idx: c_int, p: *const c_void) -> c_int {
|
||||
let abs_i = lua_absindex(L, idx);
|
||||
lua_pushlightuserdata(L, p as *mut c_void);
|
||||
@@ -421,23 +439,27 @@ pub unsafe fn lua_rawgetp(L: *mut lua_State, idx: c_int, p: *const c_void) -> c_
|
||||
}
|
||||
|
||||
#[cfg(feature = "lua52")]
|
||||
#[inline(always)]
|
||||
pub unsafe fn lua_rawgetp(L: *mut lua_State, idx: c_int, p: *const c_void) -> c_int {
|
||||
lua_rawgetp_old(L, idx, p);
|
||||
lua_type(L, -1)
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "lua51", feature = "luajit"))]
|
||||
#[inline(always)]
|
||||
pub unsafe fn lua_getuservalue(L: *mut lua_State, idx: c_int) -> c_int {
|
||||
lua_getfenv(L, idx);
|
||||
lua_type(L, -1)
|
||||
}
|
||||
|
||||
#[cfg(feature = "lua52")]
|
||||
#[inline(always)]
|
||||
pub unsafe fn lua_getuservalue(L: *mut lua_State, idx: c_int) -> c_int {
|
||||
lua_getuservalue_old(L, idx);
|
||||
lua_type(L, -1)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn lua_seti(L: *mut lua_State, mut idx: c_int, n: lua_Integer) {
|
||||
luaL_checkstack(L, 1, cstr!("not enough stack slots available"));
|
||||
idx = lua_absindex(L, idx);
|
||||
@@ -447,6 +469,7 @@ pub unsafe fn lua_seti(L: *mut lua_State, mut idx: c_int, n: lua_Integer) {
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "lua51", feature = "luajit"))]
|
||||
#[inline(always)]
|
||||
pub unsafe fn lua_rawsetp(L: *mut lua_State, idx: c_int, p: *const c_void) {
|
||||
let abs_i = lua_absindex(L, idx);
|
||||
luaL_checkstack(L, 1, cstr!("not enough stack slots"));
|
||||
@@ -456,11 +479,13 @@ pub unsafe fn lua_rawsetp(L: *mut lua_State, idx: c_int, p: *const c_void) {
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "lua51", feature = "luajit"))]
|
||||
#[inline(always)]
|
||||
pub unsafe fn lua_setuservalue(L: *mut lua_State, idx: c_int) {
|
||||
luaL_checktype(L, -1, lua::LUA_TTABLE);
|
||||
lua_setfenv(L, idx);
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn lua_dump(
|
||||
L: *mut lua_State,
|
||||
writer: lua_Writer,
|
||||
@@ -471,11 +496,13 @@ pub unsafe fn lua_dump(
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "lua51", feature = "luajit"))]
|
||||
#[inline(always)]
|
||||
pub unsafe fn lua_resume(L: *mut lua_State, _from: *mut lua_State, narg: c_int) -> c_int {
|
||||
lua_resume_old(L, narg)
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "lua51", feature = "luajit"))]
|
||||
#[inline(always)]
|
||||
pub unsafe fn lua_len(L: *mut lua_State, idx: c_int) {
|
||||
match lua_type(L, idx) {
|
||||
lua::LUA_TSTRING => {
|
||||
@@ -497,6 +524,7 @@ pub unsafe fn lua_len(L: *mut lua_State, idx: c_int) {
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn lua_stringtonumber(L: *mut lua_State, s: *const c_char) -> usize {
|
||||
use std::str::FromStr;
|
||||
|
||||
@@ -510,6 +538,7 @@ pub unsafe fn lua_stringtonumber(L: *mut lua_State, s: *const c_char) -> usize {
|
||||
0
|
||||
}
|
||||
|
||||
#[allow(clippy::branches_sharing_code)]
|
||||
pub unsafe fn lua_getextraspace(L: *mut lua_State) -> *mut c_void {
|
||||
use super::glue::LUA_EXTRASPACE;
|
||||
|
||||
@@ -574,6 +603,7 @@ pub unsafe fn lua_pushglobaltable(L: *mut lua_State) {
|
||||
//
|
||||
|
||||
#[cfg(any(feature = "lua51", feature = "luajit"))]
|
||||
#[inline(always)]
|
||||
pub unsafe fn luaL_checkstack(L: *mut lua_State, sz: c_int, msg: *const c_char) {
|
||||
if lua_checkstack(L, sz + lua::LUA_MINSTACK) == 0 {
|
||||
if !msg.is_null() {
|
||||
@@ -589,6 +619,7 @@ pub unsafe fn luaL_checkversion(_L: *mut lua_State) {
|
||||
// Void
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn luaL_getmetafield(L: *mut lua_State, obj: c_int, e: *const c_char) -> c_int {
|
||||
if luaL_getmetafield_old(L, obj, e) != 0 {
|
||||
lua_type(L, -1)
|
||||
@@ -597,6 +628,7 @@ pub unsafe fn luaL_getmetafield(L: *mut lua_State, obj: c_int, e: *const c_char)
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn luaL_newmetatable(L: *mut lua_State, tname: *const c_char) -> c_int {
|
||||
if luaL_newmetatable_old(L, tname) != 0 {
|
||||
lua_pushstring(L, tname);
|
||||
@@ -608,6 +640,7 @@ pub unsafe fn luaL_newmetatable(L: *mut lua_State, tname: *const c_char) -> c_in
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "lua51", feature = "luajit"))]
|
||||
#[inline(always)]
|
||||
pub unsafe fn luaL_loadbufferx(
|
||||
L: *mut lua_State,
|
||||
buff: *const c_char,
|
||||
@@ -627,6 +660,7 @@ pub unsafe fn luaL_loadbufferx(
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "lua51", feature = "luajit"))]
|
||||
#[inline(always)]
|
||||
pub unsafe fn luaL_len(L: *mut lua_State, idx: c_int) -> lua_Integer {
|
||||
let mut isnum = 0;
|
||||
luaL_checkstack(L, 1, cstr!("not enough stack slots"));
|
||||
@@ -646,7 +680,7 @@ pub unsafe fn luaL_traceback(
|
||||
msg: *const c_char,
|
||||
mut level: c_int,
|
||||
) {
|
||||
let mut ar: lua_Debug = std::mem::zeroed();
|
||||
let mut ar: lua_Debug = mem::zeroed();
|
||||
let top = lua_gettop(L);
|
||||
let numlevels = compat53_countlevels(L1);
|
||||
let mark = if numlevels > COMPAT53_LEVELS1 + COMPAT53_LEVELS2 {
|
||||
@@ -667,7 +701,7 @@ pub unsafe fn luaL_traceback(
|
||||
level = numlevels - COMPAT53_LEVELS2; // and skip to last ones
|
||||
} else {
|
||||
lua_getinfo(L1, cstr!("Slnt"), &mut ar);
|
||||
lua_pushfstring(L, cstr!("\n\t%s:"), cstr!("ok") /*ar.short_src*/);
|
||||
lua_pushfstring(L, cstr!("\n\t%s:"), ar.short_src.as_ptr());
|
||||
if ar.currentline > 0 {
|
||||
lua_pushfstring(L, cstr!("%d:"), ar.currentline);
|
||||
}
|
||||
@@ -716,6 +750,7 @@ pub unsafe fn luaL_tolstring(L: *mut lua_State, idx: c_int, len: *mut usize) ->
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "lua51", feature = "luajit"))]
|
||||
#[inline(always)]
|
||||
pub unsafe fn luaL_setmetatable(L: *mut lua_State, tname: *const c_char) {
|
||||
luaL_checkstack(L, 1, cstr!("not enough stack slots"));
|
||||
luaL_getmetatable(L, tname);
|
||||
@@ -723,6 +758,7 @@ pub unsafe fn luaL_setmetatable(L: *mut lua_State, tname: *const c_char) {
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "lua51", feature = "luajit"))]
|
||||
#[inline(always)]
|
||||
pub unsafe fn luaL_testudata(L: *mut lua_State, i: c_int, tname: *const c_char) -> *mut c_void {
|
||||
let mut p = lua_touserdata(L, i);
|
||||
luaL_checkstack(L, 2, cstr!("not enough stack slots"));
|
||||
@@ -740,6 +776,7 @@ pub unsafe fn luaL_testudata(L: *mut lua_State, i: c_int, tname: *const c_char)
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "lua51", feature = "luajit"))]
|
||||
#[inline(always)]
|
||||
pub unsafe fn luaL_setfuncs(L: *mut lua_State, mut l: *const luaL_Reg, nup: c_int) {
|
||||
luaL_checkstack(L, nup + 1, cstr!("too many upvalues"));
|
||||
while !(*l).name.is_null() {
|
||||
|
||||
+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
|
||||
|
||||
+24
-5
@@ -1,6 +1,6 @@
|
||||
// The MIT License (MIT)
|
||||
//
|
||||
// Copyright (c) 2019-2020 A. Orlenko
|
||||
// Copyright (c) 2019-2021 A. Orlenko
|
||||
// Copyright (c) 2014 J.C. Moyer
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
@@ -32,11 +32,9 @@ use std::ptr;
|
||||
|
||||
use super::luaconf;
|
||||
|
||||
pub use super::glue::{LUA_RELEASE, LUA_VERSION, LUA_VERSION_NUM};
|
||||
|
||||
pub use super::glue::LUA_REGISTRYINDEX;
|
||||
#[cfg(any(feature = "lua51", feature = "luajit"))]
|
||||
pub use super::glue::{LUA_ENVIRONINDEX, LUA_GLOBALSINDEX};
|
||||
pub use super::glue::{LUA_REGISTRYINDEX, LUA_VERSION_NUM};
|
||||
|
||||
#[cfg(not(feature = "luajit"))]
|
||||
pub const LUA_SIGNATURE: &[u8] = b"\x1bLua";
|
||||
@@ -113,6 +111,10 @@ pub const LUA_RIDX_GLOBALS: lua_Integer = 2;
|
||||
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
|
||||
pub const LUA_RIDX_LAST: lua_Integer = LUA_RIDX_GLOBALS;
|
||||
|
||||
// I believe `luaL_traceback` < 5.4 requires this much free stack to not error.
|
||||
// 5.4 uses `luaL_Buffer`
|
||||
pub const LUA_TRACEBACK_STACK: c_int = 11;
|
||||
|
||||
/// A Lua number, usually equivalent to `f64`.
|
||||
pub type lua_Number = luaconf::LUA_NUMBER;
|
||||
|
||||
@@ -158,8 +160,13 @@ extern "C" {
|
||||
pub fn lua_newstate(f: lua_Alloc, ud: *mut c_void) -> *mut lua_State;
|
||||
pub fn lua_close(L: *mut lua_State);
|
||||
pub fn lua_newthread(L: *mut lua_State) -> *mut lua_State;
|
||||
|
||||
#[cfg(feature = "lua54")]
|
||||
pub fn lua_resetthread(L: *mut lua_State) -> c_int;
|
||||
#[link_name = "lua_resetthread"]
|
||||
pub fn lua_resetthread_54(L: *mut lua_State) -> c_int;
|
||||
#[cfg(all(feature = "luajit", feature = "vendored"))]
|
||||
#[link_name = "lua_resetthread"]
|
||||
pub fn lua_resetthread_jit(L: *mut lua_State, th: *mut lua_State);
|
||||
|
||||
pub fn lua_atpanic(L: *mut lua_State, panicf: lua_CFunction) -> lua_CFunction;
|
||||
|
||||
@@ -218,6 +225,17 @@ extern "C" {
|
||||
pub fn lua_topointer(L: *mut lua_State, idx: c_int) -> *const c_void;
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "lua54", all(feature = "luajit", feature = "vendored")))]
|
||||
pub unsafe fn lua_resetthread(_L: *mut lua_State, th: *mut lua_State) -> c_int {
|
||||
#[cfg(all(feature = "luajit", feature = "vendored"))]
|
||||
{
|
||||
lua_resetthread_jit(_L, th);
|
||||
LUA_OK
|
||||
}
|
||||
#[cfg(feature = "lua54")]
|
||||
lua_resetthread_54(th)
|
||||
}
|
||||
|
||||
// Comparison and arithmetic functions
|
||||
pub const LUA_OPADD: c_int = 0;
|
||||
pub const LUA_OPSUB: c_int = 1;
|
||||
@@ -533,6 +551,7 @@ pub unsafe fn lua_yield(L: *mut lua_State, n: c_int) -> c_int {
|
||||
feature = "lua51",
|
||||
feature = "luajit"
|
||||
))]
|
||||
#[inline(always)]
|
||||
pub unsafe fn lua_resume(
|
||||
L: *mut lua_State,
|
||||
from: *mut lua_State,
|
||||
|
||||
+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
|
||||
|
||||
+31
-6
@@ -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
|
||||
@@ -160,8 +160,8 @@ pub use self::lua::{
|
||||
|
||||
#[cfg(feature = "lua54")]
|
||||
pub use self::lua::{
|
||||
lua_getiuservalue, lua_newuserdatauv, lua_resetthread, lua_setcstacklimit, lua_setiuservalue,
|
||||
lua_setwarnf, lua_toclose, lua_warning,
|
||||
lua_getiuservalue, lua_newuserdatauv, lua_setcstacklimit, lua_setiuservalue, lua_setwarnf,
|
||||
lua_toclose, lua_warning,
|
||||
};
|
||||
|
||||
#[cfg(any(feature = "lua54", feature = "lua53"))]
|
||||
@@ -170,6 +170,9 @@ pub use self::lua::{lua_isyieldable, lua_version};
|
||||
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
|
||||
pub use self::lua::{lua_callk, lua_pcallk, lua_upvalueid, lua_upvaluejoin, lua_yieldk};
|
||||
|
||||
#[cfg(any(feature = "lua54", all(feature = "luajit", feature = "vendored")))]
|
||||
pub use self::lua::lua_resetthread;
|
||||
|
||||
// auxiliary library types
|
||||
pub use self::lauxlib::luaL_Reg;
|
||||
|
||||
@@ -212,7 +215,8 @@ pub use self::lua::{
|
||||
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_SIGNATURE, LUA_TBOOLEAN, LUA_TFUNCTION, LUA_TLIGHTUSERDATA, LUA_TNIL, LUA_TNONE,
|
||||
LUA_TNUMBER, LUA_TSTRING, LUA_TTABLE, LUA_TTHREAD, LUA_TUSERDATA, LUA_YIELD,
|
||||
LUA_TNUMBER, LUA_TRACEBACK_STACK, LUA_TSTRING, LUA_TTABLE, LUA_TTHREAD, LUA_TUSERDATA,
|
||||
LUA_YIELD,
|
||||
};
|
||||
|
||||
#[cfg(any(feature = "lua54", feature = "lua53"))]
|
||||
@@ -253,16 +257,18 @@ pub use self::lualib::{LUA_FFILIBNAME, LUA_JITLIBNAME};
|
||||
// Not actually defined in lua.h / luaconf.h
|
||||
pub const LUA_MAX_UPVALUES: c_int = 255;
|
||||
|
||||
// Copied from https://github.com/rust-lang/rust/blob/master/src/libstd/sys_common/alloc.rs
|
||||
// Copied from https://github.com/rust-lang/rust/blob/master/library/std/src/sys/common/alloc.rs
|
||||
#[cfg(all(any(
|
||||
target_arch = "x86",
|
||||
target_arch = "arm",
|
||||
target_arch = "mips",
|
||||
target_arch = "powerpc",
|
||||
target_arch = "powerpc64",
|
||||
target_arch = "sparc",
|
||||
target_arch = "asmjs",
|
||||
target_arch = "wasm32",
|
||||
target_arch = "hexagon"
|
||||
target_arch = "hexagon",
|
||||
target_arch = "riscv32"
|
||||
)))]
|
||||
pub const SYS_MIN_ALIGN: usize = 8;
|
||||
#[cfg(all(any(
|
||||
@@ -275,6 +281,25 @@ pub const SYS_MIN_ALIGN: usize = 8;
|
||||
)))]
|
||||
pub const SYS_MIN_ALIGN: usize = 16;
|
||||
|
||||
// Hack to avoid stripping a few unused Lua symbols that could be imported
|
||||
// by C modules in unsafe mode
|
||||
pub(crate) fn keep_lua_symbols() {
|
||||
let mut symbols: Vec<*const extern "C" fn()> = Vec::new();
|
||||
symbols.push(lua_atpanic as _);
|
||||
symbols.push(lua_isuserdata as _);
|
||||
symbols.push(lua_tocfunction as _);
|
||||
symbols.push(luaL_loadstring as _);
|
||||
symbols.push(luaL_openlibs as _);
|
||||
if cfg!(any(any(
|
||||
feature = "lua54",
|
||||
feature = "lua53",
|
||||
feature = "lua52"
|
||||
))) {
|
||||
symbols.push(lua_getglobal as _);
|
||||
symbols.push(lua_setglobal as _);
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unused_imports, dead_code, non_camel_case_types)]
|
||||
#[allow(clippy::unreadable_literal)]
|
||||
mod glue {
|
||||
|
||||
+11
-15
@@ -1,12 +1,11 @@
|
||||
use std::os::raw::{c_int, c_void};
|
||||
use std::{ptr, slice};
|
||||
use std::ptr;
|
||||
use std::slice;
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
use crate::ffi;
|
||||
use crate::types::LuaRef;
|
||||
use crate::util::{
|
||||
assert_stack, check_stack, error_traceback, pop_error, protect_lua_closure, StackGuard,
|
||||
};
|
||||
use crate::util::{assert_stack, check_stack, error_traceback, pop_error, protect_lua, StackGuard};
|
||||
use crate::value::{FromLuaMulti, MultiValue, ToLuaMulti};
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
@@ -92,7 +91,7 @@ impl<'lua> Function<'lua> {
|
||||
/// Returns a Feature that, when polled, calls `self`, passing `args` as function arguments,
|
||||
/// and drives the execution.
|
||||
///
|
||||
/// Internaly it wraps the function to an [`AsyncThread`].
|
||||
/// Internally it wraps the function to an [`AsyncThread`].
|
||||
///
|
||||
/// Requires `feature = "async"`
|
||||
///
|
||||
@@ -193,13 +192,13 @@ impl<'lua> Function<'lua> {
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(lua.state);
|
||||
check_stack(lua.state, nargs + 5)?;
|
||||
|
||||
lua.push_ref(&self.0);
|
||||
ffi::lua_pushinteger(lua.state, nargs as ffi::lua_Integer);
|
||||
for arg in args {
|
||||
lua.push_value(arg)?;
|
||||
}
|
||||
|
||||
protect_lua_closure(lua.state, nargs + 2, 1, |state| {
|
||||
protect_lua(lua.state, nargs + 2, 1, |state| {
|
||||
ffi::lua_pushcclosure(state, bind_call_impl, nargs + 2);
|
||||
})?;
|
||||
|
||||
@@ -211,7 +210,7 @@ impl<'lua> Function<'lua> {
|
||||
///
|
||||
/// 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>> {
|
||||
pub fn dump(&self, strip: bool) -> Vec<u8> {
|
||||
unsafe extern "C" fn writer(
|
||||
_state: *mut ffi::lua_State,
|
||||
buf: *const c_void,
|
||||
@@ -229,18 +228,15 @@ impl<'lua> Function<'lua> {
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(lua.state);
|
||||
assert_stack(lua.state, 1);
|
||||
|
||||
lua.push_ref(&self.0);
|
||||
let data_ptr = &mut data as *mut Vec<u8> as *mut c_void;
|
||||
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_dump(lua.state, writer, data_ptr, strip);
|
||||
ffi::lua_pop(lua.state, 1);
|
||||
}
|
||||
|
||||
Ok(data)
|
||||
data
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+6
-6
@@ -9,9 +9,9 @@ use crate::util::callback_error;
|
||||
/// Contains information about currently executing Lua code.
|
||||
///
|
||||
/// The `Debug` structure is provided as a parameter to the hook function set with
|
||||
/// [`Lua::set_hook`]. You may call the methods on this structure to retrieve information about the
|
||||
/// Lua code executing at the time that the hook function was called. Further information can be
|
||||
/// found in the [Lua 5.3 documentaton][lua_doc].
|
||||
/// [`Lua::set_hook`]. You may call the methods on this structure to retrieve information about the
|
||||
/// Lua code executing at the time that the hook function was called. Further information can be
|
||||
/// found in the [Lua 5.3 documentation][lua_doc].
|
||||
///
|
||||
/// [lua_doc]: https://www.lua.org/manual/5.3/manual.html#lua_Debug
|
||||
/// [`Lua::set_hook`]: struct.Lua.html#method.set_hook
|
||||
@@ -130,7 +130,7 @@ pub struct HookTriggers {
|
||||
pub on_returns: bool,
|
||||
/// Before executing a new line, or returning from a function call.
|
||||
pub every_line: bool,
|
||||
/// After a certain number of VM instructions have been executed. When set to `Some(count)`,
|
||||
/// After a certain number of VM instructions have been executed. When set to `Some(count)`,
|
||||
/// `count` is the number of VM instructions to execute before calling the hook.
|
||||
///
|
||||
/// # Performance
|
||||
@@ -173,7 +173,7 @@ pub(crate) unsafe extern "C" fn hook_proc(state: *mut lua_State, ar: *mut lua_De
|
||||
_phantom: PhantomData,
|
||||
};
|
||||
|
||||
let lua = Lua::make_from_ptr(state);
|
||||
let lua = mlua_expect!(Lua::make_from_ptr(state), "cannot make Lua instance");
|
||||
let hook_cb = mlua_expect!(lua.hook_callback(), "no hook callback set in hook_proc");
|
||||
|
||||
#[allow(clippy::match_wild_err_arm)]
|
||||
@@ -183,7 +183,7 @@ pub(crate) unsafe extern "C" fn hook_proc(state: *mut lua_State, ar: *mut lua_De
|
||||
}?;
|
||||
|
||||
Ok(())
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
unsafe fn ptr_to_str<'a>(input: *const c_char) -> Option<&'a [u8]> {
|
||||
|
||||
+73
-11
@@ -23,6 +23,7 @@
|
||||
//!
|
||||
//! 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.
|
||||
//! Fields are supported using the [`UserDataFields`] API.
|
||||
//!
|
||||
//! # Serde support
|
||||
//!
|
||||
@@ -50,8 +51,8 @@
|
||||
//!
|
||||
//! [Lua programming language]: https://www.lua.org/
|
||||
//! [`Lua`]: struct.Lua.html
|
||||
//! [executing]: struct.Lua.html#method.exec
|
||||
//! [evaluating]: struct.Lua.html#method.eval
|
||||
//! [executing]: struct.Chunk.html#method.exec
|
||||
//! [evaluating]: struct.Chunk.html#method.eval
|
||||
//! [globals]: struct.Lua.html#method.globals
|
||||
//! [`ToLua`]: trait.ToLua.html
|
||||
//! [`FromLua`]: trait.FromLua.html
|
||||
@@ -59,6 +60,7 @@
|
||||
//! [`FromLuaMulti`]: trait.FromLuaMulti.html
|
||||
//! [`Function`]: struct.Function.html
|
||||
//! [`UserData`]: trait.UserData.html
|
||||
//! [`UserDataFields`]: trait.UserDataFields.html
|
||||
//! [`UserDataMethods`]: trait.UserDataMethods.html
|
||||
//! [`LuaSerdeExt`]: serde/trait.LuaSerdeExt.html
|
||||
//! [`Value`]: enum.Value.html
|
||||
@@ -70,7 +72,7 @@
|
||||
//! [`serde::Deserialize`]: https://docs.serde.rs/serde/de/trait.Deserialize.html
|
||||
|
||||
// mlua types in rustdoc of other crates get linked to here.
|
||||
#![doc(html_root_url = "https://docs.rs/mlua/0.5.0")]
|
||||
#![doc(html_root_url = "https://docs.rs/mlua/0.6.1")]
|
||||
// Deny warnings inside doc tests / examples. When this isn't present, rustdoc doesn't show *any*
|
||||
// warnings at all.
|
||||
#![doc(test(attr(deny(warnings))))]
|
||||
@@ -96,12 +98,13 @@ mod userdata;
|
||||
mod util;
|
||||
mod value;
|
||||
|
||||
#[doc(hidden)]
|
||||
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, ChunkMode, GCMode, Lua};
|
||||
pub use crate::lua::{AsChunk, Chunk, ChunkMode, GCMode, Lua, LuaOptions};
|
||||
pub use crate::multi::Variadic;
|
||||
pub use crate::scope::Scope;
|
||||
pub use crate::stdlib::StdLib;
|
||||
@@ -109,25 +112,84 @@ pub use crate::string::String;
|
||||
pub use crate::table::{Table, TableExt, TablePairs, TableSequence};
|
||||
pub use crate::thread::{Thread, ThreadStatus};
|
||||
pub use crate::types::{Integer, LightUserData, Number, RegistryKey};
|
||||
pub use crate::userdata::{AnyUserData, MetaMethod, UserData, UserDataMethods};
|
||||
pub use crate::userdata::{
|
||||
AnyUserData, MetaMethod, UserData, UserDataFields, UserDataMetatable, UserDataMethods,
|
||||
};
|
||||
pub use crate::value::{FromLua, FromLuaMulti, MultiValue, Nil, ToLua, ToLuaMulti, Value};
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
pub use crate::thread::AsyncThread;
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
pub use crate::serde::LuaSerdeExt;
|
||||
#[doc(inline)]
|
||||
pub use crate::serde::{ser::Options as SerializeOptions, 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")]
|
||||
#[cfg(any(feature = "mlua_derive"))]
|
||||
#[allow(unused_imports)]
|
||||
#[macro_use]
|
||||
extern crate mlua_derive;
|
||||
#[cfg(feature = "mlua_derive")]
|
||||
#[doc(hidden)]
|
||||
pub use mlua_derive::*;
|
||||
|
||||
/// Create a type that implements [`AsChunk`] and can capture Rust variables.
|
||||
///
|
||||
/// This macro allows to write Lua code directly in Rust code.
|
||||
///
|
||||
/// Rust variables can be referenced from Lua using `$` prefix, as shown in the example below.
|
||||
/// User's Rust types needs to implement [`UserData`] or [`ToLua`] traits.
|
||||
///
|
||||
/// Captured variables are **moved** into the chunk.
|
||||
///
|
||||
/// ```
|
||||
/// use mlua::{Lua, Result, chunk};
|
||||
///
|
||||
/// fn main() -> Result<()> {
|
||||
/// let lua = Lua::new();
|
||||
/// let name = "Rustacean";
|
||||
/// lua.load(chunk! {
|
||||
/// print("hello, " .. $name)
|
||||
/// }).exec()
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// ## Syntax issues
|
||||
///
|
||||
/// Since the Rust tokenizer will tokenize Lua code, this imposes some restrictions.
|
||||
/// The main thing to remember is:
|
||||
///
|
||||
/// - Use double quoted strings (`""`) instead of single quoted strings (`''`).
|
||||
///
|
||||
/// (Single quoted strings only work if they contain a single character, since in Rust,
|
||||
/// `'a'` is a character literal).
|
||||
///
|
||||
/// - Using Lua comments `--` is not desirable in **stable** Rust and can have bad side effects.
|
||||
///
|
||||
/// This is because procedural macros have Line/Column information available only in
|
||||
/// **nightly** Rust. Instead, Lua chunks represented as a big single line of code in stable Rust.
|
||||
///
|
||||
/// As workaround, Rust comments `//` can be used.
|
||||
///
|
||||
/// Other minor limitations:
|
||||
///
|
||||
/// - Certain escape codes in string literals don't work.
|
||||
/// (Specifically: `\a`, `\b`, `\f`, `\v`, `\123` (octal escape codes), `\u`, and `\U`).
|
||||
///
|
||||
/// These are accepted: : `\\`, `\n`, `\t`, `\r`, `\xAB` (hex escape codes), and `\0`.
|
||||
///
|
||||
/// - The `//` (floor division) operator is unusable, as its start a comment.
|
||||
///
|
||||
/// Everything else should work.
|
||||
///
|
||||
/// [`AsChunk`]: trait.AsChunk.html
|
||||
/// [`UserData`]: trait.UserData.html
|
||||
/// [`ToLua`]: trait.ToLua.html
|
||||
#[cfg(any(feature = "macros"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "macros")))]
|
||||
pub use mlua_derive::chunk;
|
||||
|
||||
#[cfg(any(feature = "module"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "module")))]
|
||||
pub use mlua_derive::lua_module;
|
||||
|
||||
+965
-383
File diff suppressed because it is too large
Load Diff
+3
-1
@@ -1,3 +1,5 @@
|
||||
#![allow(clippy::wrong_self_convention)]
|
||||
|
||||
use std::iter::FromIterator;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use std::result::Result as StdResult;
|
||||
@@ -34,7 +36,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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+6
-1
@@ -4,13 +4,18 @@ pub use crate::{
|
||||
AnyUserData as LuaAnyUserData, Chunk as LuaChunk, Error as LuaError,
|
||||
ExternalError as LuaExternalError, ExternalResult as LuaExternalResult, FromLua, FromLuaMulti,
|
||||
Function as LuaFunction, GCMode as LuaGCMode, Integer as LuaInteger,
|
||||
LightUserData as LuaLightUserData, Lua, MetaMethod as LuaMetaMethod,
|
||||
LightUserData as LuaLightUserData, Lua, LuaOptions, MetaMethod as LuaMetaMethod,
|
||||
MultiValue as LuaMultiValue, Nil as LuaNil, Number as LuaNumber, RegistryKey as LuaRegistryKey,
|
||||
Result as LuaResult, String as LuaString, Table as LuaTable, TableExt as LuaTableExt,
|
||||
TablePairs as LuaTablePairs, TableSequence as LuaTableSequence, Thread as LuaThread,
|
||||
ThreadStatus as LuaThreadStatus, ToLua, ToLuaMulti, UserData as LuaUserData,
|
||||
UserDataFields as LuaUserDataFields, UserDataMetatable as LuaUserDataMetatable,
|
||||
UserDataMethods as LuaUserDataMethods, Value as LuaValue,
|
||||
};
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
pub use crate::AsyncThread as LuaAsyncThread;
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
#[doc(inline)]
|
||||
pub use crate::{LuaSerdeExt, SerializeOptions as LuaSerializeOptions};
|
||||
|
||||
+329
-119
@@ -1,8 +1,9 @@
|
||||
use std::any::Any;
|
||||
use std::cell::{Cell, Ref, RefCell, RefMut};
|
||||
use std::cell::{Cell, RefCell};
|
||||
use std::marker::PhantomData;
|
||||
use std::mem;
|
||||
use std::os::raw::c_void;
|
||||
use std::os::raw::{c_int, c_void};
|
||||
use std::ptr;
|
||||
use std::rc::Rc;
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
@@ -12,20 +13,21 @@ use crate::error::{Error, Result};
|
||||
use crate::ffi;
|
||||
use crate::function::Function;
|
||||
use crate::lua::Lua;
|
||||
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,
|
||||
use crate::types::{Callback, LuaRef, MaybeSend};
|
||||
use crate::userdata::{
|
||||
AnyUserData, MetaMethod, UserData, UserDataCell, UserDataFields, UserDataMethods,
|
||||
};
|
||||
use crate::value::{FromLuaMulti, MultiValue, ToLuaMulti, Value};
|
||||
use crate::util::{
|
||||
assert_stack, check_stack, get_userdata, init_userdata_metatable, protect_lua, push_table,
|
||||
rawset_field, take_userdata, StackGuard,
|
||||
};
|
||||
use crate::value::{FromLua, FromLuaMulti, MultiValue, ToLua, 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
|
||||
@@ -34,13 +36,14 @@ use {
|
||||
/// See [`Lua::scope`] for more details.
|
||||
///
|
||||
/// [`Lua::scope`]: struct.Lua.html#method.scope
|
||||
#[allow(clippy::type_complexity)]
|
||||
pub struct Scope<'lua, 'scope> {
|
||||
lua: &'lua Lua,
|
||||
destructors: RefCell<Vec<(LuaRef<'lua>, fn(LuaRef<'lua>) -> Vec<Box<dyn Any>>)>>,
|
||||
destructors: RefCell<Vec<(LuaRef<'lua>, DestructorCallback<'lua>)>>,
|
||||
_scope_invariant: PhantomData<Cell<&'scope ()>>,
|
||||
}
|
||||
|
||||
type DestructorCallback<'lua> = Box<dyn Fn(LuaRef<'lua>) -> Vec<Box<dyn Any>> + 'lua>;
|
||||
|
||||
impl<'lua, 'scope> Scope<'lua, 'scope> {
|
||||
pub(crate) fn new(lua: &'lua Lua) -> Scope<'lua, 'scope> {
|
||||
Scope {
|
||||
@@ -53,7 +56,7 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
|
||||
/// Wraps a Rust function or closure, creating a callable Lua function handle to it.
|
||||
///
|
||||
/// This is a version of [`Lua::create_function`] that creates a callback which expires on
|
||||
/// scope drop. See [`Lua::scope`] for more details.
|
||||
/// scope drop. See [`Lua::scope`] for more details.
|
||||
///
|
||||
/// [`Lua::create_function`]: struct.Lua.html#method.create_function
|
||||
/// [`Lua::scope`]: struct.Lua.html#method.scope
|
||||
@@ -65,7 +68,7 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
|
||||
{
|
||||
// Safe, because 'scope must outlive 'callback (due to Self containing 'scope), however the
|
||||
// callback itself must be 'scope lifetime, so the function should not be able to capture
|
||||
// anything of 'callback lifetime. 'scope can't be shortened due to being invariant, and
|
||||
// anything of 'callback lifetime. 'scope can't be shortened due to being invariant, and
|
||||
// the 'callback lifetime here can't be enlarged due to coming from a universal
|
||||
// quantification in Lua::scope.
|
||||
//
|
||||
@@ -82,7 +85,7 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
|
||||
/// Wraps a Rust mutable closure, creating a callable Lua function handle to it.
|
||||
///
|
||||
/// This is a version of [`Lua::create_function_mut`] that creates a callback which expires
|
||||
/// on scope drop. See [`Lua::scope`] and [`Scope::create_function`] for more details.
|
||||
/// on scope drop. See [`Lua::scope`] and [`Scope::create_function`] for more details.
|
||||
///
|
||||
/// [`Lua::create_function_mut`]: struct.Lua.html#method.create_function_mut
|
||||
/// [`Lua::scope`]: struct.Lua.html#method.scope
|
||||
@@ -107,7 +110,7 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
|
||||
/// Wraps a Rust async function or closure, creating a callable Lua function handle to it.
|
||||
///
|
||||
/// This is a version of [`Lua::create_async_function`] that creates a callback which expires on
|
||||
/// scope drop. See [`Lua::scope`] and [`Lua::async_scope`] for more details.
|
||||
/// scope drop. See [`Lua::scope`] and [`Lua::async_scope`] for more details.
|
||||
///
|
||||
/// Requires `feature = "async"`
|
||||
///
|
||||
@@ -150,7 +153,7 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
|
||||
where
|
||||
T: 'static + UserData,
|
||||
{
|
||||
self.create_userdata_inner(UserDataWrapped::new(data))
|
||||
self.create_userdata_inner(UserDataCell::new(data))
|
||||
}
|
||||
|
||||
/// Create a Lua userdata object from a custom serializable userdata type.
|
||||
@@ -170,26 +173,43 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
|
||||
where
|
||||
T: 'static + UserData + Serialize,
|
||||
{
|
||||
self.create_userdata_inner(UserDataWrapped::new_ser(data))
|
||||
self.create_userdata_inner(UserDataCell::new_ser(data))
|
||||
}
|
||||
|
||||
fn create_userdata_inner<T>(&self, data: UserDataWrapped<T>) -> Result<AnyUserData<'lua>>
|
||||
fn create_userdata_inner<T>(&self, data: UserDataCell<T>) -> Result<AnyUserData<'lua>>
|
||||
where
|
||||
T: 'static + UserData,
|
||||
{
|
||||
// Safe even though T may not be Send, because the parent Lua cannot be sent to another
|
||||
// thread while the Scope is alive (or the returned AnyUserData handle even).
|
||||
unsafe {
|
||||
let u = self.lua.make_userdata(data)?;
|
||||
self.destructors.borrow_mut().push((u.0.clone(), |u| {
|
||||
let state = u.lua.state;
|
||||
let ud = self.lua.make_userdata(data)?;
|
||||
|
||||
#[cfg(any(feature = "lua51", feature = "luajit"))]
|
||||
let newtable = self.lua.create_table()?;
|
||||
let destructor: DestructorCallback = Box::new(move |ud| {
|
||||
let state = ud.lua.state;
|
||||
let _sg = StackGuard::new(state);
|
||||
assert_stack(state, 2);
|
||||
u.lua.push_ref(&u);
|
||||
// We know the destructor has not run yet because we hold a reference to the
|
||||
// userdata.
|
||||
|
||||
ud.lua.push_ref(&ud);
|
||||
|
||||
// We know the destructor has not run yet because we hold a reference to the userdata.
|
||||
|
||||
// Clear uservalue
|
||||
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
|
||||
ffi::lua_pushnil(state);
|
||||
#[cfg(any(feature = "lua51", feature = "luajit"))]
|
||||
ud.lua.push_ref(&newtable.0);
|
||||
ffi::lua_setuservalue(state, -2);
|
||||
|
||||
vec![Box::new(take_userdata::<UserDataCell<T>>(state))]
|
||||
}));
|
||||
Ok(u)
|
||||
});
|
||||
self.destructors
|
||||
.borrow_mut()
|
||||
.push((ud.0.clone(), destructor));
|
||||
|
||||
Ok(ud)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -205,10 +225,10 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
|
||||
///
|
||||
/// The main limitation that comes from using non-'static userdata is that the produced userdata
|
||||
/// will no longer have a `TypeId` associated with it, becuase `TypeId` can only work for
|
||||
/// 'static types. This means that it is impossible, once the userdata is created, to get a
|
||||
/// reference to it back *out* of an `AnyUserData` handle. This also implies that the
|
||||
/// 'static types. This means that it is impossible, once the userdata is created, to get a
|
||||
/// reference to it back *out* of an `AnyUserData` handle. This also implies that the
|
||||
/// "function" type methods that can be added via [`UserDataMethods`] (the ones that accept
|
||||
/// `AnyUserData` as a first parameter) are vastly less useful. Also, there is no way to re-use
|
||||
/// `AnyUserData` as a first parameter) are vastly less useful. Also, there is no way to re-use
|
||||
/// a single metatable for multiple non-'static types, so there is a higher cost associated with
|
||||
/// creating the userdata metatable each time a new userdata is created.
|
||||
///
|
||||
@@ -220,55 +240,45 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
|
||||
where
|
||||
T: 'scope + UserData,
|
||||
{
|
||||
let data = Rc::new(RefCell::new(UserDataWrapped::new(data)));
|
||||
let data = Rc::new(RefCell::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
|
||||
// 'lua. This is safe though, because `UserData::add_methods` does not get to pick the 'lua
|
||||
// 'lua. This is safe though, because `UserData::add_methods` does not get to pick the 'lua
|
||||
// lifetime, so none of the static methods UserData types can add can possibly capture
|
||||
// parameters.
|
||||
fn wrap_method<'scope, 'lua, 'callback: 'scope, T: 'scope>(
|
||||
scope: &Scope<'lua, 'scope>,
|
||||
data: Rc<UserDataCell<T>>,
|
||||
data: Rc<RefCell<T>>,
|
||||
data_ptr: *mut c_void,
|
||||
method: NonStaticMethod<'callback, T>,
|
||||
) -> Result<Function<'lua>> {
|
||||
// On methods that actually receive the userdata, we fake a type check on the passed in
|
||||
// userdata, where we pretend there is a unique type per call to
|
||||
// `Scope::create_nonstatic_userdata`. You can grab a method from a userdata and call
|
||||
// `Scope::create_nonstatic_userdata`. You can grab a method from a userdata and call
|
||||
// it on a mismatched userdata type, which when using normal 'static userdata will fail
|
||||
// with a type mismatch, but here without this check would proceed as though you had
|
||||
// called the method on the original value (since we otherwise completely ignore the
|
||||
// first argument).
|
||||
let check_data = data.clone();
|
||||
let check_ud_type = move |lua: &'callback Lua, value| {
|
||||
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);
|
||||
let _sg = StackGuard::new(lua.state);
|
||||
check_stack(lua.state, 3)?;
|
||||
lua.push_userdata_ref(&ud.0, false)?;
|
||||
if get_userdata(lua.state, -1) == data_ptr {
|
||||
return Ok(());
|
||||
}
|
||||
return ffi::lua_touserdata(lua.state, -1)
|
||||
== check_data.as_ptr() as *mut c_void;
|
||||
}
|
||||
}
|
||||
|
||||
false
|
||||
};
|
||||
Err(Error::UserDataTypeMismatch)
|
||||
};
|
||||
|
||||
match method {
|
||||
NonStaticMethod::Method(method) => {
|
||||
let f = Box::new(move |lua, mut args: MultiValue<'callback>| {
|
||||
if !check_ud_type(lua, args.pop_front()) {
|
||||
return Err(Error::UserDataTypeMismatch);
|
||||
}
|
||||
let data = data
|
||||
.try_borrow()
|
||||
.map(|cell| Ref::map(cell, AsRef::as_ref))
|
||||
.map_err(|_| Error::UserDataBorrowError)?;
|
||||
check_ud_type(lua, args.pop_front())?;
|
||||
let data = data.try_borrow().map_err(|_| Error::UserDataBorrowError)?;
|
||||
method(lua, &*data, args)
|
||||
});
|
||||
unsafe { scope.create_callback(f) }
|
||||
@@ -276,15 +286,12 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
|
||||
NonStaticMethod::MethodMut(method) => {
|
||||
let method = RefCell::new(method);
|
||||
let f = Box::new(move |lua, mut args: MultiValue<'callback>| {
|
||||
if !check_ud_type(lua, args.pop_front()) {
|
||||
return Err(Error::UserDataTypeMismatch);
|
||||
}
|
||||
check_ud_type(lua, args.pop_front())?;
|
||||
let mut method = method
|
||||
.try_borrow_mut()
|
||||
.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)
|
||||
});
|
||||
@@ -305,67 +312,136 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
|
||||
}
|
||||
}
|
||||
|
||||
let mut ud_fields = NonStaticUserDataFields::default();
|
||||
let mut ud_methods = NonStaticUserDataMethods::default();
|
||||
T::add_fields(&mut ud_fields);
|
||||
T::add_methods(&mut ud_methods);
|
||||
|
||||
unsafe {
|
||||
let lua = self.lua;
|
||||
let _sg = StackGuard::new(lua.state);
|
||||
assert_stack(lua.state, 6);
|
||||
check_stack(lua.state, 13)?;
|
||||
|
||||
push_userdata(lua.state, ())?;
|
||||
#[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"))]
|
||||
protect_lua_closure(lua.state, 0, 1, |state| {
|
||||
// Lua 5.2/5.1 allows to store only table. Then we will wrap the value.
|
||||
ffi::lua_createtable(state, 1, 0);
|
||||
ffi::lua_pushlightuserdata(state, data.as_ptr() as *mut c_void);
|
||||
ffi::lua_rawseti(state, -2, 1);
|
||||
})?;
|
||||
ffi::lua_setuservalue(lua.state, -2);
|
||||
|
||||
protect_lua_closure(lua.state, 0, 1, move |state| {
|
||||
ffi::lua_newtable(state);
|
||||
let data_ptr = protect_lua(lua.state, 0, 1, |state| {
|
||||
ffi::lua_newuserdata(state, mem::size_of::<UserDataCell<Rc<RefCell<T>>>>())
|
||||
})?;
|
||||
// Prepare metatable, add meta methods first and then meta fields
|
||||
let meta_methods_nrec = ud_methods.meta_methods.len() + ud_fields.meta_fields.len() + 1;
|
||||
push_table(lua.state, 0, meta_methods_nrec as c_int)?;
|
||||
|
||||
for (k, m) in ud_methods.meta_methods {
|
||||
push_string(lua.state, k.name())?;
|
||||
lua.push_value(Value::Function(wrap_method(self, data.clone(), m)?))?;
|
||||
let data = data.clone();
|
||||
lua.push_value(Value::Function(wrap_method(self, data, data_ptr, m)?))?;
|
||||
rawset_field(lua.state, -2, k.validate()?.name())?;
|
||||
}
|
||||
for (k, f) in ud_fields.meta_fields {
|
||||
lua.push_value(f(mem::transmute(lua))?)?;
|
||||
rawset_field(lua.state, -2, k.validate()?.name())?;
|
||||
}
|
||||
let metatable_index = ffi::lua_absindex(lua.state, -1);
|
||||
|
||||
protect_lua_closure(lua.state, 3, 1, |state| {
|
||||
ffi::lua_rawset(state, -3);
|
||||
})?;
|
||||
let mut field_getters_index = None;
|
||||
let field_getters_nrec = ud_fields.field_getters.len();
|
||||
if field_getters_nrec > 0 {
|
||||
push_table(lua.state, 0, field_getters_nrec as c_int)?;
|
||||
for (k, m) in ud_fields.field_getters {
|
||||
let data = data.clone();
|
||||
lua.push_value(Value::Function(wrap_method(self, data, data_ptr, m)?))?;
|
||||
rawset_field(lua.state, -2, &k)?;
|
||||
}
|
||||
field_getters_index = Some(ffi::lua_absindex(lua.state, -1));
|
||||
}
|
||||
|
||||
if ud_methods.methods.is_empty() {
|
||||
init_userdata_metatable::<()>(lua.state, -1, None)?;
|
||||
} else {
|
||||
protect_lua_closure(lua.state, 0, 1, |state| {
|
||||
ffi::lua_newtable(state);
|
||||
})?;
|
||||
let mut field_setters_index = None;
|
||||
let field_setters_nrec = ud_fields.field_setters.len();
|
||||
if field_setters_nrec > 0 {
|
||||
push_table(lua.state, 0, field_setters_nrec as c_int)?;
|
||||
for (k, m) in ud_fields.field_setters {
|
||||
let data = data.clone();
|
||||
lua.push_value(Value::Function(wrap_method(self, data, data_ptr, m)?))?;
|
||||
rawset_field(lua.state, -2, &k)?;
|
||||
}
|
||||
field_setters_index = Some(ffi::lua_absindex(lua.state, -1));
|
||||
}
|
||||
|
||||
let mut methods_index = None;
|
||||
let methods_nrec = ud_methods.methods.len();
|
||||
if methods_nrec > 0 {
|
||||
// Create table used for methods lookup
|
||||
push_table(lua.state, 0, methods_nrec as c_int)?;
|
||||
for (k, m) in ud_methods.methods {
|
||||
push_string(lua.state, &k)?;
|
||||
lua.push_value(Value::Function(wrap_method(self, data.clone(), m)?))?;
|
||||
protect_lua_closure(lua.state, 3, 1, |state| {
|
||||
ffi::lua_rawset(state, -3);
|
||||
})?;
|
||||
let data = data.clone();
|
||||
lua.push_value(Value::Function(wrap_method(self, data, data_ptr, m)?))?;
|
||||
rawset_field(lua.state, -2, &k)?;
|
||||
}
|
||||
methods_index = Some(ffi::lua_absindex(lua.state, -1));
|
||||
}
|
||||
|
||||
init_userdata_metatable::<UserDataCell<Rc<RefCell<T>>>>(
|
||||
lua.state,
|
||||
metatable_index,
|
||||
field_getters_index,
|
||||
field_setters_index,
|
||||
methods_index,
|
||||
)?;
|
||||
|
||||
let count = field_getters_index.map(|_| 1).unwrap_or(0)
|
||||
+ field_setters_index.map(|_| 1).unwrap_or(0)
|
||||
+ methods_index.map(|_| 1).unwrap_or(0);
|
||||
ffi::lua_pop(lua.state, count);
|
||||
|
||||
let mt_id = ffi::lua_topointer(lua.state, -1);
|
||||
// Write userdata just before attaching metatable with `__gc` metamethod
|
||||
ptr::write(data_ptr as _, UserDataCell::new(data));
|
||||
ffi::lua_setmetatable(lua.state, -2);
|
||||
let ud = AnyUserData(lua.pop_ref());
|
||||
lua.register_userdata_metatable(mt_id as isize);
|
||||
|
||||
#[cfg(any(feature = "lua51", feature = "luajit"))]
|
||||
let newtable = lua.create_table()?;
|
||||
let destructor: DestructorCallback = Box::new(move |ud| {
|
||||
let state = ud.lua.state;
|
||||
let _sg = StackGuard::new(state);
|
||||
assert_stack(state, 2);
|
||||
|
||||
ud.lua.push_ref(&ud);
|
||||
|
||||
// We know the destructor has not run yet because we hold a reference to the userdata.
|
||||
|
||||
// Deregister metatable
|
||||
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);
|
||||
|
||||
// Clear uservalue
|
||||
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
|
||||
ffi::lua_pushnil(state);
|
||||
#[cfg(any(feature = "lua51", feature = "luajit"))]
|
||||
ud.lua.push_ref(&newtable.0);
|
||||
ffi::lua_setuservalue(state, -2);
|
||||
|
||||
// A hack to drop non-static `T`
|
||||
unsafe fn seal<T>(t: T) -> Box<dyn FnOnce() + 'static> {
|
||||
let f: Box<dyn FnOnce()> = Box::new(move || drop(t));
|
||||
mem::transmute(f)
|
||||
}
|
||||
|
||||
init_userdata_metatable::<()>(lua.state, -2, Some(-1))?;
|
||||
ffi::lua_pop(lua.state, 1);
|
||||
}
|
||||
let ud = Box::new(seal(take_userdata::<UserDataCell<Rc<RefCell<T>>>>(state)));
|
||||
vec![ud]
|
||||
});
|
||||
self.destructors
|
||||
.borrow_mut()
|
||||
.push((ud.0.clone(), destructor));
|
||||
|
||||
ffi::lua_setmetatable(lua.state, -2);
|
||||
|
||||
Ok(AnyUserData(lua.pop_ref()))
|
||||
Ok(ud)
|
||||
}
|
||||
}
|
||||
|
||||
// Unsafe, because the callback can improperly capture any value with 'callback scope, such as
|
||||
// improperly capturing an argument. Since the 'callback lifetime is chosen by the user and the
|
||||
// lifetime of the callback itself is 'scope (non-'static), the borrow checker will happily pick
|
||||
// a 'callback that outlives 'scope to allow this. In order for this to be safe, the callback
|
||||
// a 'callback that outlives 'scope to allow this. In order for this to be safe, the callback
|
||||
// must NOT capture any parameters.
|
||||
unsafe fn create_callback<'callback>(
|
||||
&self,
|
||||
@@ -374,10 +450,11 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
|
||||
let f = mem::transmute::<Callback<'callback, 'scope>, Callback<'lua, 'static>>(f);
|
||||
let f = self.lua.create_callback(f)?;
|
||||
|
||||
let mut destructors = self.destructors.borrow_mut();
|
||||
destructors.push((f.0.clone(), |f| {
|
||||
let destructor: DestructorCallback = Box::new(|f| {
|
||||
let state = f.lua.state;
|
||||
let _sg = StackGuard::new(state);
|
||||
assert_stack(state, 3);
|
||||
|
||||
f.lua.push_ref(&f);
|
||||
|
||||
// We know the destructor has not run yet because we hold a reference to the callback.
|
||||
@@ -392,9 +469,12 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
|
||||
ffi::lua_pushnil(state);
|
||||
ffi::lua_setupvalue(state, -2, 2);
|
||||
|
||||
ffi::lua_pop(state, 1);
|
||||
vec![Box::new(ud1), Box::new(ud2)]
|
||||
}));
|
||||
});
|
||||
self.destructors
|
||||
.borrow_mut()
|
||||
.push((f.0.clone(), destructor));
|
||||
|
||||
Ok(f)
|
||||
}
|
||||
|
||||
@@ -406,10 +486,14 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
|
||||
let f = mem::transmute::<AsyncCallback<'callback, 'scope>, AsyncCallback<'lua, 'static>>(f);
|
||||
let f = self.lua.create_async_callback(f)?;
|
||||
|
||||
let mut destructors = self.destructors.borrow_mut();
|
||||
destructors.push((f.0.clone(), |f| {
|
||||
// We need to pre-allocate strings to avoid failures in destructor.
|
||||
let get_poll_str = self.lua.create_string("get_poll")?;
|
||||
let poll_str = self.lua.create_string("poll")?;
|
||||
let destructor: DestructorCallback = Box::new(move |f| {
|
||||
let state = f.lua.state;
|
||||
assert_stack(state, 4);
|
||||
let _sg = StackGuard::new(state);
|
||||
assert_stack(state, 5);
|
||||
|
||||
f.lua.push_ref(&f);
|
||||
|
||||
// We know the destructor has not run yet because we hold a reference to the callback.
|
||||
@@ -420,12 +504,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
|
||||
f.lua.push_ref(&get_poll_str.0);
|
||||
ffi::lua_rawget(state, -2);
|
||||
|
||||
// Finally, destroy all upvalues
|
||||
// Destroy all upvalues
|
||||
ffi::lua_getupvalue(state, -1, 1);
|
||||
let ud1 = take_userdata::<AsyncCallback>(state);
|
||||
ffi::lua_pushnil(state);
|
||||
@@ -437,9 +520,29 @@ 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
|
||||
f.lua.push_ref(&poll_str.0);
|
||||
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
|
||||
});
|
||||
self.destructors
|
||||
.borrow_mut()
|
||||
.push((f.0.clone(), destructor));
|
||||
|
||||
Ok(f)
|
||||
}
|
||||
@@ -448,7 +551,7 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
|
||||
impl<'lua, 'scope> Drop for Scope<'lua, 'scope> {
|
||||
fn drop(&mut self) {
|
||||
// We separate the action of invalidating the userdata in Lua and actually dropping the
|
||||
// userdata type into two phases. This is so that, in the event a userdata drop panics, we
|
||||
// userdata type into two phases. This is so that, in the event a userdata drop panics, we
|
||||
// can be sure that all of the userdata in Lua is actually invalidated.
|
||||
|
||||
// All destructors are non-panicking, so this is fine
|
||||
@@ -563,7 +666,6 @@ impl<'lua, T: UserData> UserDataMethods<'lua, T> for NonStaticUserDataMethods<'l
|
||||
#[cfg(feature = "async")]
|
||||
fn add_async_function<S, A, R, F, FR>(&mut self, _name: &S, _function: F)
|
||||
where
|
||||
T: Clone,
|
||||
S: AsRef<[u8]> + ?Sized,
|
||||
A: FromLuaMulti<'lua>,
|
||||
R: ToLuaMulti<'lua>,
|
||||
@@ -575,59 +677,167 @@ impl<'lua, T: UserData> UserDataMethods<'lua, T> for NonStaticUserDataMethods<'l
|
||||
mlua_panic!("asynchronous functions are not supported for non-static userdata")
|
||||
}
|
||||
|
||||
fn add_meta_method<A, R, M>(&mut self, meta: MetaMethod, method: M)
|
||||
fn add_meta_method<S, A, R, M>(&mut self, meta: S, method: M)
|
||||
where
|
||||
S: Into<MetaMethod>,
|
||||
A: FromLuaMulti<'lua>,
|
||||
R: ToLuaMulti<'lua>,
|
||||
M: 'static + MaybeSend + Fn(&'lua Lua, &T, A) -> Result<R>,
|
||||
{
|
||||
self.meta_methods.push((
|
||||
meta,
|
||||
meta.into(),
|
||||
NonStaticMethod::Method(Box::new(move |lua, ud, args| {
|
||||
method(lua, ud, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
|
||||
})),
|
||||
));
|
||||
}
|
||||
|
||||
fn add_meta_method_mut<A, R, M>(&mut self, meta: MetaMethod, mut method: M)
|
||||
fn add_meta_method_mut<S, A, R, M>(&mut self, meta: S, mut method: M)
|
||||
where
|
||||
S: Into<MetaMethod>,
|
||||
A: FromLuaMulti<'lua>,
|
||||
R: ToLuaMulti<'lua>,
|
||||
M: 'static + MaybeSend + FnMut(&'lua Lua, &mut T, A) -> Result<R>,
|
||||
{
|
||||
self.meta_methods.push((
|
||||
meta,
|
||||
meta.into(),
|
||||
NonStaticMethod::MethodMut(Box::new(move |lua, ud, args| {
|
||||
method(lua, ud, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
|
||||
})),
|
||||
));
|
||||
}
|
||||
|
||||
fn add_meta_function<A, R, F>(&mut self, meta: MetaMethod, function: F)
|
||||
fn add_meta_function<S, A, R, F>(&mut self, meta: S, function: F)
|
||||
where
|
||||
S: Into<MetaMethod>,
|
||||
A: FromLuaMulti<'lua>,
|
||||
R: ToLuaMulti<'lua>,
|
||||
F: 'static + MaybeSend + Fn(&'lua Lua, A) -> Result<R>,
|
||||
{
|
||||
self.meta_methods.push((
|
||||
meta,
|
||||
meta.into(),
|
||||
NonStaticMethod::Function(Box::new(move |lua, args| {
|
||||
function(lua, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
|
||||
})),
|
||||
));
|
||||
}
|
||||
|
||||
fn add_meta_function_mut<A, R, F>(&mut self, meta: MetaMethod, mut function: F)
|
||||
fn add_meta_function_mut<S, A, R, F>(&mut self, meta: S, mut function: F)
|
||||
where
|
||||
S: Into<MetaMethod>,
|
||||
A: FromLuaMulti<'lua>,
|
||||
R: ToLuaMulti<'lua>,
|
||||
F: 'static + MaybeSend + FnMut(&'lua Lua, A) -> Result<R>,
|
||||
{
|
||||
self.meta_methods.push((
|
||||
meta,
|
||||
meta.into(),
|
||||
NonStaticMethod::FunctionMut(Box::new(move |lua, args| {
|
||||
function(lua, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
|
||||
})),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
struct NonStaticUserDataFields<'lua, T: UserData> {
|
||||
field_getters: Vec<(Vec<u8>, NonStaticMethod<'lua, T>)>,
|
||||
field_setters: Vec<(Vec<u8>, NonStaticMethod<'lua, T>)>,
|
||||
#[allow(clippy::type_complexity)]
|
||||
meta_fields: Vec<(MetaMethod, Box<dyn Fn(&'lua Lua) -> Result<Value<'lua>>>)>,
|
||||
}
|
||||
|
||||
impl<'lua, T: UserData> Default for NonStaticUserDataFields<'lua, T> {
|
||||
fn default() -> NonStaticUserDataFields<'lua, T> {
|
||||
NonStaticUserDataFields {
|
||||
field_getters: Vec::new(),
|
||||
field_setters: Vec::new(),
|
||||
meta_fields: Vec::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua, T: UserData> UserDataFields<'lua, T> for NonStaticUserDataFields<'lua, T> {
|
||||
fn add_field_method_get<S, R, M>(&mut self, name: &S, method: M)
|
||||
where
|
||||
S: AsRef<[u8]> + ?Sized,
|
||||
R: ToLua<'lua>,
|
||||
M: 'static + MaybeSend + Fn(&'lua Lua, &T) -> Result<R>,
|
||||
{
|
||||
self.field_getters.push((
|
||||
name.as_ref().to_vec(),
|
||||
NonStaticMethod::Method(Box::new(move |lua, ud, _| {
|
||||
method(lua, ud)?.to_lua_multi(lua)
|
||||
})),
|
||||
));
|
||||
}
|
||||
|
||||
fn add_field_method_set<S, A, M>(&mut self, name: &S, mut method: M)
|
||||
where
|
||||
S: AsRef<[u8]> + ?Sized,
|
||||
A: FromLua<'lua>,
|
||||
M: 'static + MaybeSend + FnMut(&'lua Lua, &mut T, A) -> Result<()>,
|
||||
{
|
||||
self.field_setters.push((
|
||||
name.as_ref().to_vec(),
|
||||
NonStaticMethod::MethodMut(Box::new(move |lua, ud, args| {
|
||||
method(lua, ud, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
|
||||
})),
|
||||
));
|
||||
}
|
||||
|
||||
fn add_field_function_get<S, R, F>(&mut self, name: &S, function: F)
|
||||
where
|
||||
S: AsRef<[u8]> + ?Sized,
|
||||
R: ToLua<'lua>,
|
||||
F: 'static + MaybeSend + Fn(&'lua Lua, AnyUserData<'lua>) -> Result<R>,
|
||||
{
|
||||
self.field_getters.push((
|
||||
name.as_ref().to_vec(),
|
||||
NonStaticMethod::Function(Box::new(move |lua, args| {
|
||||
function(lua, AnyUserData::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
|
||||
})),
|
||||
));
|
||||
}
|
||||
|
||||
fn add_field_function_set<S, A, F>(&mut self, name: &S, mut function: F)
|
||||
where
|
||||
S: AsRef<[u8]> + ?Sized,
|
||||
A: FromLua<'lua>,
|
||||
F: 'static + MaybeSend + FnMut(&'lua Lua, AnyUserData<'lua>, A) -> Result<()>,
|
||||
{
|
||||
self.field_setters.push((
|
||||
name.as_ref().to_vec(),
|
||||
NonStaticMethod::FunctionMut(Box::new(move |lua, args| {
|
||||
let (ud, val) = <_>::from_lua_multi(args, lua)?;
|
||||
function(lua, ud, val)?.to_lua_multi(lua)
|
||||
})),
|
||||
));
|
||||
}
|
||||
|
||||
fn add_meta_field_with<S, R, F>(&mut self, meta: S, f: F)
|
||||
where
|
||||
S: Into<MetaMethod>,
|
||||
F: 'static + MaybeSend + Fn(&'lua Lua) -> Result<R>,
|
||||
R: ToLua<'lua>,
|
||||
{
|
||||
let meta = meta.into();
|
||||
self.meta_fields.push((
|
||||
meta.clone(),
|
||||
Box::new(move |lua| {
|
||||
let value = f(lua)?.to_lua(lua)?;
|
||||
if meta == MetaMethod::Index || meta == MetaMethod::NewIndex {
|
||||
match value {
|
||||
Value::Nil | Value::Table(_) | Value::Function(_) => {}
|
||||
_ => {
|
||||
return Err(Error::MetaMethodTypeError {
|
||||
method: meta.to_string(),
|
||||
type_name: value.type_name(),
|
||||
message: Some("expected nil, table or function".to_string()),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(value)
|
||||
}),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
+13
-3
@@ -7,7 +7,15 @@ 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>);
|
||||
#[derive(Debug)]
|
||||
pub struct Deserializer<'lua>(Value<'lua>);
|
||||
|
||||
impl<'lua> Deserializer<'lua> {
|
||||
/// Creates a new Lua Deserializer for the `Value`.
|
||||
pub fn new(value: Value<'lua>) -> Self {
|
||||
Deserializer(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua, 'de> serde::Deserializer<'de> for Deserializer<'lua> {
|
||||
type Error = Error;
|
||||
@@ -20,8 +28,10 @@ impl<'lua, 'de> serde::Deserializer<'de> for Deserializer<'lua> {
|
||||
match self.0 {
|
||||
Value::Nil => visitor.visit_unit(),
|
||||
Value::Boolean(b) => visitor.visit_bool(b),
|
||||
Value::Integer(i) => visitor.visit_i64(i),
|
||||
Value::Number(n) => visitor.visit_f64(n),
|
||||
#[allow(clippy::useless_conversion)]
|
||||
Value::Integer(i) => visitor.visit_i64(i.into()),
|
||||
#[allow(clippy::useless_conversion)]
|
||||
Value::Number(n) => visitor.visit_f64(n.into()),
|
||||
Value::String(s) => match s.to_str() {
|
||||
Ok(s) => visitor.visit_str(s),
|
||||
Err(_) => visitor.visit_bytes(s.as_bytes()),
|
||||
|
||||
+69
-40
@@ -1,6 +1,6 @@
|
||||
//! (De)Serialization support using serde.
|
||||
|
||||
use std::os::raw::{c_int, c_void};
|
||||
use std::os::raw::c_void;
|
||||
use std::ptr;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
@@ -9,9 +9,11 @@ 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::types::LightUserData;
|
||||
use crate::util::{assert_stack, check_stack, protect_lua, StackGuard};
|
||||
use crate::value::Value;
|
||||
|
||||
/// Trait for serializing/deserializing Lua values using Serde.
|
||||
pub trait LuaSerdeExt<'lua> {
|
||||
/// A special value (lightuserdata) to encode/decode optional (none) values.
|
||||
///
|
||||
@@ -25,7 +27,7 @@ pub trait LuaSerdeExt<'lua> {
|
||||
///
|
||||
/// fn main() -> Result<()> {
|
||||
/// let lua = Lua::new();
|
||||
/// lua.globals().set("null", lua.null()?)?;
|
||||
/// lua.globals().set("null", lua.null())?;
|
||||
///
|
||||
/// let val = lua.load(r#"{a = null}"#).eval()?;
|
||||
/// let map: HashMap<String, Option<String>> = lua.from_value(val)?;
|
||||
@@ -34,7 +36,7 @@ pub trait LuaSerdeExt<'lua> {
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
fn null(&'lua self) -> Result<Value<'lua>>;
|
||||
fn null(&'lua self) -> 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
|
||||
@@ -50,7 +52,7 @@ pub trait LuaSerdeExt<'lua> {
|
||||
///
|
||||
/// fn main() -> Result<()> {
|
||||
/// let lua = Lua::new();
|
||||
/// lua.globals().set("array_mt", lua.array_metatable()?)?;
|
||||
/// 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()?;
|
||||
@@ -65,7 +67,7 @@ pub trait LuaSerdeExt<'lua> {
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
fn array_metatable(&'lua self) -> Result<Table<'lua>>;
|
||||
fn array_metatable(&'lua self) -> Table<'lua>;
|
||||
|
||||
/// Converts `T` into a `Value` instance.
|
||||
///
|
||||
@@ -100,6 +102,33 @@ pub trait LuaSerdeExt<'lua> {
|
||||
/// ```
|
||||
fn to_value<T: Serialize + ?Sized>(&'lua self, t: &T) -> Result<Value<'lua>>;
|
||||
|
||||
/// Converts `T` into a `Value` instance with options.
|
||||
///
|
||||
/// Requires `feature = "serialize"`
|
||||
///
|
||||
/// [`Value`]: enum.Value.html
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// use mlua::{Lua, Result, LuaSerdeExt, SerializeOptions};
|
||||
///
|
||||
/// fn main() -> Result<()> {
|
||||
/// let lua = Lua::new();
|
||||
/// let v = vec![1, 2, 3];
|
||||
/// let options = SerializeOptions::new().set_array_metatable(false);
|
||||
/// lua.globals().set("v", lua.to_value_with(&v, options)?)?;
|
||||
///
|
||||
/// lua.load(r#"
|
||||
/// assert(#v == 3 and v[1] == 1 and v[2] == 2 and v[3] == 3)
|
||||
/// assert(getmetatable(v) == nil)
|
||||
/// "#).exec()
|
||||
/// }
|
||||
/// ```
|
||||
fn to_value_with<T>(&'lua self, t: &T, options: ser::Options) -> Result<Value<'lua>>
|
||||
where
|
||||
T: Serialize + ?Sized;
|
||||
|
||||
/// Deserializes a `Value` into any serde deserializable object.
|
||||
///
|
||||
/// Requires `feature = "serialize"`
|
||||
@@ -132,31 +161,18 @@ pub trait LuaSerdeExt<'lua> {
|
||||
}
|
||||
|
||||
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 null(&'lua self) -> Value<'lua> {
|
||||
Value::LightUserData(LightUserData(ptr::null_mut()))
|
||||
}
|
||||
|
||||
fn array_metatable(&'lua self) -> Result<Table<'lua>> {
|
||||
fn array_metatable(&'lua self) -> Table<'lua> {
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(self.state);
|
||||
assert_stack(self.state, 3);
|
||||
assert_stack(self.state, 1);
|
||||
|
||||
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()))
|
||||
push_array_metatable(self.state);
|
||||
|
||||
Table(self.pop_ref())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -164,37 +180,50 @@ impl<'lua> LuaSerdeExt<'lua> for Lua {
|
||||
where
|
||||
T: Serialize + ?Sized,
|
||||
{
|
||||
t.serialize(ser::Serializer(self))
|
||||
t.serialize(ser::Serializer::new(self))
|
||||
}
|
||||
|
||||
fn to_value_with<T>(&'lua self, t: &T, options: ser::Options) -> Result<Value<'lua>>
|
||||
where
|
||||
T: Serialize + ?Sized,
|
||||
{
|
||||
t.serialize(ser::Serializer::new_with_options(self, options))
|
||||
}
|
||||
|
||||
fn from_value<T>(&'lua self, value: Value<'lua>) -> Result<T>
|
||||
where
|
||||
T: Deserialize<'lua>,
|
||||
{
|
||||
T::deserialize(de::Deserializer(value))
|
||||
T::deserialize(de::Deserializer::new(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);
|
||||
// Uses 6 stack spaces and calls checkstack.
|
||||
pub(crate) unsafe fn init_metatables(state: *mut ffi::lua_State) -> Result<()> {
|
||||
check_stack(state, 3)?;
|
||||
protect_lua(state, 0, 0, |state| {
|
||||
ffi::lua_createtable(state, 0, 1);
|
||||
|
||||
ffi::lua_pushstring(state, cstr!("__metatable"));
|
||||
ffi::lua_pushboolean(state, 0);
|
||||
ffi::lua_rawset(state, -3);
|
||||
ffi::lua_pushstring(state, cstr!("__metatable"));
|
||||
ffi::lua_pushboolean(state, 0);
|
||||
ffi::lua_rawset(state, -3);
|
||||
|
||||
ffi::lua_rawset(state, ffi::LUA_REGISTRYINDEX);
|
||||
let array_metatable_key = &ARRAY_METATABLE_REGISTRY_KEY as *const u8 as *const c_void;
|
||||
ffi::lua_rawsetp(state, ffi::LUA_REGISTRYINDEX, array_metatable_key);
|
||||
})
|
||||
}
|
||||
|
||||
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);
|
||||
let array_metatable_key = &ARRAY_METATABLE_REGISTRY_KEY as *const u8 as *mut c_void;
|
||||
ffi::lua_rawgetp(state, ffi::LUA_REGISTRYINDEX, array_metatable_key);
|
||||
}
|
||||
|
||||
static ARRAY_METATABLE_REGISTRY_KEY: u8 = 0;
|
||||
|
||||
pub mod de;
|
||||
pub mod ser;
|
||||
|
||||
#[doc(inline)]
|
||||
pub use de::Deserializer;
|
||||
#[doc(inline)]
|
||||
pub use ser::Serializer;
|
||||
|
||||
+172
-69
@@ -8,18 +8,106 @@ use crate::ffi;
|
||||
use crate::lua::Lua;
|
||||
use crate::string::String;
|
||||
use crate::table::Table;
|
||||
use crate::types::{Integer, Number};
|
||||
use crate::util::{assert_stack, protect_lua, StackGuard};
|
||||
use crate::value::Value;
|
||||
use crate::types::Integer;
|
||||
use crate::util::{check_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);
|
||||
#[derive(Debug)]
|
||||
pub struct Serializer<'lua> {
|
||||
lua: &'lua Lua,
|
||||
options: Options,
|
||||
}
|
||||
|
||||
macro_rules! lua_serialize_integer {
|
||||
/// A struct with options to change default serializer behavior.
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
#[non_exhaustive]
|
||||
pub struct Options {
|
||||
/// If true, sequence serialization to a Lua table will create table
|
||||
/// with the [`array_metatable`] attached.
|
||||
///
|
||||
/// Default: **true**
|
||||
///
|
||||
/// [`array_metatable`]: ../trait.LuaSerdeExt.html#tymethod.array_metatable
|
||||
pub set_array_metatable: bool,
|
||||
|
||||
/// If true, serialize `None` (part of `Option` type) to [`null`].
|
||||
/// Otherwise it will be set to Lua [`Nil`].
|
||||
///
|
||||
/// Default: **true**
|
||||
///
|
||||
/// [`null`]: ../trait.LuaSerdeExt.html#tymethod.null
|
||||
/// [`Nil`]: ../../enum.Value.html#variant.Nil
|
||||
pub serialize_none_to_null: bool,
|
||||
|
||||
/// If true, serialize `Unit` (type of `()` in Rust) and Unit structs to [`null`].
|
||||
/// Otherwise it will be set to Lua [`Nil`].
|
||||
///
|
||||
/// Default: **true**
|
||||
///
|
||||
/// [`null`]: ../trait.LuaSerdeExt.html#tymethod.null
|
||||
/// [`Nil`]: ../../enum.Value.html#variant.Nil
|
||||
pub serialize_unit_to_null: bool,
|
||||
}
|
||||
|
||||
impl Default for Options {
|
||||
fn default() -> Self {
|
||||
Options {
|
||||
set_array_metatable: true,
|
||||
serialize_none_to_null: true,
|
||||
serialize_unit_to_null: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Options {
|
||||
/// Retruns a new instance of `Options` with default parameters.
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
/// Sets [`set_array_metatable`] option.
|
||||
///
|
||||
/// [`set_array_metatable`]: #structfield.set_array_metatable
|
||||
pub fn set_array_metatable(mut self, enabled: bool) -> Self {
|
||||
self.set_array_metatable = enabled;
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets [`serialize_none_to_null`] option.
|
||||
///
|
||||
/// [`serialize_none_to_null`]: #structfield.serialize_none_to_null
|
||||
pub fn serialize_none_to_null(mut self, enabled: bool) -> Self {
|
||||
self.serialize_none_to_null = enabled;
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets [`serialize_unit_to_null`] option.
|
||||
///
|
||||
/// [`serialize_unit_to_null`]: #structfield.serialize_unit_to_null
|
||||
pub fn serialize_unit_to_null(mut self, enabled: bool) -> Self {
|
||||
self.serialize_unit_to_null = enabled;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> Serializer<'lua> {
|
||||
/// Creates a new Lua Serializer with default options.
|
||||
pub fn new(lua: &'lua Lua) -> Self {
|
||||
Self::new_with_options(lua, Options::default())
|
||||
}
|
||||
|
||||
/// Creates a new Lua Serializer with custom options.
|
||||
pub fn new_with_options(lua: &'lua Lua, options: Options) -> Self {
|
||||
Serializer { lua, options }
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! lua_serialize_number {
|
||||
($name:ident, $t:ty) => {
|
||||
#[inline]
|
||||
fn $name(self, value: $t) -> Result<Value<'lua>> {
|
||||
Ok(Value::Integer(value as Integer))
|
||||
value.to_lua(self.lua)
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -43,28 +131,17 @@ impl<'lua> ser::Serializer for Serializer<'lua> {
|
||||
Ok(Value::Boolean(value))
|
||||
}
|
||||
|
||||
lua_serialize_integer!(serialize_i8, i8);
|
||||
lua_serialize_integer!(serialize_u8, u8);
|
||||
lua_serialize_integer!(serialize_i16, i16);
|
||||
lua_serialize_integer!(serialize_u16, u16);
|
||||
lua_serialize_integer!(serialize_i32, i32);
|
||||
lua_serialize_integer!(serialize_u32, u32);
|
||||
lua_serialize_integer!(serialize_u64, u64);
|
||||
lua_serialize_number!(serialize_i8, i8);
|
||||
lua_serialize_number!(serialize_u8, u8);
|
||||
lua_serialize_number!(serialize_i16, i16);
|
||||
lua_serialize_number!(serialize_u16, u16);
|
||||
lua_serialize_number!(serialize_i32, i32);
|
||||
lua_serialize_number!(serialize_u32, u32);
|
||||
lua_serialize_number!(serialize_i64, i64);
|
||||
lua_serialize_number!(serialize_u64, u64);
|
||||
|
||||
#[inline]
|
||||
fn serialize_i64(self, value: i64) -> Result<Value<'lua>> {
|
||||
Ok(Value::Integer(value))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn serialize_f32(self, value: f32) -> Result<Value<'lua>> {
|
||||
Ok(Value::Number(value as Number))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn serialize_f64(self, value: f64) -> Result<Value<'lua>> {
|
||||
Ok(Value::Number(value))
|
||||
}
|
||||
lua_serialize_number!(serialize_f32, f32);
|
||||
lua_serialize_number!(serialize_f64, f64);
|
||||
|
||||
#[inline]
|
||||
fn serialize_char(self, value: char) -> Result<Value<'lua>> {
|
||||
@@ -73,35 +150,47 @@ impl<'lua> ser::Serializer for Serializer<'lua> {
|
||||
|
||||
#[inline]
|
||||
fn serialize_str(self, value: &str) -> Result<Value<'lua>> {
|
||||
self.0.create_string(value).map(Value::String)
|
||||
self.lua.create_string(value).map(Value::String)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn serialize_bytes(self, value: &[u8]) -> Result<Value<'lua>> {
|
||||
self.0.create_string(value).map(Value::String)
|
||||
self.lua.create_string(value).map(Value::String)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn serialize_none(self) -> Result<Value<'lua>> {
|
||||
self.0.null()
|
||||
if self.options.serialize_none_to_null {
|
||||
Ok(self.lua.null())
|
||||
} else {
|
||||
Ok(Value::Nil)
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn serialize_some<T>(self, value: &T) -> Result<Value<'lua>>
|
||||
where
|
||||
T: ?Sized + Serialize,
|
||||
T: Serialize + ?Sized,
|
||||
{
|
||||
value.serialize(self)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn serialize_unit(self) -> Result<Value<'lua>> {
|
||||
self.0.null()
|
||||
if self.options.serialize_unit_to_null {
|
||||
Ok(self.lua.null())
|
||||
} else {
|
||||
Ok(Value::Nil)
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn serialize_unit_struct(self, _name: &'static str) -> Result<Value<'lua>> {
|
||||
self.0.null()
|
||||
if self.options.serialize_unit_to_null {
|
||||
Ok(self.lua.null())
|
||||
} else {
|
||||
Ok(Value::Nil)
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@@ -117,7 +206,7 @@ impl<'lua> ser::Serializer for Serializer<'lua> {
|
||||
#[inline]
|
||||
fn serialize_newtype_struct<T>(self, _name: &'static str, value: &T) -> Result<Value<'lua>>
|
||||
where
|
||||
T: ?Sized + Serialize,
|
||||
T: Serialize + ?Sized,
|
||||
{
|
||||
value.serialize(self)
|
||||
}
|
||||
@@ -131,11 +220,11 @@ impl<'lua> ser::Serializer for Serializer<'lua> {
|
||||
value: &T,
|
||||
) -> Result<Value<'lua>>
|
||||
where
|
||||
T: ?Sized + Serialize,
|
||||
T: Serialize + ?Sized,
|
||||
{
|
||||
let table = self.0.create_table()?;
|
||||
let variant = self.0.create_string(variant)?;
|
||||
let value = self.0.to_value(value)?;
|
||||
let table = self.lua.create_table()?;
|
||||
let variant = self.lua.create_string(variant)?;
|
||||
let value = self.lua.to_value_with(value, self.options)?;
|
||||
table.raw_set(variant, value)?;
|
||||
Ok(Value::Table(table))
|
||||
}
|
||||
@@ -143,9 +232,12 @@ impl<'lua> ser::Serializer for Serializer<'lua> {
|
||||
#[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 })
|
||||
let table = self.lua.create_table_with_capacity(len, 0)?;
|
||||
if self.options.set_array_metatable {
|
||||
table.set_metatable(Some(self.lua.array_metatable()));
|
||||
}
|
||||
let options = self.options;
|
||||
Ok(SerializeVec { table, options })
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@@ -170,9 +262,11 @@ impl<'lua> ser::Serializer for Serializer<'lua> {
|
||||
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 })
|
||||
Ok(SerializeTupleVariant {
|
||||
name: self.lua.create_string(variant)?,
|
||||
table: self.lua.create_table()?,
|
||||
options: self.options,
|
||||
})
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@@ -180,7 +274,8 @@ impl<'lua> ser::Serializer for Serializer<'lua> {
|
||||
let len = len.unwrap_or(0) as c_int;
|
||||
Ok(SerializeMap {
|
||||
key: None,
|
||||
table: self.0.create_table_with_capacity(0, len)?,
|
||||
table: self.lua.create_table_with_capacity(0, len)?,
|
||||
options: self.options,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -197,14 +292,18 @@ impl<'lua> ser::Serializer for Serializer<'lua> {
|
||||
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 })
|
||||
Ok(SerializeStructVariant {
|
||||
name: self.lua.create_string(variant)?,
|
||||
table: self.lua.create_table_with_capacity(0, len as c_int)?,
|
||||
options: self.options,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub struct SerializeVec<'lua> {
|
||||
table: Table<'lua>,
|
||||
options: Options,
|
||||
}
|
||||
|
||||
impl<'lua> ser::SerializeSeq for SerializeVec<'lua> {
|
||||
@@ -213,24 +312,20 @@ impl<'lua> ser::SerializeSeq for SerializeVec<'lua> {
|
||||
|
||||
fn serialize_element<T>(&mut self, value: &T) -> Result<()>
|
||||
where
|
||||
T: ?Sized + Serialize,
|
||||
T: Serialize + ?Sized,
|
||||
{
|
||||
let lua = self.table.0.lua;
|
||||
let value = lua.to_value(value)?;
|
||||
let value = lua.to_value_with(value, self.options)?;
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(lua.state);
|
||||
assert_stack(lua.state, 4);
|
||||
check_stack(lua.state, 5)?;
|
||||
|
||||
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;
|
||||
let len = ffi::lua_rawlen(lua.state, -2) as Integer;
|
||||
protect_lua(lua.state, 2, 0, |state| {
|
||||
ffi::lua_rawseti(state, -2, len + 1);
|
||||
1
|
||||
}
|
||||
|
||||
protect_lua(lua.state, 2, push_to_table)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -245,7 +340,7 @@ impl<'lua> ser::SerializeTuple for SerializeVec<'lua> {
|
||||
|
||||
fn serialize_element<T>(&mut self, value: &T) -> Result<()>
|
||||
where
|
||||
T: ?Sized + Serialize,
|
||||
T: Serialize + ?Sized,
|
||||
{
|
||||
ser::SerializeSeq::serialize_element(self, value)
|
||||
}
|
||||
@@ -261,7 +356,7 @@ impl<'lua> ser::SerializeTupleStruct for SerializeVec<'lua> {
|
||||
|
||||
fn serialize_field<T>(&mut self, value: &T) -> Result<()>
|
||||
where
|
||||
T: ?Sized + Serialize,
|
||||
T: Serialize + ?Sized,
|
||||
{
|
||||
ser::SerializeSeq::serialize_element(self, value)
|
||||
}
|
||||
@@ -271,9 +366,11 @@ impl<'lua> ser::SerializeTupleStruct for SerializeVec<'lua> {
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub struct SerializeTupleVariant<'lua> {
|
||||
name: String<'lua>,
|
||||
table: Table<'lua>,
|
||||
options: Options,
|
||||
}
|
||||
|
||||
impl<'lua> ser::SerializeTupleVariant for SerializeTupleVariant<'lua> {
|
||||
@@ -282,11 +379,12 @@ impl<'lua> ser::SerializeTupleVariant for SerializeTupleVariant<'lua> {
|
||||
|
||||
fn serialize_field<T>(&mut self, value: &T) -> Result<()>
|
||||
where
|
||||
T: ?Sized + Serialize,
|
||||
T: Serialize + ?Sized,
|
||||
{
|
||||
let lua = self.table.0.lua;
|
||||
let idx = self.table.raw_len() + 1;
|
||||
self.table.raw_insert(idx, lua.to_value(value)?)
|
||||
self.table
|
||||
.raw_insert(idx, lua.to_value_with(value, self.options)?)
|
||||
}
|
||||
|
||||
fn end(self) -> Result<Value<'lua>> {
|
||||
@@ -297,9 +395,11 @@ impl<'lua> ser::SerializeTupleVariant for SerializeTupleVariant<'lua> {
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub struct SerializeMap<'lua> {
|
||||
table: Table<'lua>,
|
||||
key: Option<Value<'lua>>,
|
||||
options: Options,
|
||||
}
|
||||
|
||||
impl<'lua> ser::SerializeMap for SerializeMap<'lua> {
|
||||
@@ -308,23 +408,23 @@ impl<'lua> ser::SerializeMap for SerializeMap<'lua> {
|
||||
|
||||
fn serialize_key<T>(&mut self, key: &T) -> Result<()>
|
||||
where
|
||||
T: ?Sized + Serialize,
|
||||
T: Serialize + ?Sized,
|
||||
{
|
||||
let lua = self.table.0.lua;
|
||||
self.key = Some(lua.to_value(key)?);
|
||||
self.key = Some(lua.to_value_with(key, self.options)?);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn serialize_value<T>(&mut self, value: &T) -> Result<()>
|
||||
where
|
||||
T: ?Sized + Serialize,
|
||||
T: Serialize + ?Sized,
|
||||
{
|
||||
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)?;
|
||||
let value = lua.to_value_with(value, self.options)?;
|
||||
self.table.raw_set(key, value)
|
||||
}
|
||||
|
||||
@@ -339,7 +439,7 @@ impl<'lua> ser::SerializeStruct for SerializeMap<'lua> {
|
||||
|
||||
fn serialize_field<T>(&mut self, key: &'static str, value: &T) -> Result<()>
|
||||
where
|
||||
T: ?Sized + Serialize,
|
||||
T: Serialize + ?Sized,
|
||||
{
|
||||
ser::SerializeMap::serialize_key(self, key)?;
|
||||
ser::SerializeMap::serialize_value(self, value)
|
||||
@@ -350,9 +450,11 @@ impl<'lua> ser::SerializeStruct for SerializeMap<'lua> {
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub struct SerializeStructVariant<'lua> {
|
||||
name: String<'lua>,
|
||||
table: Table<'lua>,
|
||||
options: Options,
|
||||
}
|
||||
|
||||
impl<'lua> ser::SerializeStructVariant for SerializeStructVariant<'lua> {
|
||||
@@ -361,10 +463,11 @@ impl<'lua> ser::SerializeStructVariant for SerializeStructVariant<'lua> {
|
||||
|
||||
fn serialize_field<T>(&mut self, key: &'static str, value: &T) -> Result<()>
|
||||
where
|
||||
T: ?Sized + Serialize,
|
||||
T: Serialize + ?Sized,
|
||||
{
|
||||
let lua = self.table.0.lua;
|
||||
self.table.raw_set(key, lua.to_value(value)?)?;
|
||||
self.table
|
||||
.raw_set(key, lua.to_value_with(value, self.options)?)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
+3
-3
@@ -39,17 +39,17 @@ impl StdLib {
|
||||
#[cfg(any(feature = "luajit", doc))]
|
||||
pub const JIT: StdLib = StdLib(1 << 9);
|
||||
|
||||
/// (unsafe) [`ffi`](http://luajit.org/ext_ffi.html) library
|
||||
/// (**unsafe**) [`ffi`](http://luajit.org/ext_ffi.html) library
|
||||
///
|
||||
/// Requires `feature = "luajit"`
|
||||
#[cfg(any(feature = "luajit", doc))]
|
||||
pub const FFI: StdLib = StdLib(1 << 30);
|
||||
/// (unsafe) [`debug`](https://www.lua.org/manual/5.3/manual.html#6.10) library
|
||||
/// (**unsafe**) [`debug`](https://www.lua.org/manual/5.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
|
||||
/// (**unsafe**) All standard libraries
|
||||
pub const ALL: StdLib = StdLib(u32::MAX);
|
||||
/// The safe subset of the standard libraries
|
||||
pub const ALL_SAFE: StdLib = StdLib((1 << 30) - 1);
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
use std::borrow::Cow;
|
||||
use std::string::String as StdString;
|
||||
use std::{slice, str};
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
@@ -44,6 +46,28 @@ impl<'lua> String<'lua> {
|
||||
})
|
||||
}
|
||||
|
||||
/// Converts this string to a [`Cow<str>`].
|
||||
///
|
||||
/// Any non-Unicode sequences are replaced with [`U+FFFD REPLACEMENT CHARACTER`][U+FFFD].
|
||||
///
|
||||
/// [U+FFFD]: std::char::REPLACEMENT_CHARACTER
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use mlua::{Lua, Result};
|
||||
/// # fn main() -> Result<()> {
|
||||
/// let lua = Lua::new();
|
||||
///
|
||||
/// let s = lua.create_string(b"test\xff")?;
|
||||
/// assert_eq!(s.to_string_lossy(), "test\u{fffd}");
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn to_string_lossy(&self) -> Cow<'_, str> {
|
||||
StdString::from_utf8_lossy(self.as_bytes())
|
||||
}
|
||||
|
||||
/// Get the bytes that make up this string.
|
||||
///
|
||||
/// The returned slice will not contain the terminating nul byte, but will contain any nul
|
||||
|
||||
+83
-104
@@ -1,5 +1,4 @@
|
||||
use std::marker::PhantomData;
|
||||
use std::os::raw::c_int;
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
use {
|
||||
@@ -11,7 +10,7 @@ use crate::error::{Error, Result};
|
||||
use crate::ffi;
|
||||
use crate::function::Function;
|
||||
use crate::types::{Integer, LuaRef};
|
||||
use crate::util::{assert_stack, protect_lua, protect_lua_closure, StackGuard};
|
||||
use crate::util::{assert_stack, check_stack, protect_lua, StackGuard};
|
||||
use crate::value::{FromLua, FromLuaMulti, Nil, ToLua, ToLuaMulti, Value};
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
@@ -60,19 +59,15 @@ impl<'lua> Table<'lua> {
|
||||
let lua = self.0.lua;
|
||||
let key = key.to_lua(lua)?;
|
||||
let value = value.to_lua(lua)?;
|
||||
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(lua.state);
|
||||
assert_stack(lua.state, 6);
|
||||
check_stack(lua.state, 6)?;
|
||||
|
||||
lua.push_ref(&self.0);
|
||||
lua.push_value(key)?;
|
||||
lua.push_value(value)?;
|
||||
|
||||
unsafe extern "C" fn set_table(state: *mut ffi::lua_State) -> c_int {
|
||||
ffi::lua_settable(state, -3);
|
||||
1
|
||||
}
|
||||
protect_lua(lua.state, 3, set_table)
|
||||
protect_lua(lua.state, 3, 0, |state| ffi::lua_settable(state, -3))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -103,18 +98,15 @@ impl<'lua> Table<'lua> {
|
||||
pub fn get<K: ToLua<'lua>, V: FromLua<'lua>>(&self, key: K) -> Result<V> {
|
||||
let lua = self.0.lua;
|
||||
let key = key.to_lua(lua)?;
|
||||
|
||||
let value = unsafe {
|
||||
let _sg = StackGuard::new(lua.state);
|
||||
assert_stack(lua.state, 5);
|
||||
check_stack(lua.state, 5)?;
|
||||
|
||||
lua.push_ref(&self.0);
|
||||
lua.push_value(key)?;
|
||||
protect_lua(lua.state, 2, 1, |state| ffi::lua_gettable(state, -2))?;
|
||||
|
||||
unsafe extern "C" fn get_table(state: *mut ffi::lua_State) -> c_int {
|
||||
ffi::lua_gettable(state, -2);
|
||||
1
|
||||
}
|
||||
protect_lua(lua.state, 2, get_table)?;
|
||||
lua.pop_value()
|
||||
};
|
||||
V::from_lua(value, lua)
|
||||
@@ -127,19 +119,13 @@ impl<'lua> Table<'lua> {
|
||||
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(lua.state);
|
||||
assert_stack(lua.state, 5);
|
||||
check_stack(lua.state, 5)?;
|
||||
|
||||
lua.push_ref(&self.0);
|
||||
lua.push_value(key)?;
|
||||
|
||||
unsafe extern "C" fn get_table(state: *mut ffi::lua_State) -> c_int {
|
||||
ffi::lua_gettable(state, -2);
|
||||
1
|
||||
}
|
||||
protect_lua(lua.state, 2, get_table)?;
|
||||
|
||||
let has = ffi::lua_isnil(lua.state, -1) == 0;
|
||||
Ok(has)
|
||||
protect_lua(lua.state, 2, 1, |state| {
|
||||
ffi::lua_gettable(state, -2) != ffi::LUA_TNIL
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -207,19 +193,12 @@ impl<'lua> Table<'lua> {
|
||||
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(lua.state);
|
||||
assert_stack(lua.state, 6);
|
||||
check_stack(lua.state, 6)?;
|
||||
|
||||
lua.push_ref(&self.0);
|
||||
lua.push_value(key)?;
|
||||
lua.push_value(value)?;
|
||||
|
||||
unsafe extern "C" fn raw_set(state: *mut ffi::lua_State) -> c_int {
|
||||
ffi::lua_rawset(state, -3);
|
||||
0
|
||||
}
|
||||
protect_lua(lua.state, 3, raw_set)?;
|
||||
|
||||
Ok(())
|
||||
protect_lua(lua.state, 3, 0, |state| ffi::lua_rawset(state, -3))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -227,13 +206,15 @@ impl<'lua> Table<'lua> {
|
||||
pub fn raw_get<K: ToLua<'lua>, V: FromLua<'lua>>(&self, key: K) -> Result<V> {
|
||||
let lua = self.0.lua;
|
||||
let key = key.to_lua(lua)?;
|
||||
|
||||
let value = unsafe {
|
||||
let _sg = StackGuard::new(lua.state);
|
||||
assert_stack(lua.state, 3);
|
||||
check_stack(lua.state, 3)?;
|
||||
|
||||
lua.push_ref(&self.0);
|
||||
lua.push_value(key)?;
|
||||
ffi::lua_rawget(lua.state, -2);
|
||||
|
||||
lua.pop_value()
|
||||
};
|
||||
V::from_lua(value, lua)
|
||||
@@ -251,18 +232,17 @@ impl<'lua> Table<'lua> {
|
||||
let value = value.to_lua(lua)?;
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(lua.state);
|
||||
assert_stack(lua.state, 6);
|
||||
check_stack(lua.state, 5)?;
|
||||
|
||||
lua.push_ref(&self.0);
|
||||
lua.push_value(value)?;
|
||||
|
||||
protect_lua_closure(lua.state, 2, 0, |state| {
|
||||
for i in (idx..size + 1).rev() {
|
||||
protect_lua(lua.state, 2, 0, |state| {
|
||||
for i in (idx..=size).rev() {
|
||||
// table[i+1] = table[i]
|
||||
ffi::lua_rawgeti(state, -2, i);
|
||||
ffi::lua_rawseti(state, -3, i + 1);
|
||||
}
|
||||
ffi::lua_rawseti(state, -2, idx);
|
||||
ffi::lua_rawseti(state, -2, idx)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -270,10 +250,10 @@ impl<'lua> Table<'lua> {
|
||||
/// Removes a key from the table.
|
||||
///
|
||||
/// If `key` is an integer, mlua shifts down the elements from `table[key+1]`,
|
||||
/// and erases element `table[key]`. The complexity is O(n) in worst case,
|
||||
/// and erases element `table[key]`. The complexity is O(n) in the worst case,
|
||||
/// where n is the table length.
|
||||
///
|
||||
/// For othey key types this is equivalent to setting `table[key] = nil`.
|
||||
/// For other key types this is equivalent to setting `table[key] = nil`.
|
||||
pub fn raw_remove<K: ToLua<'lua>>(&self, key: K) -> Result<()> {
|
||||
let lua = self.0.lua;
|
||||
let key = key.to_lua(lua)?;
|
||||
@@ -285,11 +265,10 @@ impl<'lua> Table<'lua> {
|
||||
}
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(lua.state);
|
||||
assert_stack(lua.state, 6);
|
||||
check_stack(lua.state, 4)?;
|
||||
|
||||
lua.push_ref(&self.0);
|
||||
|
||||
protect_lua_closure(lua.state, 1, 0, |state| {
|
||||
protect_lua(lua.state, 1, 0, |state| {
|
||||
for i in idx..size {
|
||||
ffi::lua_rawgeti(state, -1, i + 1);
|
||||
ffi::lua_rawseti(state, -2, i);
|
||||
@@ -312,9 +291,10 @@ impl<'lua> Table<'lua> {
|
||||
let lua = self.0.lua;
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(lua.state);
|
||||
assert_stack(lua.state, 4);
|
||||
check_stack(lua.state, 4)?;
|
||||
|
||||
lua.push_ref(&self.0);
|
||||
protect_lua_closure(lua.state, 1, 0, |state| ffi::luaL_len(state, -1))
|
||||
protect_lua(lua.state, 1, 0, |state| ffi::luaL_len(state, -1))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -324,9 +304,9 @@ impl<'lua> Table<'lua> {
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(lua.state);
|
||||
assert_stack(lua.state, 1);
|
||||
|
||||
lua.push_ref(&self.0);
|
||||
let len = ffi::lua_rawlen(lua.state, -1);
|
||||
len as Integer
|
||||
ffi::lua_rawlen(lua.state, -1) as Integer
|
||||
}
|
||||
}
|
||||
|
||||
@@ -337,13 +317,13 @@ impl<'lua> Table<'lua> {
|
||||
let lua = self.0.lua;
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(lua.state);
|
||||
assert_stack(lua.state, 1);
|
||||
assert_stack(lua.state, 2);
|
||||
|
||||
lua.push_ref(&self.0);
|
||||
if ffi::lua_getmetatable(lua.state, -1) == 0 {
|
||||
None
|
||||
} else {
|
||||
let table = Table(lua.pop_ref());
|
||||
Some(table)
|
||||
Some(Table(lua.pop_ref()))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -356,7 +336,8 @@ impl<'lua> Table<'lua> {
|
||||
let lua = self.0.lua;
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(lua.state);
|
||||
assert_stack(lua.state, 1);
|
||||
assert_stack(lua.state, 2);
|
||||
|
||||
lua.push_ref(&self.0);
|
||||
if let Some(metatable) = metatable {
|
||||
lua.push_ref(&metatable.0);
|
||||
@@ -403,7 +384,7 @@ impl<'lua> Table<'lua> {
|
||||
pub fn pairs<K: FromLua<'lua>, V: FromLua<'lua>>(self) -> TablePairs<'lua, K, V> {
|
||||
TablePairs {
|
||||
table: self.0,
|
||||
next_key: Some(Nil),
|
||||
key: Some(Nil),
|
||||
_phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
@@ -411,7 +392,7 @@ impl<'lua> Table<'lua> {
|
||||
/// Consume this table and return an iterator over all values in the sequence part of the table.
|
||||
///
|
||||
/// The iterator will yield all values `t[1]`, `t[2]`, and so on, until a `nil` value is
|
||||
/// encountered. This mirrors the behaviour of Lua's `ipairs` function and will invoke the
|
||||
/// encountered. This mirrors the behavior of Lua's `ipairs` function and will invoke the
|
||||
/// `__index` metamethod according to the usual rules. However, the deprecated `__ipairs`
|
||||
/// metatable will not be called.
|
||||
///
|
||||
@@ -474,9 +455,12 @@ impl<'lua> Table<'lua> {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
pub(crate) fn raw_sequence_values_by_len<V: FromLua<'lua>>(self) -> TableSequence<'lua, V> {
|
||||
let len = self.raw_len();
|
||||
#[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),
|
||||
@@ -492,6 +476,7 @@ impl<'lua> Table<'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;
|
||||
@@ -641,7 +626,7 @@ impl<'lua> Serialize for Table<'lua> {
|
||||
let len = self.raw_len() as usize;
|
||||
if len > 0 || self.is_array() {
|
||||
let mut seq = serializer.serialize_seq(Some(len))?;
|
||||
for v in self.clone().raw_sequence_values_by_len::<Value>() {
|
||||
for v in self.clone().raw_sequence_values_by_len::<Value>(None) {
|
||||
let v = v.map_err(serde::ser::Error::custom)?;
|
||||
seq.serialize_element(&v)?;
|
||||
}
|
||||
@@ -664,7 +649,7 @@ impl<'lua> Serialize for Table<'lua> {
|
||||
/// [`Table::pairs`]: struct.Table.html#method.pairs
|
||||
pub struct TablePairs<'lua, K, V> {
|
||||
table: LuaRef<'lua>,
|
||||
next_key: Option<Value<'lua>>,
|
||||
key: Option<Value<'lua>>,
|
||||
_phantom: PhantomData<(K, V)>,
|
||||
}
|
||||
|
||||
@@ -676,41 +661,37 @@ where
|
||||
type Item = Result<(K, V)>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
if let Some(next_key) = self.next_key.take() {
|
||||
if let Some(prev_key) = self.key.take() {
|
||||
let lua = self.table.lua;
|
||||
|
||||
let res = (|| {
|
||||
let res = unsafe {
|
||||
let _sg = StackGuard::new(lua.state);
|
||||
assert_stack(lua.state, 6);
|
||||
let res = (|| unsafe {
|
||||
let _sg = StackGuard::new(lua.state);
|
||||
check_stack(lua.state, 5)?;
|
||||
|
||||
lua.push_ref(&self.table);
|
||||
lua.push_value(next_key)?;
|
||||
lua.push_ref(&self.table);
|
||||
lua.push_value(prev_key)?;
|
||||
|
||||
let next = protect_lua_closure(lua.state, 2, ffi::LUA_MULTRET, |state| {
|
||||
ffi::lua_next(state, -2) != 0
|
||||
})?;
|
||||
if next {
|
||||
ffi::lua_pushvalue(lua.state, -2);
|
||||
let key = lua.pop_value();
|
||||
let value = lua.pop_value();
|
||||
self.next_key = Some(lua.pop_value());
|
||||
|
||||
Some((key, value))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
};
|
||||
|
||||
Ok(if let Some((key, value)) = res {
|
||||
Some((K::from_lua(key, lua)?, V::from_lua(value, lua)?))
|
||||
let next = protect_lua(lua.state, 2, ffi::LUA_MULTRET, |state| {
|
||||
ffi::lua_next(state, -2)
|
||||
})?;
|
||||
if next != 0 {
|
||||
let value = lua.pop_value();
|
||||
let key = lua.pop_value();
|
||||
Ok(Some((
|
||||
key.clone(),
|
||||
K::from_lua(key, lua)?,
|
||||
V::from_lua(value, lua)?,
|
||||
)))
|
||||
} else {
|
||||
None
|
||||
})
|
||||
Ok(None)
|
||||
}
|
||||
})();
|
||||
|
||||
match res {
|
||||
Ok(Some((key, value))) => Some(Ok((key, value))),
|
||||
Ok(Some((key, ret_key, value))) => {
|
||||
self.key = Some(key);
|
||||
Some(Ok((ret_key, value)))
|
||||
}
|
||||
Ok(None) => None,
|
||||
Err(e) => Some(Err(e)),
|
||||
}
|
||||
@@ -743,31 +724,29 @@ where
|
||||
if let Some(index) = self.index.take() {
|
||||
let lua = self.table.lua;
|
||||
|
||||
let res = unsafe {
|
||||
let res = (|| unsafe {
|
||||
let _sg = StackGuard::new(lua.state);
|
||||
assert_stack(lua.state, 5);
|
||||
check_stack(lua.state, 1 + if self.raw { 0 } else { 3 })?;
|
||||
|
||||
lua.push_ref(&self.table);
|
||||
let lua_geti = if self.raw {
|
||||
ffi::lua_rawgeti
|
||||
let res = if self.raw {
|
||||
ffi::lua_rawgeti(lua.state, -1, index)
|
||||
} else {
|
||||
ffi::lua_geti
|
||||
protect_lua(lua.state, 1, 1, |state| ffi::lua_geti(state, -1, index))?
|
||||
};
|
||||
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);
|
||||
Some(Ok(value))
|
||||
}
|
||||
Err(err) => Some(Err(err)),
|
||||
match res {
|
||||
ffi::LUA_TNIL if index > self.len.unwrap_or(0) => Ok(None),
|
||||
_ => Ok(Some((index, lua.pop_value()))),
|
||||
}
|
||||
};
|
||||
})();
|
||||
|
||||
match res {
|
||||
Some(Ok(r)) => Some(V::from_lua(r, lua)),
|
||||
Some(Err(err)) => Some(Err(err)),
|
||||
None => None,
|
||||
Ok(Some((index, r))) => {
|
||||
self.index = Some(index + 1);
|
||||
Some(V::from_lua(r, lua))
|
||||
}
|
||||
Ok(None) => None,
|
||||
Err(err) => Some(Err(err)),
|
||||
}
|
||||
} else {
|
||||
None
|
||||
|
||||
+73
-52
@@ -1,25 +1,27 @@
|
||||
use std::cmp;
|
||||
use std::os::raw::c_int;
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
use crate::ffi;
|
||||
use crate::types::LuaRef;
|
||||
use crate::util::{
|
||||
assert_stack, check_stack, error_traceback, pop_error, protect_lua_closure, StackGuard,
|
||||
};
|
||||
use crate::util::{assert_stack, check_stack, error_traceback, pop_error, protect_lua, StackGuard};
|
||||
use crate::value::{FromLuaMulti, MultiValue, ToLuaMulti};
|
||||
|
||||
#[cfg(any(feature = "lua54", all(feature = "luajit", feature = "vendored"), doc))]
|
||||
use crate::function::Function;
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
use {
|
||||
crate::{
|
||||
error::ExternalError,
|
||||
lua::{AsyncPollPending, Lua, WAKER_REGISTRY_KEY},
|
||||
util::{get_gc_userdata, push_gc_userdata},
|
||||
lua::{ASYNC_POLL_PENDING, WAKER_REGISTRY_KEY},
|
||||
util::get_gc_userdata,
|
||||
value::Value,
|
||||
},
|
||||
futures_core::{future::Future, stream::Stream},
|
||||
std::{
|
||||
cell::RefCell,
|
||||
marker::PhantomData,
|
||||
mem,
|
||||
os::raw::c_void,
|
||||
pin::Pin,
|
||||
task::{Context, Poll, Waker},
|
||||
@@ -109,24 +111,21 @@ impl<'lua> Thread<'lua> {
|
||||
{
|
||||
let lua = self.0.lua;
|
||||
let args = args.to_lua_multi(lua)?;
|
||||
let nargs = args.len() as c_int;
|
||||
let results = unsafe {
|
||||
let _sg = StackGuard::new(lua.state);
|
||||
assert_stack(lua.state, 3);
|
||||
check_stack(lua.state, cmp::min(nargs + 1, 3))?;
|
||||
|
||||
lua.push_ref(&self.0);
|
||||
let thread_state = ffi::lua_tothread(lua.state, -1);
|
||||
ffi::lua_pop(lua.state, 1);
|
||||
|
||||
let status = ffi::lua_status(thread_state);
|
||||
if status != ffi::LUA_YIELD && ffi::lua_gettop(thread_state) == 0 {
|
||||
return Err(Error::CoroutineInactive);
|
||||
}
|
||||
|
||||
ffi::lua_pop(lua.state, 1);
|
||||
|
||||
let nargs = args.len() as c_int;
|
||||
check_stack(lua.state, nargs)?;
|
||||
check_stack(thread_state, nargs + 1)?;
|
||||
|
||||
check_stack(thread_state, nargs)?;
|
||||
for arg in args {
|
||||
lua.push_value(arg)?;
|
||||
}
|
||||
@@ -136,17 +135,14 @@ impl<'lua> Thread<'lua> {
|
||||
|
||||
let ret = ffi::lua_resume(thread_state, lua.state, nargs, &mut nresults as *mut c_int);
|
||||
if ret != ffi::LUA_OK && ret != ffi::LUA_YIELD {
|
||||
protect_lua_closure(lua.state, 0, 0, |_| {
|
||||
error_traceback(thread_state);
|
||||
0
|
||||
})?;
|
||||
protect_lua(lua.state, 0, 0, |_| error_traceback(thread_state))?;
|
||||
return Err(pop_error(thread_state, ret));
|
||||
}
|
||||
|
||||
let mut results = MultiValue::new();
|
||||
check_stack(lua.state, nresults + 2)?; // 2 is extra for `lua.pop_value()` below
|
||||
ffi::lua_xmove(thread_state, lua.state, nresults);
|
||||
|
||||
assert_stack(lua.state, 2);
|
||||
for _ in 0..nresults {
|
||||
results.push_front(lua.pop_value());
|
||||
}
|
||||
@@ -177,6 +173,43 @@ impl<'lua> Thread<'lua> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Resets a thread
|
||||
///
|
||||
/// In [Lua 5.4]: cleans its call stack and closes all pending to-be-closed variables.
|
||||
/// Returns a error in case of either the original error that stopped the thread or errors
|
||||
/// in closing methods.
|
||||
///
|
||||
/// In [LuaJIT]: resets to the initial state of a newly created Lua thread.
|
||||
/// Lua threads in arbitrary states (like yielded or errored) can be reset properly.
|
||||
///
|
||||
/// Sets a Lua function for the thread afterwards.
|
||||
///
|
||||
/// Requires `feature = "lua54"` OR `feature = "luajit,vendored"`
|
||||
///
|
||||
/// [Lua 5.4]: https://www.lua.org/manual/5.4/manual.html#lua_resetthread
|
||||
/// [LuaJIT]: https://github.com/openresty/luajit2#lua_resetthread
|
||||
#[cfg(any(feature = "lua54", all(feature = "luajit", feature = "vendored"), doc))]
|
||||
pub fn reset(&self, func: Function<'lua>) -> Result<()> {
|
||||
let lua = self.0.lua;
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(lua.state);
|
||||
check_stack(lua.state, 2)?;
|
||||
|
||||
lua.push_ref(&self.0);
|
||||
let thread_state = ffi::lua_tothread(lua.state, -1);
|
||||
|
||||
let ret = ffi::lua_resetthread(lua.state, thread_state);
|
||||
if ret != ffi::LUA_OK {
|
||||
return Err(pop_error(thread_state, ret));
|
||||
}
|
||||
|
||||
lua.push_ref(&func.0);
|
||||
ffi::lua_xmove(lua.state, thread_state, 1);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Converts Thread to an AsyncThread which implements Future and Stream traits.
|
||||
///
|
||||
/// `args` are passed as arguments to the thread function for first call.
|
||||
@@ -225,7 +258,7 @@ impl<'lua> Thread<'lua> {
|
||||
A: ToLuaMulti<'lua>,
|
||||
R: FromLuaMulti<'lua>,
|
||||
{
|
||||
let args = args.to_lua_multi(&self.0.lua);
|
||||
let args = args.to_lua_multi(self.0.lua);
|
||||
AsyncThread {
|
||||
thread: self,
|
||||
args0: RefCell::new(Some(args)),
|
||||
@@ -262,7 +295,7 @@ where
|
||||
self.thread.resume(())?
|
||||
};
|
||||
|
||||
if is_poll_pending(lua, &ret) {
|
||||
if is_poll_pending(&ret) {
|
||||
return Poll::Pending;
|
||||
}
|
||||
|
||||
@@ -283,7 +316,7 @@ where
|
||||
|
||||
match self.thread.status() {
|
||||
ThreadStatus::Resumable => {}
|
||||
_ => return Poll::Ready(Err("Thread already finished".to_lua_err())),
|
||||
_ => return Poll::Ready(Err(Error::CoroutineInactive)),
|
||||
};
|
||||
|
||||
let _wg = WakerGuard::new(lua.state, cx.waker().clone());
|
||||
@@ -293,7 +326,7 @@ where
|
||||
self.thread.resume(())?
|
||||
};
|
||||
|
||||
if is_poll_pending(lua, &ret) {
|
||||
if is_poll_pending(&ret) {
|
||||
return Poll::Pending;
|
||||
}
|
||||
|
||||
@@ -308,44 +341,31 @@ where
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
fn is_poll_pending(lua: &Lua, val: &MultiValue) -> bool {
|
||||
if val.len() != 1 {
|
||||
return false;
|
||||
}
|
||||
|
||||
if let Some(Value::UserData(ud)) = val.iter().next() {
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(lua.state);
|
||||
assert_stack(lua.state, 3);
|
||||
|
||||
lua.push_ref(&ud.0);
|
||||
let is_pending = get_gc_userdata::<AsyncPollPending>(lua.state, -1)
|
||||
.as_ref()
|
||||
.is_some();
|
||||
ffi::lua_pop(lua.state, 1);
|
||||
|
||||
return is_pending;
|
||||
fn is_poll_pending(val: &MultiValue) -> bool {
|
||||
match val.iter().enumerate().last() {
|
||||
Some((1, Value::LightUserData(ud))) => {
|
||||
ud.0 == &ASYNC_POLL_PENDING as *const u8 as *mut c_void
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
struct WakerGuard(*mut ffi::lua_State);
|
||||
struct WakerGuard(*mut ffi::lua_State, Option<Waker>);
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
impl WakerGuard {
|
||||
pub fn new(state: *mut ffi::lua_State, waker: Waker) -> Result<WakerGuard> {
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(state);
|
||||
assert_stack(state, 6);
|
||||
check_stack(state, 3)?;
|
||||
|
||||
ffi::lua_pushlightuserdata(state, &WAKER_REGISTRY_KEY as *const u8 as *mut c_void);
|
||||
push_gc_userdata(state, waker)?;
|
||||
ffi::lua_rawset(state, ffi::LUA_REGISTRYINDEX);
|
||||
let waker_key = &WAKER_REGISTRY_KEY as *const u8 as *const c_void;
|
||||
ffi::lua_rawgetp(state, ffi::LUA_REGISTRYINDEX, waker_key);
|
||||
let waker_slot = get_gc_userdata::<Option<Waker>>(state, -1).as_mut();
|
||||
let old = mlua_expect!(waker_slot, "Waker is destroyed").replace(waker);
|
||||
|
||||
Ok(WakerGuard(state))
|
||||
Ok(WakerGuard(state, old))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -353,14 +373,15 @@ impl WakerGuard {
|
||||
#[cfg(feature = "async")]
|
||||
impl Drop for WakerGuard {
|
||||
fn drop(&mut self) {
|
||||
let state = self.0;
|
||||
unsafe {
|
||||
let state = self.0;
|
||||
let _sg = StackGuard::new(state);
|
||||
assert_stack(state, 2);
|
||||
assert_stack(state, 3);
|
||||
|
||||
ffi::lua_pushlightuserdata(state, &WAKER_REGISTRY_KEY as *const u8 as *mut c_void);
|
||||
ffi::lua_pushnil(state);
|
||||
ffi::lua_rawset(state, ffi::LUA_REGISTRYINDEX);
|
||||
let waker_key = &WAKER_REGISTRY_KEY as *const u8 as *const c_void;
|
||||
ffi::lua_rawgetp(state, ffi::LUA_REGISTRYINDEX, waker_key);
|
||||
let waker_slot = get_gc_userdata::<Option<Waker>>(state, -1).as_mut();
|
||||
mem::swap(mlua_expect!(waker_slot, "Waker is destroyed"), &mut self.1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+18
-6
@@ -1,4 +1,5 @@
|
||||
use std::cell::RefCell;
|
||||
use std::hash::{Hash, Hasher};
|
||||
use std::os::raw::{c_int, c_void};
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::{fmt, mem, ptr};
|
||||
@@ -10,7 +11,6 @@ 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;
|
||||
|
||||
@@ -32,8 +32,6 @@ 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")]
|
||||
@@ -46,7 +44,7 @@ impl<T> MaybeSend for T {}
|
||||
|
||||
/// An auto generated key into the Lua registry.
|
||||
///
|
||||
/// This is a handle to a value stored inside the Lua registry. It is not automatically
|
||||
/// This is a handle to a value stored inside the Lua registry. It is not automatically
|
||||
/// garbage collected on Drop, but it can be removed with [`Lua::remove_registry_value`],
|
||||
/// and instances not manually removed can be garbage collected with [`Lua::expire_registry_values`].
|
||||
///
|
||||
@@ -70,6 +68,20 @@ impl fmt::Debug for RegistryKey {
|
||||
}
|
||||
}
|
||||
|
||||
impl Hash for RegistryKey {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
self.registry_id.hash(state)
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for RegistryKey {
|
||||
fn eq(&self, other: &RegistryKey) -> bool {
|
||||
self.registry_id == other.registry_id && Arc::ptr_eq(&self.unref_list, &other.unref_list)
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for RegistryKey {}
|
||||
|
||||
impl Drop for RegistryKey {
|
||||
fn drop(&mut self) {
|
||||
let mut unref_list = mlua_expect!(self.unref_list.lock(), "unref list poisoned");
|
||||
@@ -120,8 +132,8 @@ impl<'lua> PartialEq for LuaRef<'lua> {
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(lua.state);
|
||||
assert_stack(lua.state, 2);
|
||||
lua.push_ref(&self);
|
||||
lua.push_ref(&other);
|
||||
lua.push_ref(self);
|
||||
lua.push_ref(other);
|
||||
ffi::lua_rawequal(lua.state, -1, -2) == 1
|
||||
}
|
||||
}
|
||||
|
||||
+568
-154
@@ -1,4 +1,9 @@
|
||||
use std::cell::{Ref, RefMut};
|
||||
use std::any::TypeId;
|
||||
use std::cell::{Ref, RefCell, RefMut};
|
||||
use std::fmt;
|
||||
use std::hash::{Hash, Hasher};
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use std::string::String as StdString;
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
use std::future::Future;
|
||||
@@ -13,10 +18,18 @@ 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, UserDataCell};
|
||||
use crate::util::{assert_stack, get_destructed_userdata_metatable, get_userdata, StackGuard};
|
||||
use crate::value::{FromLua, FromLuaMulti, ToLua, ToLuaMulti, Value};
|
||||
use crate::table::{Table, TablePairs};
|
||||
use crate::types::{Callback, LuaRef, MaybeSend};
|
||||
use crate::util::{
|
||||
check_stack, get_destructed_userdata_metatable, get_userdata, push_string, StackGuard,
|
||||
};
|
||||
use crate::value::{FromLua, FromLuaMulti, ToLua, ToLuaMulti};
|
||||
|
||||
#[cfg(any(feature = "lua52", feature = "lua51", feature = "luajit"))]
|
||||
use crate::value::Value;
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
use crate::types::AsyncCallback;
|
||||
|
||||
/// Kinds of metamethods that can be overridden.
|
||||
///
|
||||
@@ -24,7 +37,7 @@ use crate::value::{FromLua, FromLuaMulti, ToLua, ToLuaMulti, Value};
|
||||
/// generally no need to do so: [`UserData`] implementors can instead just implement `Drop`.
|
||||
///
|
||||
/// [`UserData`]: trait.UserData.html
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum MetaMethod {
|
||||
/// The `+` operator.
|
||||
Add,
|
||||
@@ -93,6 +106,15 @@ pub enum MetaMethod {
|
||||
/// Requires `feature = "lua54/lua53/lua52"`
|
||||
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52", doc))]
|
||||
Pairs,
|
||||
/// The `__ipairs` metamethod.
|
||||
///
|
||||
/// This is not an operator, but it will be called by the built-in [`ipairs`] function.
|
||||
///
|
||||
/// Requires `feature = "lua52"`
|
||||
///
|
||||
/// [`ipairs`]: https://www.lua.org/manual/5.2/manual.html#pdf-ipairs
|
||||
#[cfg(any(feature = "lua52", doc))]
|
||||
IPairs,
|
||||
/// The `__close` metamethod.
|
||||
///
|
||||
/// Executed when a variable, that marked as to-be-closed, goes out of scope.
|
||||
@@ -105,58 +127,155 @@ pub enum MetaMethod {
|
||||
/// [lua_doc]: https://www.lua.org/manual/5.4/manual.html#3.3.8
|
||||
#[cfg(any(feature = "lua54", doc))]
|
||||
Close,
|
||||
/// A custom metamethod.
|
||||
///
|
||||
/// Must not be in the protected list: `__gc`, `__metatable`, `__mlua*`.
|
||||
Custom(StdString),
|
||||
}
|
||||
|
||||
impl PartialEq for MetaMethod {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.name() == other.name()
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for MetaMethod {}
|
||||
|
||||
impl Hash for MetaMethod {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
self.name().hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for MetaMethod {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(fmt, "{}", self.name())
|
||||
}
|
||||
}
|
||||
|
||||
impl MetaMethod {
|
||||
pub(crate) fn name(self) -> &'static [u8] {
|
||||
/// Returns Lua metamethod name, usually prefixed by two underscores.
|
||||
pub fn name(&self) -> &str {
|
||||
match self {
|
||||
MetaMethod::Add => b"__add",
|
||||
MetaMethod::Sub => b"__sub",
|
||||
MetaMethod::Mul => b"__mul",
|
||||
MetaMethod::Div => b"__div",
|
||||
MetaMethod::Mod => b"__mod",
|
||||
MetaMethod::Pow => b"__pow",
|
||||
MetaMethod::Unm => b"__unm",
|
||||
MetaMethod::Add => "__add",
|
||||
MetaMethod::Sub => "__sub",
|
||||
MetaMethod::Mul => "__mul",
|
||||
MetaMethod::Div => "__div",
|
||||
MetaMethod::Mod => "__mod",
|
||||
MetaMethod::Pow => "__pow",
|
||||
MetaMethod::Unm => "__unm",
|
||||
|
||||
#[cfg(any(feature = "lua54", feature = "lua53"))]
|
||||
MetaMethod::IDiv => b"__idiv",
|
||||
MetaMethod::IDiv => "__idiv",
|
||||
#[cfg(any(feature = "lua54", feature = "lua53"))]
|
||||
MetaMethod::BAnd => b"__band",
|
||||
MetaMethod::BAnd => "__band",
|
||||
#[cfg(any(feature = "lua54", feature = "lua53"))]
|
||||
MetaMethod::BOr => b"__bor",
|
||||
MetaMethod::BOr => "__bor",
|
||||
#[cfg(any(feature = "lua54", feature = "lua53"))]
|
||||
MetaMethod::BXor => b"__bxor",
|
||||
MetaMethod::BXor => "__bxor",
|
||||
#[cfg(any(feature = "lua54", feature = "lua53"))]
|
||||
MetaMethod::BNot => b"__bnot",
|
||||
MetaMethod::BNot => "__bnot",
|
||||
#[cfg(any(feature = "lua54", feature = "lua53"))]
|
||||
MetaMethod::Shl => b"__shl",
|
||||
MetaMethod::Shl => "__shl",
|
||||
#[cfg(any(feature = "lua54", feature = "lua53"))]
|
||||
MetaMethod::Shr => b"__shr",
|
||||
MetaMethod::Shr => "__shr",
|
||||
|
||||
MetaMethod::Concat => b"__concat",
|
||||
MetaMethod::Len => b"__len",
|
||||
MetaMethod::Eq => b"__eq",
|
||||
MetaMethod::Lt => b"__lt",
|
||||
MetaMethod::Le => b"__le",
|
||||
MetaMethod::Index => b"__index",
|
||||
MetaMethod::NewIndex => b"__newindex",
|
||||
MetaMethod::Call => b"__call",
|
||||
MetaMethod::ToString => b"__tostring",
|
||||
MetaMethod::Concat => "__concat",
|
||||
MetaMethod::Len => "__len",
|
||||
MetaMethod::Eq => "__eq",
|
||||
MetaMethod::Lt => "__lt",
|
||||
MetaMethod::Le => "__le",
|
||||
MetaMethod::Index => "__index",
|
||||
MetaMethod::NewIndex => "__newindex",
|
||||
MetaMethod::Call => "__call",
|
||||
MetaMethod::ToString => "__tostring",
|
||||
|
||||
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
|
||||
MetaMethod::Pairs => b"__pairs",
|
||||
MetaMethod::Pairs => "__pairs",
|
||||
#[cfg(feature = "lua52")]
|
||||
MetaMethod::IPairs => "__ipairs",
|
||||
|
||||
#[cfg(feature = "lua54")]
|
||||
MetaMethod::Close => b"__close",
|
||||
MetaMethod::Close => "__close",
|
||||
|
||||
MetaMethod::Custom(ref name) => name,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn validate(self) -> Result<Self> {
|
||||
match self {
|
||||
MetaMethod::Custom(name) if name == "__gc" => Err(Error::MetaMethodRestricted(name)),
|
||||
MetaMethod::Custom(name) if name == "__metatable" => {
|
||||
Err(Error::MetaMethodRestricted(name))
|
||||
}
|
||||
MetaMethod::Custom(name) if name.starts_with("__mlua") => {
|
||||
Err(Error::MetaMethodRestricted(name))
|
||||
}
|
||||
_ => Ok(self),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<StdString> for MetaMethod {
|
||||
fn from(name: StdString) -> Self {
|
||||
match name.as_str() {
|
||||
"__add" => MetaMethod::Add,
|
||||
"__sub" => MetaMethod::Sub,
|
||||
"__mul" => MetaMethod::Mul,
|
||||
"__div" => MetaMethod::Div,
|
||||
"__mod" => MetaMethod::Mod,
|
||||
"__pow" => MetaMethod::Pow,
|
||||
"__unm" => MetaMethod::Unm,
|
||||
|
||||
#[cfg(any(feature = "lua54", feature = "lua53"))]
|
||||
"__idiv" => MetaMethod::IDiv,
|
||||
#[cfg(any(feature = "lua54", feature = "lua53"))]
|
||||
"__band" => MetaMethod::BAnd,
|
||||
#[cfg(any(feature = "lua54", feature = "lua53"))]
|
||||
"__bor" => MetaMethod::BOr,
|
||||
#[cfg(any(feature = "lua54", feature = "lua53"))]
|
||||
"__bxor" => MetaMethod::BXor,
|
||||
#[cfg(any(feature = "lua54", feature = "lua53"))]
|
||||
"__bnot" => MetaMethod::BNot,
|
||||
#[cfg(any(feature = "lua54", feature = "lua53"))]
|
||||
"__shl" => MetaMethod::Shl,
|
||||
#[cfg(any(feature = "lua54", feature = "lua53"))]
|
||||
"__shr" => MetaMethod::Shr,
|
||||
|
||||
"__concat" => MetaMethod::Concat,
|
||||
"__len" => MetaMethod::Len,
|
||||
"__eq" => MetaMethod::Eq,
|
||||
"__lt" => MetaMethod::Lt,
|
||||
"__le" => MetaMethod::Le,
|
||||
"__index" => MetaMethod::Index,
|
||||
"__newindex" => MetaMethod::NewIndex,
|
||||
"__call" => MetaMethod::Call,
|
||||
"__tostring" => MetaMethod::ToString,
|
||||
|
||||
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
|
||||
"__pairs" => MetaMethod::Pairs,
|
||||
#[cfg(feature = "lua52")]
|
||||
"__ipairs" => MetaMethod::IPairs,
|
||||
|
||||
#[cfg(feature = "lua54")]
|
||||
"__close" => MetaMethod::Close,
|
||||
|
||||
_ => MetaMethod::Custom(name),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&str> for MetaMethod {
|
||||
fn from(name: &str) -> Self {
|
||||
MetaMethod::from(name.to_owned())
|
||||
}
|
||||
}
|
||||
|
||||
/// Method registry for [`UserData`] implementors.
|
||||
///
|
||||
/// [`UserData`]: trait.UserData.html
|
||||
pub trait UserDataMethods<'lua, T: UserData> {
|
||||
/// Add a method which accepts a `&T` as the first parameter.
|
||||
/// Add a regular method which accepts a `&T` as the first parameter.
|
||||
///
|
||||
/// Regular methods are implemented by overriding the `__index` metamethod and returning the
|
||||
/// accessed method. This allows them to be used with the expected `userdata:method()` syntax.
|
||||
@@ -165,7 +284,7 @@ pub trait UserDataMethods<'lua, T: UserData> {
|
||||
/// be used as a fall-back if no regular method is found.
|
||||
fn add_method<S, A, R, M>(&mut self, name: &S, method: M)
|
||||
where
|
||||
S: ?Sized + AsRef<[u8]>,
|
||||
S: AsRef<[u8]> + ?Sized,
|
||||
A: FromLuaMulti<'lua>,
|
||||
R: ToLuaMulti<'lua>,
|
||||
M: 'static + MaybeSend + Fn(&'lua Lua, &T, A) -> Result<R>;
|
||||
@@ -177,7 +296,7 @@ pub trait UserDataMethods<'lua, T: UserData> {
|
||||
/// [`add_method`]: #method.add_method
|
||||
fn add_method_mut<S, A, R, M>(&mut self, name: &S, method: M)
|
||||
where
|
||||
S: ?Sized + AsRef<[u8]>,
|
||||
S: AsRef<[u8]> + ?Sized,
|
||||
A: FromLuaMulti<'lua>,
|
||||
R: ToLuaMulti<'lua>,
|
||||
M: 'static + MaybeSend + FnMut(&'lua Lua, &mut T, A) -> Result<R>;
|
||||
@@ -195,24 +314,25 @@ pub trait UserDataMethods<'lua, T: UserData> {
|
||||
fn add_async_method<S, A, R, M, MR>(&mut self, name: &S, method: M)
|
||||
where
|
||||
T: Clone,
|
||||
S: ?Sized + AsRef<[u8]>,
|
||||
S: AsRef<[u8]> + ?Sized,
|
||||
A: FromLuaMulti<'lua>,
|
||||
R: ToLuaMulti<'lua>,
|
||||
M: 'static + MaybeSend + Fn(&'lua Lua, T, A) -> MR,
|
||||
MR: 'lua + Future<Output = Result<R>>;
|
||||
|
||||
/// Add a regular method as a function which accepts generic arguments, the first argument will
|
||||
/// be a `UserData` of type T if the method is called with Lua method syntax:
|
||||
/// be a [`AnyUserData`] of type `T` if the method is called with Lua method syntax:
|
||||
/// `my_userdata:my_method(arg1, arg2)`, or it is passed in as the first argument:
|
||||
/// `my_userdata.my_method(my_userdata, arg1, arg2)`.
|
||||
///
|
||||
/// Prefer to use [`add_method`] or [`add_method_mut`] as they are easier to use.
|
||||
///
|
||||
/// [`AnyUserData`]: struct.AnyUserData.html
|
||||
/// [`add_method`]: #method.add_method
|
||||
/// [`add_method_mut`]: #method.add_method_mut
|
||||
fn add_function<S, A, R, F>(&mut self, name: &S, function: F)
|
||||
where
|
||||
S: ?Sized + AsRef<[u8]>,
|
||||
S: AsRef<[u8]> + ?Sized,
|
||||
A: FromLuaMulti<'lua>,
|
||||
R: ToLuaMulti<'lua>,
|
||||
F: 'static + MaybeSend + Fn(&'lua Lua, A) -> Result<R>;
|
||||
@@ -224,7 +344,7 @@ pub trait UserDataMethods<'lua, T: UserData> {
|
||||
/// [`add_function`]: #method.add_function
|
||||
fn add_function_mut<S, A, R, F>(&mut self, name: &S, function: F)
|
||||
where
|
||||
S: ?Sized + AsRef<[u8]>,
|
||||
S: AsRef<[u8]> + ?Sized,
|
||||
A: FromLuaMulti<'lua>,
|
||||
R: ToLuaMulti<'lua>,
|
||||
F: 'static + MaybeSend + FnMut(&'lua Lua, A) -> Result<R>;
|
||||
@@ -241,8 +361,7 @@ pub trait UserDataMethods<'lua, T: UserData> {
|
||||
#[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,
|
||||
S: ?Sized + AsRef<[u8]>,
|
||||
S: AsRef<[u8]> + ?Sized,
|
||||
A: FromLuaMulti<'lua>,
|
||||
R: ToLuaMulti<'lua>,
|
||||
F: 'static + MaybeSend + Fn(&'lua Lua, A) -> FR,
|
||||
@@ -256,8 +375,9 @@ pub trait UserDataMethods<'lua, T: UserData> {
|
||||
/// side has a metatable. To prevent this, use [`add_meta_function`].
|
||||
///
|
||||
/// [`add_meta_function`]: #method.add_meta_function
|
||||
fn add_meta_method<A, R, M>(&mut self, meta: MetaMethod, method: M)
|
||||
fn add_meta_method<S, A, R, M>(&mut self, meta: S, method: M)
|
||||
where
|
||||
S: Into<MetaMethod>,
|
||||
A: FromLuaMulti<'lua>,
|
||||
R: ToLuaMulti<'lua>,
|
||||
M: 'static + MaybeSend + Fn(&'lua Lua, &T, A) -> Result<R>;
|
||||
@@ -270,8 +390,9 @@ pub trait UserDataMethods<'lua, T: UserData> {
|
||||
/// side has a metatable. To prevent this, use [`add_meta_function`].
|
||||
///
|
||||
/// [`add_meta_function`]: #method.add_meta_function
|
||||
fn add_meta_method_mut<A, R, M>(&mut self, meta: MetaMethod, method: M)
|
||||
fn add_meta_method_mut<S, A, R, M>(&mut self, meta: S, method: M)
|
||||
where
|
||||
S: Into<MetaMethod>,
|
||||
A: FromLuaMulti<'lua>,
|
||||
R: ToLuaMulti<'lua>,
|
||||
M: 'static + MaybeSend + FnMut(&'lua Lua, &mut T, A) -> Result<R>;
|
||||
@@ -281,8 +402,9 @@ pub trait UserDataMethods<'lua, T: UserData> {
|
||||
/// Metamethods for binary operators can be triggered if either the left or right argument to
|
||||
/// the binary operator has a metatable, so the first argument here is not necessarily a
|
||||
/// userdata of type `T`.
|
||||
fn add_meta_function<A, R, F>(&mut self, meta: MetaMethod, function: F)
|
||||
fn add_meta_function<S, A, R, F>(&mut self, meta: S, function: F)
|
||||
where
|
||||
S: Into<MetaMethod>,
|
||||
A: FromLuaMulti<'lua>,
|
||||
R: ToLuaMulti<'lua>,
|
||||
F: 'static + MaybeSend + Fn(&'lua Lua, A) -> Result<R>;
|
||||
@@ -292,11 +414,107 @@ pub trait UserDataMethods<'lua, T: UserData> {
|
||||
/// This is a version of [`add_meta_function`] that accepts a FnMut argument.
|
||||
///
|
||||
/// [`add_meta_function`]: #method.add_meta_function
|
||||
fn add_meta_function_mut<A, R, F>(&mut self, meta: MetaMethod, function: F)
|
||||
fn add_meta_function_mut<S, A, R, F>(&mut self, meta: S, function: F)
|
||||
where
|
||||
S: Into<MetaMethod>,
|
||||
A: FromLuaMulti<'lua>,
|
||||
R: ToLuaMulti<'lua>,
|
||||
F: 'static + MaybeSend + FnMut(&'lua Lua, A) -> Result<R>;
|
||||
|
||||
//
|
||||
// Below are internal methods used in generated code
|
||||
//
|
||||
|
||||
#[doc(hidden)]
|
||||
fn add_callback(&mut self, _name: Vec<u8>, _callback: Callback<'lua, 'static>) {}
|
||||
|
||||
#[doc(hidden)]
|
||||
#[cfg(feature = "async")]
|
||||
fn add_async_callback(&mut self, _name: Vec<u8>, _callback: AsyncCallback<'lua, 'static>) {}
|
||||
|
||||
#[doc(hidden)]
|
||||
fn add_meta_callback(&mut self, _meta: MetaMethod, _callback: Callback<'lua, 'static>) {}
|
||||
}
|
||||
|
||||
/// Field registry for [`UserData`] implementors.
|
||||
///
|
||||
/// [`UserData`]: trait.UserData.html
|
||||
pub trait UserDataFields<'lua, T: UserData> {
|
||||
/// Add a regular field getter as a method which accepts a `&T` as the parameter.
|
||||
///
|
||||
/// Regular field getters are implemented by overriding the `__index` metamethod and returning the
|
||||
/// accessed field. This allows them to be used with the expected `userdata.field` syntax.
|
||||
///
|
||||
/// If `add_meta_method` is used to set the `__index` metamethod, the `__index` metamethod will
|
||||
/// be used as a fall-back if no regular field or method are found.
|
||||
fn add_field_method_get<S, R, M>(&mut self, name: &S, method: M)
|
||||
where
|
||||
S: AsRef<[u8]> + ?Sized,
|
||||
R: ToLua<'lua>,
|
||||
M: 'static + MaybeSend + Fn(&'lua Lua, &T) -> Result<R>;
|
||||
|
||||
/// Add a regular field setter as a method which accepts a `&mut T` as the first parameter.
|
||||
///
|
||||
/// Regular field setters are implemented by overriding the `__newindex` metamethod and setting the
|
||||
/// accessed field. This allows them to be used with the expected `userdata.field = value` syntax.
|
||||
///
|
||||
/// If `add_meta_method` is used to set the `__newindex` metamethod, the `__newindex` metamethod will
|
||||
/// be used as a fall-back if no regular field is found.
|
||||
fn add_field_method_set<S, A, M>(&mut self, name: &S, method: M)
|
||||
where
|
||||
S: AsRef<[u8]> + ?Sized,
|
||||
A: FromLua<'lua>,
|
||||
M: 'static + MaybeSend + FnMut(&'lua Lua, &mut T, A) -> Result<()>;
|
||||
|
||||
/// Add a regular field getter as a function which accepts a generic [`AnyUserData`] of type `T`
|
||||
/// argument.
|
||||
///
|
||||
/// Prefer to use [`add_field_method_get`] as it is easier to use.
|
||||
///
|
||||
/// [`AnyUserData`]: struct.AnyUserData.html
|
||||
/// [`add_field_method_get`]: #method.add_field_method_get
|
||||
fn add_field_function_get<S, R, F>(&mut self, name: &S, function: F)
|
||||
where
|
||||
S: AsRef<[u8]> + ?Sized,
|
||||
R: ToLua<'lua>,
|
||||
F: 'static + MaybeSend + Fn(&'lua Lua, AnyUserData<'lua>) -> Result<R>;
|
||||
|
||||
/// Add a regular field setter as a function which accepts a generic [`AnyUserData`] of type `T`
|
||||
/// first argument.
|
||||
///
|
||||
/// Prefer to use [`add_field_method_set`] as it is easier to use.
|
||||
///
|
||||
/// [`AnyUserData`]: struct.AnyUserData.html
|
||||
/// [`add_field_method_set`]: #method.add_field_method_set
|
||||
fn add_field_function_set<S, A, F>(&mut self, name: &S, function: F)
|
||||
where
|
||||
S: AsRef<[u8]> + ?Sized,
|
||||
A: FromLua<'lua>,
|
||||
F: 'static + MaybeSend + FnMut(&'lua Lua, AnyUserData<'lua>, A) -> Result<()>;
|
||||
|
||||
/// Add a metamethod value computed from `f`.
|
||||
///
|
||||
/// This will initialize the metamethod value from `f` on `UserData` creation.
|
||||
///
|
||||
/// # Note
|
||||
///
|
||||
/// `mlua` will trigger an error on an attempt to define a protected metamethod,
|
||||
/// like `__gc` or `__metatable`.
|
||||
fn add_meta_field_with<S, R, F>(&mut self, meta: S, f: F)
|
||||
where
|
||||
S: Into<MetaMethod>,
|
||||
F: 'static + MaybeSend + Fn(&'lua Lua) -> Result<R>,
|
||||
R: ToLua<'lua>;
|
||||
|
||||
//
|
||||
// Below are internal methods used in generated code
|
||||
//
|
||||
|
||||
#[doc(hidden)]
|
||||
fn add_field_getter(&mut self, _name: Vec<u8>, _callback: Callback<'lua, 'static>) {}
|
||||
|
||||
#[doc(hidden)]
|
||||
fn add_field_setter(&mut self, _name: Vec<u8>, _callback: Callback<'lua, 'static>) {}
|
||||
}
|
||||
|
||||
/// Trait for custom userdata types.
|
||||
@@ -322,21 +540,21 @@ pub trait UserDataMethods<'lua, T: UserData> {
|
||||
/// # }
|
||||
/// ```
|
||||
///
|
||||
/// Custom methods and operators can be provided by implementing `add_methods` (refer to
|
||||
/// [`UserDataMethods`] for more information):
|
||||
/// Custom fields, methods and operators can be provided by implementing `add_fields` or `add_methods`
|
||||
/// (refer to [`UserDataFields`] and [`UserDataMethods`] for more information):
|
||||
///
|
||||
/// ```
|
||||
/// # use mlua::{Lua, MetaMethod, Result, UserData, UserDataMethods};
|
||||
/// # use mlua::{Lua, MetaMethod, Result, UserData, UserDataFields, UserDataMethods};
|
||||
/// # fn main() -> Result<()> {
|
||||
/// # let lua = Lua::new();
|
||||
/// struct MyUserData(i32);
|
||||
///
|
||||
/// impl UserData for MyUserData {
|
||||
/// fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
|
||||
/// methods.add_method("get", |_, this, _: ()| {
|
||||
/// Ok(this.0)
|
||||
/// });
|
||||
/// fn add_fields<'lua, F: UserDataFields<'lua, Self>>(fields: &mut F) {
|
||||
/// fields.add_field_method_get("val", |_, this| Ok(this.0));
|
||||
/// }
|
||||
///
|
||||
/// fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
|
||||
/// methods.add_method_mut("add", |_, this, value: i32| {
|
||||
/// this.0 += value;
|
||||
/// Ok(())
|
||||
@@ -351,9 +569,9 @@ pub trait UserDataMethods<'lua, T: UserData> {
|
||||
/// lua.globals().set("myobject", MyUserData(123))?;
|
||||
///
|
||||
/// lua.load(r#"
|
||||
/// assert(myobject:get() == 123)
|
||||
/// assert(myobject.val == 123)
|
||||
/// myobject:add(7)
|
||||
/// assert(myobject:get() == 130)
|
||||
/// assert(myobject.val == 130)
|
||||
/// assert(myobject + 10 == 140)
|
||||
/// "#).exec()?;
|
||||
/// # Ok(())
|
||||
@@ -362,18 +580,158 @@ pub trait UserDataMethods<'lua, T: UserData> {
|
||||
///
|
||||
/// [`ToLua`]: trait.ToLua.html
|
||||
/// [`FromLua`]: trait.FromLua.html
|
||||
/// [`UserDataFields`]: trait.UserDataFields.html
|
||||
/// [`UserDataMethods`]: trait.UserDataMethods.html
|
||||
pub trait UserData: Sized {
|
||||
/// Adds custom fields specific to this userdata.
|
||||
fn add_fields<'lua, F: UserDataFields<'lua, Self>>(_fields: &mut F) {}
|
||||
|
||||
/// Adds custom methods and operators specific to this userdata.
|
||||
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(_methods: &mut M) {}
|
||||
}
|
||||
|
||||
// Wraps UserData in a way to always implement `serde::Serialize` trait.
|
||||
pub(crate) struct UserDataCell<T>(RefCell<UserDataWrapped<T>>);
|
||||
|
||||
impl<T> UserDataCell<T> {
|
||||
pub(crate) fn new(data: T) -> Self {
|
||||
UserDataCell(RefCell::new(UserDataWrapped::new(data)))
|
||||
}
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
pub(crate) fn new_ser(data: T) -> Self
|
||||
where
|
||||
T: 'static + Serialize,
|
||||
{
|
||||
UserDataCell(RefCell::new(UserDataWrapped::new_ser(data)))
|
||||
}
|
||||
|
||||
// Immutably borrows the wrapped value.
|
||||
fn try_borrow(&self) -> Result<UserDataRef<T>> {
|
||||
self.0
|
||||
.try_borrow()
|
||||
.map(|r| UserDataRef(UserDataRefInner::Ref(r)))
|
||||
.map_err(|_| Error::UserDataBorrowError)
|
||||
}
|
||||
|
||||
// Mutably borrows the wrapped value.
|
||||
fn try_borrow_mut(&self) -> Result<UserDataRefMut<T>> {
|
||||
self.0
|
||||
.try_borrow_mut()
|
||||
.map(|r| UserDataRefMut(UserDataRefMutInner::Ref(r)))
|
||||
.map_err(|_| Error::UserDataBorrowMutError)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
impl Serialize for UserDataCell<()> {
|
||||
fn serialize<S>(&self, serializer: S) -> StdResult<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
let ser = self
|
||||
.0
|
||||
.try_borrow()
|
||||
.map_err(|_| ser::Error::custom(Error::UserDataBorrowError))?
|
||||
.ser;
|
||||
unsafe { (&*ser).serialize(serializer) }
|
||||
}
|
||||
}
|
||||
|
||||
/// A wrapper type for an immutably borrowed value from an `AnyUserData`.
|
||||
pub struct UserDataRef<'a, T>(UserDataRefInner<'a, T>);
|
||||
|
||||
enum UserDataRefInner<'a, T> {
|
||||
Ref(Ref<'a, UserDataWrapped<T>>),
|
||||
}
|
||||
|
||||
/// A wrapper type for a mutably borrowed value from an `AnyUserData`.
|
||||
pub struct UserDataRefMut<'a, T>(UserDataRefMutInner<'a, T>);
|
||||
|
||||
enum UserDataRefMutInner<'a, T> {
|
||||
Ref(RefMut<'a, UserDataWrapped<T>>),
|
||||
}
|
||||
|
||||
impl<T> Deref for UserDataRef<'_, T> {
|
||||
type Target = T;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
match &self.0 {
|
||||
UserDataRefInner::Ref(x) => &*x,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Deref for UserDataRefMut<'_, T> {
|
||||
type Target = T;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
match &self.0 {
|
||||
UserDataRefMutInner::Ref(x) => &*x,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> DerefMut for UserDataRefMut<'_, T> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
match &mut self.0 {
|
||||
UserDataRefMutInner::Ref(x) => &mut *x,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: fmt::Debug> fmt::Debug for UserDataRef<'_, T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
fmt::Debug::fmt(&*self as &T, f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: fmt::Debug> fmt::Debug for UserDataRefMut<'_, T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
fmt::Debug::fmt(&*self as &T, f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: fmt::Display> fmt::Display for UserDataRef<'_, T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
fmt::Display::fmt(&*self as &T, f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: fmt::Display> fmt::Display for UserDataRefMut<'_, T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
fmt::Display::fmt(&*self as &T, f)
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct UserDataWrapped<T> {
|
||||
pub(crate) data: *mut T,
|
||||
#[cfg(feature = "serialize")]
|
||||
ser: *mut dyn erased_serde::Serialize,
|
||||
}
|
||||
|
||||
impl<T> UserDataWrapped<T> {
|
||||
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")]
|
||||
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> Drop for UserDataWrapped<T> {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
@@ -386,42 +744,22 @@ impl<T> Drop for UserDataWrapped<T> {
|
||||
}
|
||||
}
|
||||
|
||||
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)),
|
||||
}
|
||||
}
|
||||
impl<T> Deref for UserDataWrapped<T> {
|
||||
type Target = T;
|
||||
|
||||
#[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 {
|
||||
fn deref(&self) -> &Self::Target {
|
||||
unsafe { &*self.data }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> AsMut<T> for UserDataWrapped<T> {
|
||||
fn as_mut(&mut self) -> &mut T {
|
||||
impl<T> DerefMut for UserDataWrapped<T> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
unsafe { &mut *self.data }
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
pub(crate) struct UserDataSerializeError;
|
||||
struct UserDataSerializeError;
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
impl Serialize for UserDataSerializeError {
|
||||
@@ -468,26 +806,18 @@ 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| {
|
||||
let cell_ref = cell.try_borrow().map_err(|_| Error::UserDataBorrowError)?;
|
||||
Ok(Ref::map(cell_ref, |x| unsafe { &*x.data }))
|
||||
})
|
||||
pub fn borrow<T: 'static + UserData>(&self) -> Result<UserDataRef<T>> {
|
||||
self.inspect(|cell| cell.try_borrow())
|
||||
}
|
||||
|
||||
/// Borrow this userdata mutably if it is of type `T`.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns a `UserDataBorrowMutError` if the userdata is already borrowed. Returns a
|
||||
/// `UserDataTypeMismatch` if the userdata is not of type `T`.
|
||||
pub fn borrow_mut<T: 'static + UserData>(&self) -> Result<RefMut<T>> {
|
||||
self.inspect(|cell| {
|
||||
let cell_ref = cell
|
||||
.try_borrow_mut()
|
||||
.map_err(|_| Error::UserDataBorrowMutError)?;
|
||||
Ok(RefMut::map(cell_ref, |x| unsafe { &mut *x.data }))
|
||||
})
|
||||
/// Returns a `UserDataBorrowMutError` if the userdata cannot be mutably borrowed.
|
||||
/// Returns a `UserDataTypeMismatch` if the userdata is not of type `T`.
|
||||
pub fn borrow_mut<T: 'static + UserData>(&self) -> Result<UserDataRefMut<T>> {
|
||||
self.inspect(|cell| cell.try_borrow_mut())
|
||||
}
|
||||
|
||||
/// Sets an associated value to this `AnyUserData`.
|
||||
@@ -500,19 +830,21 @@ impl<'lua> AnyUserData<'lua> {
|
||||
let lua = self.0.lua;
|
||||
#[cfg(any(feature = "lua52", feature = "lua51", feature = "luajit"))]
|
||||
let v = {
|
||||
// Lua 5.2/5.1 allows to store only a table. Then we will wrap the value.
|
||||
let t = lua.create_table()?;
|
||||
// Lua <= 5.2 allows to store only a table. Then we will wrap the value.
|
||||
let t = lua.create_table_with_capacity(1, 0)?;
|
||||
t.raw_set(1, v)?;
|
||||
crate::Value::Table(t)
|
||||
Value::Table(t)
|
||||
};
|
||||
#[cfg(any(feature = "lua54", feature = "lua53"))]
|
||||
let v = v.to_lua(lua)?;
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(lua.state);
|
||||
assert_stack(lua.state, 2);
|
||||
lua.push_ref(&self.0);
|
||||
check_stack(lua.state, 3)?;
|
||||
|
||||
lua.push_userdata_ref(&self.0, false)?;
|
||||
lua.push_value(v)?;
|
||||
ffi::lua_setuservalue(lua.state, -2);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -526,57 +858,54 @@ impl<'lua> AnyUserData<'lua> {
|
||||
let lua = self.0.lua;
|
||||
let res = unsafe {
|
||||
let _sg = StackGuard::new(lua.state);
|
||||
assert_stack(lua.state, 3);
|
||||
lua.push_ref(&self.0);
|
||||
check_stack(lua.state, 3)?;
|
||||
|
||||
lua.push_userdata_ref(&self.0, false)?;
|
||||
ffi::lua_getuservalue(lua.state, -1);
|
||||
lua.pop_value()
|
||||
};
|
||||
#[cfg(any(feature = "lua52", feature = "lua51", feature = "luajit"))]
|
||||
return crate::Table::from_lua(res, lua)?.get(1);
|
||||
return match <Option<Table>>::from_lua(res, lua)? {
|
||||
Some(t) => t.get(1),
|
||||
None => V::from_lua(Value::Nil, lua),
|
||||
};
|
||||
#[cfg(any(feature = "lua54", feature = "lua53"))]
|
||||
V::from_lua(res, lua)
|
||||
}
|
||||
|
||||
/// Checks for a metamethod in this `AnyUserData`
|
||||
pub fn has_metamethod(&self, method: MetaMethod) -> Result<bool> {
|
||||
match self.get_metatable() {
|
||||
Ok(mt) => {
|
||||
let name = self.0.lua.create_string(method.name())?;
|
||||
if let Value::Nil = mt.raw_get(name)? {
|
||||
Ok(false)
|
||||
} else {
|
||||
Ok(true)
|
||||
}
|
||||
}
|
||||
Err(Error::UserDataTypeMismatch) => Ok(false),
|
||||
Err(e) => Err(e),
|
||||
}
|
||||
/// Returns a metatable of this `UserData`.
|
||||
///
|
||||
/// Returned [`UserDataMetatable`] object wraps the original metatable and
|
||||
/// provides safe access to its methods.
|
||||
///
|
||||
/// For `T: UserData + 'static` returned metatable is shared among all instances of type `T`.
|
||||
///
|
||||
/// [`UserDataMetatable`]: struct.UserDataMetatable.html
|
||||
pub fn get_metatable(&self) -> Result<UserDataMetatable<'lua>> {
|
||||
self.get_raw_metatable().map(UserDataMetatable)
|
||||
}
|
||||
|
||||
fn get_metatable(&self) -> Result<Table<'lua>> {
|
||||
fn get_raw_metatable(&self) -> Result<Table<'lua>> {
|
||||
unsafe {
|
||||
let lua = self.0.lua;
|
||||
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 Err(Error::UserDataTypeMismatch);
|
||||
}
|
||||
check_stack(lua.state, 3)?;
|
||||
|
||||
lua.push_userdata_ref(&self.0, false)?;
|
||||
ffi::lua_getmetatable(lua.state, -1); // Checked that non-empty on the previous call
|
||||
Ok(Table(lua.pop_ref()))
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn equals<T: AsRef<Self>>(&self, other: T) -> Result<bool> {
|
||||
let other = other.as_ref();
|
||||
// Uses lua_rawequal() under the hood
|
||||
if self == other {
|
||||
return Ok(true);
|
||||
}
|
||||
|
||||
let mt = self.get_metatable()?;
|
||||
if mt != other.get_metatable()? {
|
||||
let mt = self.get_raw_metatable()?;
|
||||
if mt != other.get_raw_metatable()? {
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
@@ -589,39 +918,52 @@ impl<'lua> AnyUserData<'lua> {
|
||||
Ok(false)
|
||||
}
|
||||
|
||||
pub(crate) fn type_id(&self) -> Result<TypeId> {
|
||||
let lua = self.0.lua;
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(lua.state);
|
||||
check_stack(lua.state, 5)?;
|
||||
|
||||
// Push userdata with metatable
|
||||
lua.push_userdata_ref(&self.0, true)?;
|
||||
|
||||
// Get the special `__mlua_type_id`
|
||||
push_string(lua.state, "__mlua_type_id")?;
|
||||
if ffi::lua_rawget(lua.state, -2) != ffi::LUA_TUSERDATA {
|
||||
return Err(Error::UserDataTypeMismatch);
|
||||
}
|
||||
|
||||
Ok(*(ffi::lua_touserdata(lua.state, -1) as *const TypeId))
|
||||
}
|
||||
}
|
||||
|
||||
fn inspect<'a, T, R, F>(&'a self, func: F) -> Result<R>
|
||||
where
|
||||
T: 'static + UserData,
|
||||
F: FnOnce(&'a UserDataCell<T>) -> Result<R>,
|
||||
{
|
||||
let lua = self.0.lua;
|
||||
unsafe {
|
||||
let lua = self.0.lua;
|
||||
let _sg = StackGuard::new(lua.state);
|
||||
assert_stack(lua.state, 3);
|
||||
check_stack(lua.state, 3)?;
|
||||
|
||||
lua.push_ref(&self.0);
|
||||
|
||||
if ffi::lua_getmetatable(lua.state, -1) == 0 {
|
||||
Err(Error::UserDataTypeMismatch)
|
||||
} else {
|
||||
ffi::lua_rawgeti(
|
||||
lua.state,
|
||||
ffi::LUA_REGISTRYINDEX,
|
||||
lua.userdata_metatable::<T>()? as ffi::lua_Integer,
|
||||
);
|
||||
return Err(Error::UserDataTypeMismatch);
|
||||
}
|
||||
lua.push_userdata_metatable::<T>()?;
|
||||
|
||||
if ffi::lua_rawequal(lua.state, -1, -2) == 0 {
|
||||
// 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)
|
||||
}
|
||||
if ffi::lua_rawequal(lua.state, -1, -2) == 0 {
|
||||
// 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 {
|
||||
func(&*get_userdata::<UserDataCell<T>>(lua.state, -3))
|
||||
Err(Error::UserDataTypeMismatch)
|
||||
}
|
||||
} else {
|
||||
func(&*get_userdata::<UserDataCell<T>>(lua.state, -3))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -640,23 +982,95 @@ impl<'lua> AsRef<AnyUserData<'lua>> for AnyUserData<'lua> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Handle to a `UserData` metatable.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct UserDataMetatable<'lua>(pub(crate) Table<'lua>);
|
||||
|
||||
impl<'lua> UserDataMetatable<'lua> {
|
||||
/// Gets the value associated to `key` from the metatable.
|
||||
///
|
||||
/// If no value is associated to `key`, returns the `Nil` value.
|
||||
/// Access to restricted metamethods such as `__gc` or `__metatable` will cause an error.
|
||||
pub fn get<K: Into<MetaMethod>, V: FromLua<'lua>>(&self, key: K) -> Result<V> {
|
||||
self.0.raw_get(key.into().validate()?.name())
|
||||
}
|
||||
|
||||
/// Sets a key-value pair in the metatable.
|
||||
///
|
||||
/// If the value is `Nil`, this will effectively remove the `key`.
|
||||
/// Access to restricted metamethods such as `__gc` or `__metatable` will cause an error.
|
||||
/// Setting `__index` or `__newindex` metamethods is also restricted because their values are cached
|
||||
/// for `mlua` internal usage.
|
||||
pub fn set<K: Into<MetaMethod>, V: ToLua<'lua>>(&self, key: K, value: V) -> Result<()> {
|
||||
let key = key.into().validate()?;
|
||||
// `__index` and `__newindex` cannot be changed in runtime, because values are cached
|
||||
if key == MetaMethod::Index || key == MetaMethod::NewIndex {
|
||||
return Err(Error::MetaMethodRestricted(key.to_string()));
|
||||
}
|
||||
self.0.raw_set(key.name(), value)
|
||||
}
|
||||
|
||||
/// Checks whether the metatable contains a non-nil value for `key`.
|
||||
pub fn contains<K: Into<MetaMethod>>(&self, key: K) -> Result<bool> {
|
||||
self.0.contains_key(key.into().validate()?.name())
|
||||
}
|
||||
|
||||
/// Consumes this metatable and returns an iterator over the pairs of the metatable.
|
||||
///
|
||||
/// The pairs are wrapped in a [`Result`], since they are lazily converted to `V` type.
|
||||
///
|
||||
/// [`Result`]: type.Result.html
|
||||
pub fn pairs<V: FromLua<'lua>>(self) -> UserDataMetatablePairs<'lua, V> {
|
||||
UserDataMetatablePairs(self.0.pairs())
|
||||
}
|
||||
}
|
||||
|
||||
/// An iterator over the pairs of a [`UserData`] metatable.
|
||||
///
|
||||
/// It skips restricted metamethods, such as `__gc` or `__metatable`.
|
||||
///
|
||||
/// This struct is created by the [`UserDataMetatable::pairs`] method.
|
||||
///
|
||||
/// [`UserData`]: trait.UserData.html
|
||||
/// [`UserDataMetatable::pairs`]: struct.UserDataMetatable.html#method.pairs
|
||||
pub struct UserDataMetatablePairs<'lua, V>(TablePairs<'lua, StdString, V>);
|
||||
|
||||
impl<'lua, V> Iterator for UserDataMetatablePairs<'lua, V>
|
||||
where
|
||||
V: FromLua<'lua>,
|
||||
{
|
||||
type Item = Result<(MetaMethod, V)>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
loop {
|
||||
match self.0.next()? {
|
||||
Ok((key, value)) => {
|
||||
// Skip restricted metamethods
|
||||
if let Ok(metamethod) = MetaMethod::from(key).validate() {
|
||||
break Some(Ok((metamethod, value)));
|
||||
}
|
||||
}
|
||||
Err(e) => break Some(Err(e)),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[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 {
|
||||
unsafe {
|
||||
let lua = self.0.lua;
|
||||
let _sg = StackGuard::new(lua.state);
|
||||
assert_stack(lua.state, 2);
|
||||
check_stack(lua.state, 3).map_err(ser::Error::custom)?;
|
||||
|
||||
lua.push_userdata_ref(&self.0)?;
|
||||
lua.push_userdata_ref(&self.0, false)
|
||||
.map_err(ser::Error::custom)?;
|
||||
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)
|
||||
ud.serialize(serializer)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+439
-308
File diff suppressed because it is too large
Load Diff
+10
-10
@@ -8,7 +8,6 @@ use {
|
||||
};
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
use crate::ffi;
|
||||
use crate::function::Function;
|
||||
use crate::lua::Lua;
|
||||
use crate::string::String;
|
||||
@@ -17,10 +16,9 @@ use crate::thread::Thread;
|
||||
use crate::types::{Integer, LightUserData, Number};
|
||||
use crate::userdata::AnyUserData;
|
||||
|
||||
/// A dynamically typed Lua value. The `String`, `Table`, `Function`, `Thread`, and `UserData`
|
||||
/// variants contain handle types into the internal Lua state. It is a logic error to mix handle
|
||||
/// types between separate `Lua` instances, or between a parent `Lua` instance and one received as a
|
||||
/// parameter in a Rust callback, and doing so will result in a panic.
|
||||
/// A dynamically typed Lua value. The `String`, `Table`, `Function`, `Thread`, and `UserData`
|
||||
/// variants contain handle types into the internal Lua state. It is a logic error to mix handle
|
||||
/// types between separate `Lua` instances, and doing so will result in a panic.
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Value<'lua> {
|
||||
/// The Lua value `nil`.
|
||||
@@ -48,7 +46,7 @@ pub enum Value<'lua> {
|
||||
/// Reference to a userdata object that holds a custom type which implements `UserData`.
|
||||
/// Special builtin userdata types will be represented as other `Value` variants.
|
||||
UserData(AnyUserData<'lua>),
|
||||
/// `Error` is a special builtin userdata type. When received from Lua it is implicitly cloned.
|
||||
/// `Error` is a special builtin userdata type. When received from Lua it is implicitly cloned.
|
||||
Error(Error),
|
||||
}
|
||||
pub use self::Value::Nil;
|
||||
@@ -96,8 +94,8 @@ impl<'lua> PartialEq for Value<'lua> {
|
||||
(Value::Boolean(a), Value::Boolean(b)) => a == b,
|
||||
(Value::LightUserData(a), Value::LightUserData(b)) => a == b,
|
||||
(Value::Integer(a), Value::Integer(b)) => *a == *b,
|
||||
(Value::Integer(a), Value::Number(b)) => *a as ffi::lua_Number == *b,
|
||||
(Value::Number(a), Value::Integer(b)) => *a == *b as ffi::lua_Number,
|
||||
(Value::Integer(a), Value::Number(b)) => *a as Number == *b,
|
||||
(Value::Number(a), Value::Integer(b)) => *a == *b as Number,
|
||||
(Value::Number(a), Value::Number(b)) => *a == *b,
|
||||
(Value::String(a), Value::String(b)) => a == b,
|
||||
(Value::Table(a), Value::Table(b)) => a == b,
|
||||
@@ -125,8 +123,10 @@ impl<'lua> Serialize for Value<'lua> {
|
||||
match self {
|
||||
Value::Nil => serializer.serialize_unit(),
|
||||
Value::Boolean(b) => serializer.serialize_bool(*b),
|
||||
Value::Integer(i) => serializer.serialize_i64(*i),
|
||||
Value::Number(n) => serializer.serialize_f64(*n),
|
||||
#[allow(clippy::useless_conversion)]
|
||||
Value::Integer(i) => serializer.serialize_i64((*i).into()),
|
||||
#[allow(clippy::useless_conversion)]
|
||||
Value::Number(n) => serializer.serialize_f64((*n).into()),
|
||||
Value::String(s) => s.serialize(serializer),
|
||||
Value::Table(t) => t.serialize(serializer),
|
||||
Value::UserData(ud) => ud.serialize(serializer),
|
||||
|
||||
+36
-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
-13
@@ -1,20 +1,8 @@
|
||||
#![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};
|
||||
|
||||
#[test]
|
||||
fn byte_string_round_trip() -> Result<()> {
|
||||
fn test_byte_string_round_trip() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
lua.load(
|
||||
|
||||
@@ -0,0 +1,125 @@
|
||||
use std::borrow::Cow;
|
||||
use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet};
|
||||
use std::ffi::{CStr, CString};
|
||||
|
||||
use maplit::{btreemap, btreeset, hashmap, hashset};
|
||||
use mlua::{Lua, Result};
|
||||
|
||||
#[test]
|
||||
fn test_conv_vec() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let v = vec![1, 2, 3];
|
||||
lua.globals().set("v", v.clone())?;
|
||||
let v2: Vec<i32> = lua.globals().get("v")?;
|
||||
assert_eq!(v, v2);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_conv_hashmap() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let map = hashmap! {"hello".to_string() => "world".to_string()};
|
||||
lua.globals().set("map", map.clone())?;
|
||||
let map2: HashMap<String, String> = lua.globals().get("map")?;
|
||||
assert_eq!(map, map2);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_conv_hashset() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let set = hashset! {"hello".to_string(), "world".to_string()};
|
||||
lua.globals().set("set", set.clone())?;
|
||||
let set2: HashSet<String> = lua.globals().get("set")?;
|
||||
assert_eq!(set, set2);
|
||||
|
||||
let set3 = lua.load(r#"{"a", "b", "c"}"#).eval::<HashSet<String>>()?;
|
||||
assert_eq!(set3, hashset! { "a".into(), "b".into(), "c".into() });
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_conv_btreemap() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let map = btreemap! {"hello".to_string() => "world".to_string()};
|
||||
lua.globals().set("map", map.clone())?;
|
||||
let map2: BTreeMap<String, String> = lua.globals().get("map")?;
|
||||
assert_eq!(map, map2);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_conv_btreeset() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let set = btreeset! {"hello".to_string(), "world".to_string()};
|
||||
lua.globals().set("set", set.clone())?;
|
||||
let set2: BTreeSet<String> = lua.globals().get("set")?;
|
||||
assert_eq!(set, set2);
|
||||
|
||||
let set3 = lua.load(r#"{"a", "b", "c"}"#).eval::<BTreeSet<String>>()?;
|
||||
assert_eq!(set3, btreeset! { "a".into(), "b".into(), "c".into() });
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_conv_cstring() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let s = CString::new(b"hello".to_vec()).unwrap();
|
||||
lua.globals().set("s", s.clone())?;
|
||||
let s2: CString = lua.globals().get("s")?;
|
||||
assert_eq!(s, s2);
|
||||
|
||||
let cs = CStr::from_bytes_with_nul(b"hello\0").unwrap();
|
||||
lua.globals().set("cs", cs)?;
|
||||
let cs2: CString = lua.globals().get("cs")?;
|
||||
assert_eq!(cs, cs2.as_c_str());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_conv_cow() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let s = Cow::from("hello");
|
||||
lua.globals().set("s", s.clone())?;
|
||||
let s2: String = lua.globals().get("s")?;
|
||||
assert_eq!(s, s2);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_conv_boxed_str() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let s = String::from("hello").into_boxed_str();
|
||||
lua.globals().set("s", s.clone())?;
|
||||
let s2: Box<str> = lua.globals().get("s")?;
|
||||
assert_eq!(s, s2);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_conv_boxed_slice() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let v = vec![1, 2, 3].into_boxed_slice();
|
||||
lua.globals().set("v", v.clone())?;
|
||||
let v2: Box<[i32]> = lua.globals().get("v")?;
|
||||
assert_eq!(v, v2);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
+1
-13
@@ -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]
|
||||
@@ -95,7 +83,7 @@ fn test_dump() -> Result<()> {
|
||||
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()?;
|
||||
let concat = lua.load(&concat_lua.dump(false)).into_function()?;
|
||||
|
||||
assert_eq!(concat.call::<_, String>(("foo", "bar"))?, "foobar");
|
||||
|
||||
|
||||
+6
-18
@@ -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;
|
||||
@@ -18,7 +6,7 @@ use std::sync::{Arc, Mutex};
|
||||
use mlua::{Error, HookTriggers, Lua, Result, Value};
|
||||
|
||||
#[test]
|
||||
fn line_counts() -> Result<()> {
|
||||
fn test_line_counts() -> Result<()> {
|
||||
let output = Arc::new(Mutex::new(Vec::new()));
|
||||
let hook_output = output.clone();
|
||||
|
||||
@@ -55,7 +43,7 @@ fn line_counts() -> Result<()> {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn function_calls() -> Result<()> {
|
||||
fn test_function_calls() -> Result<()> {
|
||||
let output = Arc::new(Mutex::new(Vec::new()));
|
||||
let hook_output = output.clone();
|
||||
|
||||
@@ -107,7 +95,7 @@ fn function_calls() -> Result<()> {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn error_within_hook() -> Result<()> {
|
||||
fn test_error_within_hook() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
lua.set_hook(
|
||||
HookTriggers {
|
||||
@@ -138,7 +126,7 @@ fn error_within_hook() -> Result<()> {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn limit_execution_instructions() -> Result<()> {
|
||||
fn test_limit_execution_instructions() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
let mut max_instructions = 10000;
|
||||
|
||||
@@ -177,7 +165,7 @@ fn limit_execution_instructions() -> Result<()> {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hook_removal() -> Result<()> {
|
||||
fn test_hook_removal() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
lua.set_hook(
|
||||
@@ -200,7 +188,7 @@ fn hook_removal() -> Result<()> {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hook_swap_within_hook() -> Result<()> {
|
||||
fn test_hook_swap_within_hook() -> Result<()> {
|
||||
thread_local! {
|
||||
static TL_LUA: RefCell<Option<Lua>> = RefCell::new(None);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
#![cfg(feature = "macros")]
|
||||
|
||||
use mlua::{chunk, Lua, Result};
|
||||
|
||||
#[test]
|
||||
fn test_chunk_macro() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let name = "Rustacean";
|
||||
let table = vec![1];
|
||||
|
||||
let data = lua.create_table()?;
|
||||
data.raw_set("num", 1)?;
|
||||
|
||||
lua.globals().set("g", 123)?;
|
||||
|
||||
lua.load(chunk! {
|
||||
assert($name == "Rustacean")
|
||||
assert($table[1] == 1)
|
||||
assert($data.num == 1)
|
||||
assert(g == 123)
|
||||
s = 321
|
||||
})
|
||||
.exec()?;
|
||||
|
||||
assert_eq!(lua.globals().get::<_, i32>("s")?, 321);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
+6
-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 std::sync::Arc;
|
||||
|
||||
use mlua::{Lua, Result, UserData};
|
||||
@@ -50,6 +38,9 @@ fn test_gc_control() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
let globals = lua.globals();
|
||||
|
||||
#[cfg(feature = "lua54")]
|
||||
assert_eq!(lua.gc_gen(0, 0), mlua::GCMode::Incremental);
|
||||
|
||||
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
|
||||
{
|
||||
assert!(lua.gc_is_running());
|
||||
@@ -71,6 +62,9 @@ fn test_gc_control() -> Result<()> {
|
||||
lua.gc_collect()?;
|
||||
assert_eq!(Arc::strong_count(&rc), 1);
|
||||
|
||||
#[cfg(feature = "lua54")]
|
||||
assert_eq!(lua.gc_inc(0, 0, 0), mlua::GCMode::Generational);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
@@ -15,6 +15,32 @@ fn test_module() -> Result<()> {
|
||||
.exec()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_module_multi() -> Result<()> {
|
||||
let lua = make_lua()?;
|
||||
lua.load(
|
||||
r#"
|
||||
local mod = require("rust_module")
|
||||
local mod2 = require("rust_module.second")
|
||||
assert(mod.check_userdata(mod2.userdata) == 123)
|
||||
"#,
|
||||
)
|
||||
.exec()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_module_error() -> Result<()> {
|
||||
let lua = make_lua()?;
|
||||
lua.load(
|
||||
r#"
|
||||
local ok, err = pcall(require, "rust_module.error")
|
||||
assert(not ok)
|
||||
assert(string.find(tostring(err), "custom module error"))
|
||||
"#,
|
||||
)
|
||||
.exec()
|
||||
}
|
||||
|
||||
#[cfg(any(
|
||||
feature = "lua54",
|
||||
feature = "lua53",
|
||||
|
||||
+167
-49
@@ -1,22 +1,14 @@
|
||||
#![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 std::sync::Arc;
|
||||
|
||||
use mlua::{Error, Function, Lua, MetaMethod, Result, String, UserData, UserDataMethods};
|
||||
use mlua::{
|
||||
AnyUserData, Error, Function, Lua, MetaMethod, Result, String, UserData, UserDataFields,
|
||||
UserDataMethods,
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn scope_func() -> Result<()> {
|
||||
fn test_scope_func() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let rc = Rc::new(Cell::new(0));
|
||||
@@ -35,7 +27,10 @@ fn scope_func() -> Result<()> {
|
||||
assert_eq!(Rc::strong_count(&rc), 1);
|
||||
|
||||
match lua.globals().get::<_, Function>("bad")?.call::<_, ()>(()) {
|
||||
Err(Error::CallbackError { .. }) => {}
|
||||
Err(Error::CallbackError { ref cause, .. }) => match *cause.as_ref() {
|
||||
Error::CallbackDestructed => {}
|
||||
ref err => panic!("wrong error type {:?}", err),
|
||||
},
|
||||
r => panic!("improper return for destructed function: {:?}", r),
|
||||
};
|
||||
|
||||
@@ -43,36 +38,7 @@ fn scope_func() -> Result<()> {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn scope_drop() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
struct MyUserdata(Rc<()>);
|
||||
impl UserData for MyUserdata {
|
||||
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
|
||||
methods.add_method("method", |_, _, ()| Ok(()));
|
||||
}
|
||||
}
|
||||
|
||||
let rc = Rc::new(());
|
||||
|
||||
lua.scope(|scope| {
|
||||
lua.globals()
|
||||
.set("test", 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 { .. }) => {}
|
||||
r => panic!("improper return for destructed userdata: {:?}", r),
|
||||
};
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn scope_capture() -> Result<()> {
|
||||
fn test_scope_capture() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let mut i = 0;
|
||||
@@ -90,7 +56,7 @@ fn scope_capture() -> Result<()> {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn outer_lua_access() -> Result<()> {
|
||||
fn test_scope_outer_lua_access() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let table = lua.create_table()?;
|
||||
@@ -105,7 +71,42 @@ fn outer_lua_access() -> Result<()> {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn scope_userdata_methods() -> Result<()> {
|
||||
fn test_scope_userdata_fields() -> Result<()> {
|
||||
struct MyUserData<'a>(&'a Cell<i64>);
|
||||
|
||||
impl<'a> UserData for MyUserData<'a> {
|
||||
fn add_fields<'lua, F: UserDataFields<'lua, Self>>(fields: &mut F) {
|
||||
fields.add_field_method_get("val", |_, data| Ok(data.0.get()));
|
||||
fields.add_field_method_set("val", |_, data, val| {
|
||||
data.0.set(val);
|
||||
Ok(())
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
let lua = Lua::new();
|
||||
|
||||
let i = Cell::new(42);
|
||||
let f: Function = lua
|
||||
.load(
|
||||
r#"
|
||||
function(u)
|
||||
assert(u.val == 42)
|
||||
u.val = 44
|
||||
end
|
||||
"#,
|
||||
)
|
||||
.eval()?;
|
||||
|
||||
lua.scope(|scope| f.call::<_, ()>(scope.create_nonstatic_userdata(MyUserData(&i))?))?;
|
||||
|
||||
assert_eq!(i.get(), 44);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_scope_userdata_methods() -> Result<()> {
|
||||
struct MyUserData<'a>(&'a Cell<i64>);
|
||||
|
||||
impl<'a> UserData for MyUserData<'a> {
|
||||
@@ -146,7 +147,7 @@ fn scope_userdata_methods() -> Result<()> {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn scope_userdata_functions() -> Result<()> {
|
||||
fn test_scope_userdata_functions() -> Result<()> {
|
||||
struct MyUserData<'a>(&'a i64);
|
||||
|
||||
impl<'a> UserData for MyUserData<'a> {
|
||||
@@ -188,7 +189,7 @@ fn scope_userdata_functions() -> Result<()> {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn scope_userdata_mismatch() -> Result<()> {
|
||||
fn test_scope_userdata_mismatch() -> Result<()> {
|
||||
struct MyUserData<'a>(&'a Cell<i64>);
|
||||
|
||||
impl<'a> UserData for MyUserData<'a> {
|
||||
@@ -238,3 +239,120 @@ fn scope_userdata_mismatch() -> Result<()> {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_scope_userdata_drop() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
struct MyUserData(Rc<()>);
|
||||
|
||||
impl UserData for MyUserData {
|
||||
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
|
||||
methods.add_method("method", |_, _, ()| Ok(()));
|
||||
}
|
||||
}
|
||||
|
||||
struct MyUserDataArc(Arc<()>);
|
||||
|
||||
impl UserData for MyUserDataArc {}
|
||||
|
||||
let rc = Rc::new(());
|
||||
let arc = Arc::new(());
|
||||
lua.scope(|scope| {
|
||||
let ud = scope.create_userdata(MyUserData(rc.clone()))?;
|
||||
ud.set_user_value(MyUserDataArc(arc.clone()))?;
|
||||
lua.globals().set("ud", ud)?;
|
||||
assert_eq!(Rc::strong_count(&rc), 2);
|
||||
assert_eq!(Arc::strong_count(&arc), 2);
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
lua.gc_collect()?;
|
||||
assert_eq!(Rc::strong_count(&rc), 1);
|
||||
assert_eq!(Arc::strong_count(&arc), 1);
|
||||
|
||||
match lua.load("ud:method()").exec() {
|
||||
Err(Error::CallbackError { ref cause, .. }) => match cause.as_ref() {
|
||||
Error::CallbackDestructed => {}
|
||||
err => panic!("expected CallbackDestructed, got {:?}", err),
|
||||
},
|
||||
r => panic!("improper return for destructed userdata: {:?}", r),
|
||||
};
|
||||
|
||||
let ud = lua.globals().get::<_, AnyUserData>("ud")?;
|
||||
match ud.borrow::<MyUserData>() {
|
||||
Ok(_) => panic!("succesfull borrow for destructed userdata"),
|
||||
Err(Error::UserDataDestructed) => {}
|
||||
Err(err) => panic!("improper borrow error for destructed userdata: {:?}", err),
|
||||
}
|
||||
|
||||
match ud.get_metatable() {
|
||||
Ok(_) => panic!("successful metatable retrieval of destructed userdata"),
|
||||
Err(Error::UserDataDestructed) => {}
|
||||
Err(err) => panic!(
|
||||
"improper metatable error for destructed userdata: {:?}",
|
||||
err
|
||||
),
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_scope_nonstatic_userdata_drop() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
struct MyUserData<'a>(&'a Cell<i64>, Arc<()>);
|
||||
|
||||
impl<'a> UserData for MyUserData<'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(())
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
struct MyUserDataArc(Arc<()>);
|
||||
|
||||
impl UserData for MyUserDataArc {}
|
||||
|
||||
let i = Cell::new(1);
|
||||
let arc = Arc::new(());
|
||||
lua.scope(|scope| {
|
||||
let ud = scope.create_nonstatic_userdata(MyUserData(&i, arc.clone()))?;
|
||||
ud.set_user_value(MyUserDataArc(arc.clone()))?;
|
||||
lua.globals().set("ud", ud)?;
|
||||
lua.load("ud:inc()").exec()?;
|
||||
assert_eq!(Arc::strong_count(&arc), 3);
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
lua.gc_collect()?;
|
||||
assert_eq!(Arc::strong_count(&arc), 1);
|
||||
|
||||
match lua.load("ud:inc()").exec() {
|
||||
Err(Error::CallbackError { ref cause, .. }) => match cause.as_ref() {
|
||||
Error::CallbackDestructed => {}
|
||||
err => panic!("expected CallbackDestructed, got {:?}", err),
|
||||
},
|
||||
r => panic!("improper return for destructed userdata: {:?}", r),
|
||||
};
|
||||
|
||||
let ud = lua.globals().get::<_, AnyUserData>("ud")?;
|
||||
match ud.borrow::<MyUserData>() {
|
||||
Ok(_) => panic!("succesfull borrow for destructed userdata"),
|
||||
Err(Error::UserDataDestructed) => {}
|
||||
Err(err) => panic!("improper borrow error for destructed userdata: {:?}", err),
|
||||
}
|
||||
match ud.get_metatable() {
|
||||
Ok(_) => panic!("successful metatable retrieval of destructed userdata"),
|
||||
Err(Error::UserDataDestructed) => {}
|
||||
Err(err) => panic!(
|
||||
"improper metatable error for destructed userdata: {:?}",
|
||||
err
|
||||
),
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
+95
-21
@@ -1,17 +1,8 @@
|
||||
#![cfg(feature = "serialize")]
|
||||
#![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::collections::HashMap;
|
||||
|
||||
use mlua::{Error, Lua, LuaSerdeExt, Result as LuaResult, UserData, Value};
|
||||
use mlua::{Error, Lua, LuaSerdeExt, Result as LuaResult, SerializeOptions, UserData, Value};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[test]
|
||||
@@ -19,17 +10,17 @@ fn test_serialize() -> Result<(), Box<dyn std::error::Error>> {
|
||||
#[derive(Serialize)]
|
||||
struct MyUserData(i64, String);
|
||||
|
||||
impl UserData for MyUserData {};
|
||||
impl UserData for MyUserData {}
|
||||
|
||||
let lua = Lua::new();
|
||||
let globals = lua.globals();
|
||||
|
||||
let ud = lua.create_ser_userdata(MyUserData(123, "test userdata".into()))?;
|
||||
globals.set("ud", ud)?;
|
||||
globals.set("null", lua.null()?)?;
|
||||
globals.set("null", lua.null())?;
|
||||
|
||||
let empty_array = lua.create_table()?;
|
||||
empty_array.set_metatable(Some(lua.array_metatable()?));
|
||||
empty_array.set_metatable(Some(lua.array_metatable()));
|
||||
globals.set("empty_array", empty_array)?;
|
||||
|
||||
let val = lua
|
||||
@@ -81,7 +72,7 @@ fn test_serialize_in_scope() -> LuaResult<()> {
|
||||
#[derive(Serialize, Clone)]
|
||||
struct MyUserData(i64, String);
|
||||
|
||||
impl UserData for MyUserData {};
|
||||
impl UserData for MyUserData {}
|
||||
|
||||
let lua = Lua::new();
|
||||
lua.scope(|scope| {
|
||||
@@ -104,6 +95,19 @@ fn test_serialize_in_scope() -> LuaResult<()> {
|
||||
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(())
|
||||
}
|
||||
|
||||
@@ -112,7 +116,7 @@ fn test_serialize_failure() -> Result<(), Box<dyn std::error::Error>> {
|
||||
#[derive(Serialize)]
|
||||
struct MyUserData(i64);
|
||||
|
||||
impl UserData for MyUserData {};
|
||||
impl UserData for MyUserData {}
|
||||
|
||||
let lua = Lua::new();
|
||||
|
||||
@@ -141,14 +145,14 @@ fn test_serialize_failure() -> Result<(), Box<dyn std::error::Error>> {
|
||||
fn test_to_value_struct() -> LuaResult<()> {
|
||||
let lua = Lua::new();
|
||||
let globals = lua.globals();
|
||||
globals.set("null", lua.null()?)?;
|
||||
globals.set("null", lua.null())?;
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct Test {
|
||||
name: String,
|
||||
key: i64,
|
||||
data: Option<bool>,
|
||||
};
|
||||
}
|
||||
|
||||
let test = Test {
|
||||
name: "alex".to_string(),
|
||||
@@ -171,14 +175,14 @@ fn test_to_value_struct() -> LuaResult<()> {
|
||||
fn test_to_value_enum() -> LuaResult<()> {
|
||||
let lua = Lua::new();
|
||||
let globals = lua.globals();
|
||||
globals.set("null", lua.null()?)?;
|
||||
globals.set("null", lua.null())?;
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct Test {
|
||||
name: String,
|
||||
key: i64,
|
||||
data: Option<bool>,
|
||||
};
|
||||
}
|
||||
|
||||
let test = Test {
|
||||
name: "alex".to_string(),
|
||||
@@ -228,6 +232,76 @@ fn test_to_value_enum() -> LuaResult<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_to_value_with_options() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let lua = Lua::new();
|
||||
let globals = lua.globals();
|
||||
globals.set("null", lua.null())?;
|
||||
|
||||
// set_array_metatable
|
||||
let data = lua.to_value_with(
|
||||
&Vec::<i32>::new(),
|
||||
SerializeOptions::new().set_array_metatable(false),
|
||||
)?;
|
||||
globals.set("data", data)?;
|
||||
lua.load(
|
||||
r#"
|
||||
assert(type(data) == "table" and #data == 0)
|
||||
assert(getmetatable(data) == nil)
|
||||
"#,
|
||||
)
|
||||
.exec()?;
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct UnitStruct;
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct MyData {
|
||||
map: HashMap<&'static str, Option<i32>>,
|
||||
unit: (),
|
||||
unitstruct: UnitStruct,
|
||||
}
|
||||
|
||||
// serialize_none_to_null
|
||||
let mut map = HashMap::new();
|
||||
map.insert("key", None);
|
||||
let mydata = MyData {
|
||||
map,
|
||||
unit: (),
|
||||
unitstruct: UnitStruct,
|
||||
};
|
||||
let data2 = lua.to_value_with(
|
||||
&mydata,
|
||||
SerializeOptions::new().serialize_none_to_null(false),
|
||||
)?;
|
||||
globals.set("data2", data2)?;
|
||||
lua.load(
|
||||
r#"
|
||||
assert(data2.map.key == nil)
|
||||
assert(data2.unit == null)
|
||||
assert(data2.unitstruct == null)
|
||||
"#,
|
||||
)
|
||||
.exec()?;
|
||||
|
||||
// serialize_unit_to_null
|
||||
let data3 = lua.to_value_with(
|
||||
&mydata,
|
||||
SerializeOptions::new().serialize_unit_to_null(false),
|
||||
)?;
|
||||
globals.set("data3", data3)?;
|
||||
lua.load(
|
||||
r#"
|
||||
assert(data3.map.key == null)
|
||||
assert(data3.unit == nil)
|
||||
assert(data3.unitstruct == nil)
|
||||
"#,
|
||||
)
|
||||
.exec()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_from_value_struct() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let lua = Lua::new();
|
||||
@@ -303,7 +377,7 @@ fn test_from_value_enum() -> Result<(), Box<dyn std::error::Error>> {
|
||||
#[test]
|
||||
fn test_from_value_enum_untagged() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let lua = Lua::new();
|
||||
lua.globals().set("null", lua.null()?)?;
|
||||
lua.globals().set("null", lua.null())?;
|
||||
|
||||
#[derive(Deserialize, PartialEq, Debug)]
|
||||
#[serde(untagged)]
|
||||
|
||||
+7
-15
@@ -1,21 +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::borrow::Cow;
|
||||
|
||||
use mlua::{Lua, Result, String};
|
||||
|
||||
#[test]
|
||||
fn compare() {
|
||||
fn test_string_compare() {
|
||||
fn with_str<F: FnOnce(String)>(s: &str, f: F) {
|
||||
f(Lua::new().create_string(s).unwrap());
|
||||
}
|
||||
@@ -33,7 +21,7 @@ fn compare() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn string_views() -> Result<()> {
|
||||
fn test_string_views() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
lua.load(
|
||||
@@ -51,6 +39,10 @@ fn string_views() -> Result<()> {
|
||||
let empty: String = globals.get("empty")?;
|
||||
|
||||
assert_eq!(ok.to_str()?, "null bytes are valid utf-8, wh\0 knew?");
|
||||
assert_eq!(
|
||||
ok.to_string_lossy(),
|
||||
"null bytes are valid utf-8, wh\0 knew?"
|
||||
);
|
||||
assert_eq!(
|
||||
ok.as_bytes(),
|
||||
&b"null bytes are valid utf-8, wh\0 knew?"[..]
|
||||
@@ -67,7 +59,7 @@ fn string_views() -> Result<()> {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn raw_string() -> Result<()> {
|
||||
fn test_raw_string() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let rs = lua.create_string(&[0, 1, 2, 3, 0, 1, 2, 3])?;
|
||||
|
||||
@@ -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]
|
||||
|
||||
+203
-82
@@ -1,23 +1,13 @@
|
||||
#![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::collections::HashMap;
|
||||
use std::iter::FromIterator;
|
||||
use std::panic::catch_unwind;
|
||||
use std::panic::{catch_unwind, AssertUnwindSafe};
|
||||
use std::string::String as StdString;
|
||||
use std::sync::Arc;
|
||||
use std::{error, f32, f64, fmt};
|
||||
|
||||
use mlua::{
|
||||
ChunkMode, Error, ExternalError, Function, Lua, Nil, Result, StdLib, String, Table, UserData,
|
||||
Value, Variadic,
|
||||
ChunkMode, Error, ExternalError, Function, Lua, LuaOptions, Nil, Result, StdLib, String, Table,
|
||||
UserData, Value, Variadic,
|
||||
};
|
||||
|
||||
#[test]
|
||||
@@ -35,7 +25,7 @@ fn test_safety() -> Result<()> {
|
||||
assert!(lua.load(r#"require "debug""#).exec().is_ok());
|
||||
drop(lua);
|
||||
|
||||
match Lua::new_with(StdLib::DEBUG) {
|
||||
match Lua::new_with(StdLib::DEBUG, LuaOptions::default()) {
|
||||
Err(Error::SafetyError(_)) => {}
|
||||
Err(e) => panic!("expected SafetyError, got {:?}", e),
|
||||
Ok(_) => panic!("expected SafetyError, got new Lua state"),
|
||||
@@ -64,7 +54,7 @@ fn test_safety() -> Result<()> {
|
||||
Ok(_) => panic!("expected SafetyError, got no error"),
|
||||
}
|
||||
|
||||
let bytecode = lua.load("return 1 + 1").into_function()?.dump(true)?;
|
||||
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"))
|
||||
@@ -75,7 +65,7 @@ fn test_safety() -> Result<()> {
|
||||
drop(lua);
|
||||
|
||||
// Test safety rules after dynamically loading `package` library
|
||||
let lua = Lua::new_with(StdLib::NONE)?;
|
||||
let lua = Lua::new_with(StdLib::NONE, LuaOptions::default())?;
|
||||
assert!(lua.globals().get::<_, Option<Value>>("require")?.is_none());
|
||||
lua.load_from_std_lib(StdLib::PACKAGE)?;
|
||||
match lua.load(r#"package.loadlib()"#).exec() {
|
||||
@@ -93,6 +83,7 @@ fn test_safety() -> Result<()> {
|
||||
#[test]
|
||||
fn test_load() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let func = lua.load("return 1+2").into_function()?;
|
||||
let result: i32 = func.call(())?;
|
||||
assert_eq!(result, 3);
|
||||
@@ -174,7 +165,7 @@ fn test_load_mode() -> Result<()> {
|
||||
Err(e) => panic!("expected SyntaxError, got {:?}", e),
|
||||
};
|
||||
|
||||
let bytecode = lua.load("return 1 + 1").into_function()?.dump(true)?;
|
||||
let bytecode = lua.load("return 1 + 1").into_function()?.dump(true);
|
||||
assert_eq!(lua.load(&bytecode).eval::<i32>()?, 2);
|
||||
assert_eq!(
|
||||
lua.load(&bytecode)
|
||||
@@ -233,6 +224,7 @@ fn test_coercion() -> Result<()> {
|
||||
int = 123
|
||||
str = "123"
|
||||
num = 123.0
|
||||
func = function() end
|
||||
"#,
|
||||
)
|
||||
.exec()?;
|
||||
@@ -241,6 +233,7 @@ fn test_coercion() -> Result<()> {
|
||||
assert_eq!(globals.get::<_, String>("int")?, "123");
|
||||
assert_eq!(globals.get::<_, i32>("str")?, 123);
|
||||
assert_eq!(globals.get::<_, i32>("num")?, 123);
|
||||
assert!(globals.get::<_, String>("func").is_err());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -384,62 +377,139 @@ 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(options: LuaOptions) -> Result<Lua> {
|
||||
let lua = Lua::new_with(StdLib::ALL_SAFE, options)?;
|
||||
let rust_panic_function =
|
||||
lua.create_function(|_, msg: Option<StdString>| -> Result<()> {
|
||||
if let Some(msg) = msg {
|
||||
panic!("{}", msg)
|
||||
}
|
||||
panic!("rust panic")
|
||||
})?;
|
||||
lua.globals()
|
||||
.set("rust_panic_function", rust_panic_function)?;
|
||||
Ok(lua)
|
||||
}
|
||||
|
||||
// Test triggering Lua error with sending Rust panic (must be resumed)
|
||||
{
|
||||
let lua = make_lua(LuaOptions::default())?;
|
||||
|
||||
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(LuaOptions::default())?;
|
||||
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(LuaOptions::default())?;
|
||||
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"),
|
||||
};
|
||||
}
|
||||
|
||||
// Test disabling `catch_rust_panics` option / pcall correctness
|
||||
match catch_unwind(|| -> Result<()> {
|
||||
let lua = make_lua(LuaOptions::new().catch_rust_panics(false))?;
|
||||
lua.load(
|
||||
r#"
|
||||
local ok, err = pcall(function(msg) error(msg) end, "hello")
|
||||
assert(not ok and err:find("hello") ~= nil)
|
||||
|
||||
ok, err = pcall(rust_panic_function, "rust panic from lua")
|
||||
-- Nothing to return, panic should be automatically resumed
|
||||
"#,
|
||||
)
|
||||
.exec()
|
||||
}) {
|
||||
Ok(r) => panic!("no panic was detected: {:?}", r),
|
||||
Err(p) => assert!(*p.downcast::<StdString>().unwrap() == "rust panic from lua"),
|
||||
}
|
||||
|
||||
// Test disabling `catch_rust_panics` option / xpcall correctness
|
||||
match catch_unwind(|| -> Result<()> {
|
||||
let lua = make_lua(LuaOptions::new().catch_rust_panics(false))?;
|
||||
lua.load(
|
||||
r#"
|
||||
local msgh_ok = false
|
||||
local msgh = function(err)
|
||||
msgh_ok = err ~= nil and err:find("hello") ~= nil
|
||||
return err
|
||||
end
|
||||
local ok, err = xpcall(function(msg) error(msg) end, msgh, "hello")
|
||||
assert(not ok and err:find("hello") ~= nil)
|
||||
assert(msgh_ok)
|
||||
|
||||
ok, err = xpcall(rust_panic_function, msgh, "rust panic from lua")
|
||||
-- Nothing to return, panic should be automatically resumed
|
||||
"#,
|
||||
)
|
||||
.exec()
|
||||
}) {
|
||||
Ok(r) => panic!("no panic was detected: {:?}", r),
|
||||
Err(p) => assert!(*p.downcast::<StdString>().unwrap() == "rust panic from lua"),
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -543,7 +613,7 @@ fn test_pcall_xpcall() -> Result<()> {
|
||||
assert!(lua.load("xpcall()").exec().is_err());
|
||||
assert!(lua.load("xpcall(function() end)").exec().is_err());
|
||||
|
||||
// Lua 5.3/5.2 / LuaJIT compatible version of xpcall
|
||||
// Lua >= 5.2 compatible version of xpcall for 5.1
|
||||
#[cfg(feature = "lua51")]
|
||||
lua.load(
|
||||
r#"
|
||||
@@ -729,6 +799,23 @@ fn test_drop_registry_value() -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_lua_registry_hash() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let r1 = Arc::new(lua.create_registry_value("value1")?);
|
||||
let r2 = Arc::new(lua.create_registry_value("value2")?);
|
||||
|
||||
let mut map = HashMap::new();
|
||||
map.insert(r1.clone(), "value1");
|
||||
map.insert(r2.clone(), "value2");
|
||||
|
||||
assert_eq!(map[&r1], "value1");
|
||||
assert_eq!(map[&r2], "value2");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_lua_registry_ownership() -> Result<()> {
|
||||
let lua1 = Lua::new();
|
||||
@@ -760,7 +847,26 @@ fn test_mismatched_registry_key() -> Result<()> {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn too_many_returns() -> Result<()> {
|
||||
fn test_recursion() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let f = lua.create_function(move |lua, i: i32| {
|
||||
if i < 64 {
|
||||
lua.globals()
|
||||
.get::<_, Function>("f")?
|
||||
.call::<_, ()>(i + 1)?;
|
||||
}
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
lua.globals().set("f", f.clone())?;
|
||||
f.call::<_, ()>(1)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_too_many_returns() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
let f = lua.create_function(|_, ()| Ok(Variadic::from_iter(1..1000000)))?;
|
||||
assert!(f.call::<_, Vec<u32>>(()).is_err());
|
||||
@@ -768,7 +874,7 @@ fn too_many_returns() -> Result<()> {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn too_many_arguments() -> Result<()> {
|
||||
fn test_too_many_arguments() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
lua.load("function test(...) end").exec()?;
|
||||
let args = Variadic::from_iter(1..1000000);
|
||||
@@ -782,23 +888,20 @@ fn too_many_arguments() -> Result<()> {
|
||||
|
||||
#[test]
|
||||
#[cfg(not(feature = "luajit"))]
|
||||
fn too_many_recursions() -> Result<()> {
|
||||
fn test_too_many_recursions() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let f = lua
|
||||
.create_function(move |lua, ()| lua.globals().get::<_, Function>("f")?.call::<_, ()>(()))?;
|
||||
lua.globals().set("f", f)?;
|
||||
|
||||
assert!(lua
|
||||
.globals()
|
||||
.get::<_, Function>("f")?
|
||||
.call::<_, ()>(())
|
||||
.is_err());
|
||||
lua.globals().set("f", f.clone())?;
|
||||
assert!(f.call::<_, ()>(()).is_err());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn too_many_binds() -> Result<()> {
|
||||
fn test_too_many_binds() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
let globals = lua.globals();
|
||||
lua.load(
|
||||
@@ -819,7 +922,25 @@ fn too_many_binds() -> Result<()> {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn large_args() -> Result<()> {
|
||||
fn test_ref_stack_exhaustion() {
|
||||
match catch_unwind(AssertUnwindSafe(|| -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
let mut vals = Vec::new();
|
||||
for _ in 0..1000000 {
|
||||
vals.push(lua.create_table()?);
|
||||
}
|
||||
Ok(())
|
||||
})) {
|
||||
Ok(_) => panic!("no panic was detected"),
|
||||
Err(p) => assert!(p
|
||||
.downcast::<StdString>()
|
||||
.unwrap()
|
||||
.starts_with("cannot create a Lua reference, out of auxiliary stack space")),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_large_args() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
let globals = lua.globals();
|
||||
|
||||
@@ -854,7 +975,7 @@ fn large_args() -> Result<()> {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn large_args_ref() -> Result<()> {
|
||||
fn test_large_args_ref() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let f = lua.create_function(|_, args: Variadic<String>| {
|
||||
@@ -870,7 +991,7 @@ fn large_args_ref() -> Result<()> {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn chunk_env() -> Result<()> {
|
||||
fn test_chunk_env() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let assert: Function = lua.globals().get("assert")?;
|
||||
@@ -912,7 +1033,7 @@ fn chunk_env() -> Result<()> {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn context_thread() -> Result<()> {
|
||||
fn test_context_thread() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let f = lua
|
||||
@@ -935,7 +1056,7 @@ fn context_thread() -> Result<()> {
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "lua51", feature = "luajit"))]
|
||||
fn context_thread_51() -> Result<()> {
|
||||
fn test_context_thread_51() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let thread = lua.create_thread(
|
||||
|
||||
+45
-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::panic::catch_unwind;
|
||||
|
||||
use mlua::{Error, Function, Lua, Result, Thread, ThreadStatus};
|
||||
@@ -106,7 +94,50 @@ fn test_thread() -> Result<()> {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn coroutine_from_closure() -> Result<()> {
|
||||
#[cfg(any(feature = "lua54", all(feature = "luajit", feature = "vendored")))]
|
||||
fn test_thread_reset() -> Result<()> {
|
||||
use mlua::{AnyUserData, UserData};
|
||||
use std::sync::Arc;
|
||||
|
||||
let lua = Lua::new();
|
||||
|
||||
struct MyUserData(Arc<()>);
|
||||
impl UserData for MyUserData {}
|
||||
|
||||
let arc = Arc::new(());
|
||||
|
||||
let func: Function = lua.load(r#"function(ud) coroutine.yield(ud) end"#).eval()?;
|
||||
let thread = lua.create_thread(func.clone())?;
|
||||
|
||||
for _ in 0..2 {
|
||||
assert_eq!(thread.status(), ThreadStatus::Resumable);
|
||||
let _ = thread.resume::<_, AnyUserData>(MyUserData(arc.clone()))?;
|
||||
assert_eq!(thread.status(), ThreadStatus::Resumable);
|
||||
assert_eq!(Arc::strong_count(&arc), 2);
|
||||
thread.resume::<_, ()>(())?;
|
||||
assert_eq!(thread.status(), ThreadStatus::Unresumable);
|
||||
thread.reset(func.clone())?;
|
||||
lua.gc_collect()?;
|
||||
assert_eq!(Arc::strong_count(&arc), 1);
|
||||
}
|
||||
|
||||
// Check for errors (Lua 5.4 only)
|
||||
#[cfg(feature = "lua54")]
|
||||
{
|
||||
let func: Function = lua.load(r#"function(ud) error("test error") end"#).eval()?;
|
||||
let thread = lua.create_thread(func.clone())?;
|
||||
let _ = thread.resume::<_, AnyUserData>(MyUserData(arc.clone()));
|
||||
assert_eq!(thread.status(), ThreadStatus::Error);
|
||||
assert_eq!(Arc::strong_count(&arc), 2);
|
||||
assert!(thread.reset(func.clone()).is_err());
|
||||
assert_eq!(thread.status(), ThreadStatus::Error);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_coroutine_from_closure() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let thrd_main = lua.create_function(|_, ()| Ok(()))?;
|
||||
@@ -130,7 +161,7 @@ fn coroutine_from_closure() -> Result<()> {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn coroutine_panic() {
|
||||
fn test_coroutine_panic() {
|
||||
match catch_unwind(|| -> Result<()> {
|
||||
// check that coroutines propagate panics correctly
|
||||
let lua = Lua::new();
|
||||
|
||||
@@ -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};
|
||||
|
||||
+205
-23
@@ -1,23 +1,14 @@
|
||||
#![cfg_attr(
|
||||
all(feature = "luajit", target_os = "macos", target_arch = "x86_64"),
|
||||
feature(link_args)
|
||||
)]
|
||||
use std::sync::{Arc, Mutex, RwLock};
|
||||
|
||||
#[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(not(feature = "send"))]
|
||||
use std::{cell::RefCell, rc::Rc};
|
||||
|
||||
#[cfg(feature = "lua54")]
|
||||
use std::sync::atomic::{AtomicI64, Ordering};
|
||||
|
||||
use mlua::{
|
||||
AnyUserData, ExternalError, Function, Lua, MetaMethod, Result, String, UserData,
|
||||
UserDataMethods, Value,
|
||||
AnyUserData, Error, ExternalError, Function, Lua, MetaMethod, Nil, Result, String, UserData,
|
||||
UserDataFields, UserDataMethods, Value,
|
||||
};
|
||||
|
||||
#[test]
|
||||
@@ -25,8 +16,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))?;
|
||||
@@ -167,10 +158,10 @@ fn test_metamethods() -> Result<()> {
|
||||
assert!(userdata2.equals(userdata3)?);
|
||||
|
||||
let userdata1: AnyUserData = globals.get("userdata1")?;
|
||||
assert!(userdata1.has_metamethod(MetaMethod::Add)?);
|
||||
assert!(userdata1.has_metamethod(MetaMethod::Sub)?);
|
||||
assert!(userdata1.has_metamethod(MetaMethod::Index)?);
|
||||
assert!(!userdata1.has_metamethod(MetaMethod::Pow)?);
|
||||
assert!(userdata1.get_metatable()?.contains(MetaMethod::Add)?);
|
||||
assert!(userdata1.get_metatable()?.contains(MetaMethod::Sub)?);
|
||||
assert!(userdata1.get_metatable()?.contains(MetaMethod::Index)?);
|
||||
assert!(!userdata1.get_metatable()?.contains(MetaMethod::Pow)?);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -262,7 +253,7 @@ fn test_gc_userdata() -> Result<()> {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn detroys_userdata() -> Result<()> {
|
||||
fn test_destroy_userdata() -> Result<()> {
|
||||
struct MyUserdata(Arc<()>);
|
||||
|
||||
impl UserData for MyUserdata {}
|
||||
@@ -284,7 +275,7 @@ fn detroys_userdata() -> Result<()> {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn user_value() -> Result<()> {
|
||||
fn test_user_value() -> Result<()> {
|
||||
struct MyUserData;
|
||||
|
||||
impl UserData for MyUserData {}
|
||||
@@ -307,7 +298,7 @@ fn test_functions() -> Result<()> {
|
||||
methods.add_function("get_value", |_, ud: AnyUserData| {
|
||||
Ok(ud.borrow::<MyUserData>()?.0)
|
||||
});
|
||||
methods.add_function("set_value", |_, (ud, value): (AnyUserData, i64)| {
|
||||
methods.add_function_mut("set_value", |_, (ud, value): (AnyUserData, i64)| {
|
||||
ud.borrow_mut::<MyUserData>()?.0 = value;
|
||||
Ok(())
|
||||
});
|
||||
@@ -347,3 +338,194 @@ fn test_functions() -> Result<()> {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_fields() -> Result<()> {
|
||||
#[derive(Copy, Clone)]
|
||||
struct MyUserData(i64);
|
||||
|
||||
impl UserData for MyUserData {
|
||||
fn add_fields<'lua, F: UserDataFields<'lua, Self>>(fields: &mut F) {
|
||||
fields.add_field_method_get("val", |_, data| Ok(data.0));
|
||||
fields.add_field_method_set("val", |_, data, val| {
|
||||
data.0 = val;
|
||||
Ok(())
|
||||
});
|
||||
|
||||
// Use userdata "uservalue" storage
|
||||
fields.add_field_function_get("uval", |_, ud| ud.get_user_value::<Option<String>>());
|
||||
fields
|
||||
.add_field_function_set("uval", |_, ud, s| ud.set_user_value::<Option<String>>(s));
|
||||
|
||||
fields.add_meta_field_with(MetaMethod::Index, |lua| {
|
||||
let index = lua.create_table()?;
|
||||
index.set("f", 321)?;
|
||||
Ok(index)
|
||||
});
|
||||
fields.add_meta_field_with(MetaMethod::NewIndex, |lua| {
|
||||
lua.create_function(|lua, (_, field, val): (AnyUserData, String, Value)| {
|
||||
lua.globals().set(field, val)?;
|
||||
Ok(())
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
let lua = Lua::new();
|
||||
let globals = lua.globals();
|
||||
globals.set("ud", MyUserData(7))?;
|
||||
lua.load(
|
||||
r#"
|
||||
assert(ud.val == 7)
|
||||
ud.val = 10
|
||||
assert(ud.val == 10)
|
||||
|
||||
assert(ud.uval == nil)
|
||||
ud.uval = "hello"
|
||||
assert(ud.uval == "hello")
|
||||
|
||||
assert(ud.f == 321)
|
||||
|
||||
ud.unknown = 789
|
||||
assert(unknown == 789)
|
||||
"#,
|
||||
)
|
||||
.exec()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_metatable() -> Result<()> {
|
||||
#[derive(Copy, Clone)]
|
||||
struct MyUserData(i64);
|
||||
|
||||
impl UserData for MyUserData {
|
||||
fn add_fields<'lua, F: UserDataFields<'lua, Self>>(fields: &mut F) {
|
||||
fields.add_meta_field_with("__type_name", |_| Ok("MyUserData"));
|
||||
}
|
||||
|
||||
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
|
||||
methods.add_function("my_type_name", |_, data: AnyUserData| {
|
||||
let metatable = data.get_metatable()?;
|
||||
metatable.get::<_, String>("__type_name")
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
let lua = Lua::new();
|
||||
let globals = lua.globals();
|
||||
globals.set("ud", MyUserData(7))?;
|
||||
lua.load(
|
||||
r#"
|
||||
assert(ud:my_type_name() == "MyUserData")
|
||||
"#,
|
||||
)
|
||||
.exec()?;
|
||||
|
||||
let ud: AnyUserData = globals.get("ud")?;
|
||||
let metatable = ud.get_metatable()?;
|
||||
|
||||
match metatable.get::<_, Value>("__gc") {
|
||||
Ok(_) => panic!("expected MetaMethodRestricted, got no error"),
|
||||
Err(Error::MetaMethodRestricted(_)) => {}
|
||||
Err(e) => panic!("expected MetaMethodRestricted, got {:?}", e),
|
||||
}
|
||||
|
||||
match metatable.set(MetaMethod::Index, Nil) {
|
||||
Ok(_) => panic!("expected MetaMethodRestricted, got no error"),
|
||||
Err(Error::MetaMethodRestricted(_)) => {}
|
||||
Err(e) => panic!("expected MetaMethodRestricted, got {:?}", e),
|
||||
}
|
||||
|
||||
let mut methods = metatable
|
||||
.pairs()
|
||||
.into_iter()
|
||||
.map(|kv: Result<(_, Value)>| Ok(kv?.0))
|
||||
.collect::<Result<Vec<_>>>()?;
|
||||
methods.sort_by_cached_key(|k| k.name().to_owned());
|
||||
assert_eq!(methods, vec![MetaMethod::Index, "__type_name".into()]);
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
struct MyUserData2(i64);
|
||||
|
||||
impl UserData for MyUserData2 {
|
||||
fn add_fields<'lua, F: UserDataFields<'lua, Self>>(fields: &mut F) {
|
||||
fields.add_meta_field_with("__index", |_| Ok(1));
|
||||
}
|
||||
}
|
||||
|
||||
match lua.create_userdata(MyUserData2(1)) {
|
||||
Ok(_) => panic!("expected MetaMethodTypeError, got no error"),
|
||||
Err(Error::MetaMethodTypeError { .. }) => {}
|
||||
Err(e) => panic!("expected MetaMethodTypeError, got {:?}", e),
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_userdata_wrapped() -> Result<()> {
|
||||
struct MyUserData(i64);
|
||||
|
||||
impl UserData for MyUserData {
|
||||
fn add_fields<'lua, F: UserDataFields<'lua, Self>>(fields: &mut F) {
|
||||
fields.add_field_method_get("data", |_, this| Ok(this.0));
|
||||
fields.add_field_method_set("data", |_, this, val| {
|
||||
this.0 = val;
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
let lua = Lua::new();
|
||||
let globals = lua.globals();
|
||||
|
||||
#[cfg(not(feature = "send"))]
|
||||
{
|
||||
let ud1 = Rc::new(RefCell::new(MyUserData(1)));
|
||||
globals.set("rc_refcell_ud", ud1.clone())?;
|
||||
lua.load(
|
||||
r#"
|
||||
rc_refcell_ud.data = rc_refcell_ud.data + 1
|
||||
assert(rc_refcell_ud.data == 2)
|
||||
"#,
|
||||
)
|
||||
.exec()?;
|
||||
assert_eq!(ud1.borrow().0, 2);
|
||||
globals.set("rc_refcell_ud", Nil)?;
|
||||
lua.gc_collect()?;
|
||||
assert_eq!(Rc::strong_count(&ud1), 1);
|
||||
}
|
||||
|
||||
let ud2 = Arc::new(Mutex::new(MyUserData(2)));
|
||||
globals.set("arc_mutex_ud", ud2.clone())?;
|
||||
lua.load(
|
||||
r#"
|
||||
arc_mutex_ud.data = arc_mutex_ud.data + 1
|
||||
assert(arc_mutex_ud.data == 3)
|
||||
"#,
|
||||
)
|
||||
.exec()?;
|
||||
assert_eq!(ud2.lock().unwrap().0, 3);
|
||||
|
||||
let ud3 = Arc::new(RwLock::new(MyUserData(3)));
|
||||
globals.set("arc_rwlock_ud", ud3.clone())?;
|
||||
lua.load(
|
||||
r#"
|
||||
arc_rwlock_ud.data = arc_rwlock_ud.data + 1
|
||||
assert(arc_rwlock_ud.data == 4)
|
||||
"#,
|
||||
)
|
||||
.exec()?;
|
||||
assert_eq!(ud3.read().unwrap().0, 4);
|
||||
|
||||
// Test drop
|
||||
globals.set("arc_mutex_ud", Nil)?;
|
||||
globals.set("arc_rwlock_ud", Nil)?;
|
||||
lua.gc_collect()?;
|
||||
assert_eq!(Arc::strong_count(&ud2), 1);
|
||||
assert_eq!(Arc::strong_count(&ud3), 1);
|
||||
|
||||
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 mlua::{Lua, Result, Value};
|
||||
|
||||
#[test]
|
||||
|
||||
Reference in New Issue
Block a user