mirror of
https://github.com/mlua-rs/mlua
synced 2026-06-08 16:05:43 +00:00
Compare commits
129 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 584b377640 | |||
| 1141073a65 | |||
| 5b1483bd56 | |||
| a74b637ed4 | |||
| 7623016d4a | |||
| bdd3c923ba | |||
| d586eef0f5 | |||
| 01154c0616 | |||
| e42d67c70d | |||
| 771a7775c5 | |||
| ee1c8a1a3d | |||
| 3597e34ffb | |||
| 35eedd5a5e | |||
| 53f873a482 | |||
| fc1fe2c15e | |||
| 7e7a44f4cd | |||
| 63c4861520 | |||
| 27e7facf9b | |||
| 31d32f2dda | |||
| 7d1b322e18 | |||
| d906405818 | |||
| 60fd060d47 | |||
| 9f02a9ca09 | |||
| 1d7f105585 | |||
| 1020315a9b | |||
| 628b70f561 | |||
| 8a7e546c66 | |||
| 97bd288f56 | |||
| 59253410bb | |||
| 5fb7b96704 | |||
| 17162b1e0b | |||
| 934aa3b65f | |||
| 3bffe1d7c3 | |||
| 582b2c585f | |||
| 56b6b128b5 | |||
| 1731f5d61b | |||
| 60822d12d2 | |||
| 8ff610529b | |||
| adbc9ccc9b | |||
| 84fe5f7f76 | |||
| 1fe583027b | |||
| 7b5b78fa3d | |||
| 41aae83304 | |||
| 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 |
@@ -0,0 +1 @@
|
||||
github: khvzak
|
||||
@@ -14,7 +14,7 @@ jobs:
|
||||
|
||||
- name: Generate code coverage
|
||||
run: |
|
||||
cargo tarpaulin --verbose --features lua53,vendored,async,send,serialize --out xml --exclude-files benches --exclude-files tests --exclude-files build --exclude-files src/ffi
|
||||
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
|
||||
|
||||
+47
-18
@@ -24,17 +24,18 @@ jobs:
|
||||
toolchain: ${{ matrix.rust }}
|
||||
target: ${{ matrix.target }}
|
||||
override: true
|
||||
- uses: Swatinem/rust-cache@v1
|
||||
- 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 --features "${{ matrix.lua }},vendored"
|
||||
cargo build --features "${{ matrix.lua }},vendored,async,send,serialize,macros"
|
||||
shell: bash
|
||||
- name: Build ${{ matrix.lua }} pkg-config
|
||||
if: ${{ matrix.os == 'ubuntu-18.04' && matrix.lua != 'lua54' }}
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y --no-install-recommends liblua5.3-dev liblua5.2-dev liblua5.1-0-dev libluajit-5.1-dev
|
||||
cargo build --release --features "${{ matrix.lua }}"
|
||||
cargo build --features "${{ matrix.lua }}"
|
||||
|
||||
build_aarch64_cross_macos:
|
||||
name: Cross-compile to aarch64-apple-darwin
|
||||
@@ -47,11 +48,11 @@ jobs:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: nightly
|
||||
toolchain: stable
|
||||
target: aarch64-apple-darwin
|
||||
override: true
|
||||
- name: Cross-compile
|
||||
run: cargo build --target aarch64-apple-darwin --features "${{ matrix.lua }} async send serialize vendored"
|
||||
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
|
||||
@@ -64,7 +65,7 @@ jobs:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: nightly
|
||||
toolchain: stable
|
||||
target: aarch64-unknown-linux-gnu
|
||||
override: true
|
||||
- name: Install ARM compiler toolchain
|
||||
@@ -73,7 +74,7 @@ jobs:
|
||||
sudo apt-get install -y --no-install-recommends gcc-aarch64-linux-gnu libc6-dev-arm64-cross
|
||||
shell: bash
|
||||
- name: Cross-compile
|
||||
run: cargo build --target aarch64-unknown-linux-gnu --features "${{ matrix.lua }} async send serialize vendored"
|
||||
run: cargo build --target aarch64-unknown-linux-gnu --features "${{ matrix.lua }},vendored,async,send,serialize,macros"
|
||||
shell: bash
|
||||
|
||||
build_armv7_cross_ubuntu:
|
||||
@@ -87,7 +88,7 @@ jobs:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: nightly
|
||||
toolchain: stable
|
||||
target: armv7-unknown-linux-gnueabihf
|
||||
override: true
|
||||
- name: Install ARM compiler toolchain
|
||||
@@ -96,7 +97,7 @@ jobs:
|
||||
sudo apt-get install -y --no-install-recommends gcc-arm-linux-gnueabihf libc-dev-armhf-cross
|
||||
shell: bash
|
||||
- name: Cross-compile
|
||||
run: cargo build --target armv7-unknown-linux-gnueabihf --features "${{ matrix.lua }} async send serialize vendored"
|
||||
run: cargo build --target armv7-unknown-linux-gnueabihf --features "${{ matrix.lua }},vendored,async,send,serialize,macros"
|
||||
shell: bash
|
||||
|
||||
test:
|
||||
@@ -106,7 +107,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
|
||||
@@ -122,16 +123,43 @@ jobs:
|
||||
toolchain: ${{ matrix.rust }}
|
||||
target: ${{ matrix.target }}
|
||||
override: true
|
||||
- uses: Swatinem/rust-cache@v1
|
||||
- name: Run ${{ matrix.lua }} tests
|
||||
run: |
|
||||
cargo test --release --features "${{ matrix.lua }} vendored"
|
||||
cargo test --release --features "${{ matrix.lua }} vendored async send serialize"
|
||||
cargo test --features "${{ matrix.lua }},vendored"
|
||||
cargo test --features "${{ matrix.lua }},vendored,async,send,serialize,macros"
|
||||
shell: bash
|
||||
- name: Run compile tests (macos lua53)
|
||||
if: ${{ matrix.os == 'macos-latest' && matrix.lua == 'lua53' }}
|
||||
run: |
|
||||
TRYBUILD=overwrite cargo test --release --features "${{ matrix.lua }} vendored" -- --ignored
|
||||
TRYBUILD=overwrite cargo test --release --features "${{ matrix.lua }} vendored async send serialize" -- --ignored
|
||||
TRYBUILD=overwrite cargo test --features "${{ matrix.lua }},vendored" -- --ignored
|
||||
TRYBUILD=overwrite cargo test --features "${{ matrix.lua }},vendored,async,send,serialize,macros" -- --ignored
|
||||
shell: bash
|
||||
|
||||
test_with_sanitizer:
|
||||
name: Test with address sanitizer
|
||||
runs-on: ${{ matrix.os }}
|
||||
needs: build
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-18.04]
|
||||
rust: [nightly]
|
||||
lua: [lua54, lua53, lua52, lua51, luajit]
|
||||
include:
|
||||
- os: ubuntu-18.04
|
||||
target: x86_64-unknown-linux-gnu
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: ${{ matrix.rust }}
|
||||
target: ${{ matrix.target }}
|
||||
override: true
|
||||
- uses: Swatinem/rust-cache@v1
|
||||
- name: Run ${{ matrix.lua }} tests with address sanitizer
|
||||
run: |
|
||||
RUSTFLAGS="-Z sanitizer=address" \
|
||||
cargo test --tests --features "${{ matrix.lua }},vendored,async,send,serialize,macros" --target x86_64-unknown-linux-gnu -- --skip test_too_many_recursions
|
||||
shell: bash
|
||||
|
||||
test_modules:
|
||||
@@ -155,10 +183,11 @@ jobs:
|
||||
toolchain: ${{ matrix.rust }}
|
||||
target: ${{ matrix.target }}
|
||||
override: true
|
||||
- uses: Swatinem/rust-cache@v1
|
||||
- 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:
|
||||
@@ -167,7 +196,7 @@ jobs:
|
||||
needs: build
|
||||
strategy:
|
||||
matrix:
|
||||
lua: [lua53, luajit]
|
||||
lua: [lua54, luajit]
|
||||
defaults:
|
||||
run:
|
||||
shell: msys2 {0}
|
||||
@@ -210,4 +239,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,3 +1,76 @@
|
||||
## v0.6.4
|
||||
|
||||
- Performance optimizations
|
||||
- Fixed table traversal used in recursion detection in deserializer
|
||||
|
||||
## v0.6.3
|
||||
|
||||
- Disabled catching Rust panics in userdata finalizers on drop. It also has positive performance impact.
|
||||
- Added `Debug::event()` to the hook's Debug structure
|
||||
- Simplified interface of `hook::HookTriggers`
|
||||
- Added finalizer to `ExtraData` in module mode. This helps avoiding memory leak on closing state when Lua unloads modules and frees memory.
|
||||
- Added `DeserializeOptions` struct to control deserializer behavior (`from_value_with` function).
|
||||
|
||||
## v0.6.2
|
||||
|
||||
- New functionality: `Lua::load_from_function()` and `Lua::create_c_function()`
|
||||
- Many optimizations in callbacks/userdata creation and methods execution
|
||||
|
||||
## 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
|
||||
|
||||
+18
-8
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "mlua"
|
||||
version = "0.5.4" # remember to update html_root_url and mlua_derive
|
||||
version = "0.6.4" # remember to update html_root_url and mlua_derive
|
||||
authors = ["Aleksandr Orlenko <zxteam@pm.me>", "kyren <catherine@chucklefish.org>"]
|
||||
edition = "2018"
|
||||
repository = "https://github.com/khvzak/mlua"
|
||||
@@ -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 }
|
||||
@@ -57,7 +58,7 @@ lua-src = { version = ">= 540.0.0, < 550.0.0", optional = true }
|
||||
luajit-src = { version = ">= 210.1.2, < 220.0.0", optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
rustyline = "7.0"
|
||||
rustyline = "8.0"
|
||||
criterion = { version = "0.3.4", features = ["html_reports", "async_tokio"] }
|
||||
trybuild = "1.0"
|
||||
futures = "0.3.5"
|
||||
@@ -66,6 +67,7 @@ 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"
|
||||
@@ -74,11 +76,11 @@ required-features = ["async"]
|
||||
|
||||
[[example]]
|
||||
name = "async_http_client"
|
||||
required-features = ["async"]
|
||||
required-features = ["async", "macros"]
|
||||
|
||||
[[example]]
|
||||
name = "async_http_reqwest"
|
||||
required-features = ["async", "serialize"]
|
||||
required-features = ["async", "serialize", "macros"]
|
||||
|
||||
[[example]]
|
||||
name = "async_http_server"
|
||||
@@ -86,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"]
|
||||
|
||||
@@ -17,7 +17,7 @@ _safe_ (as far as it's possible), high level, easy to use, practical and flexibl
|
||||
|
||||
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` tested on Windows/macOS/Linux including module mode in [GitHub Actions] on `x86_64` platform and cross-compilation to `aarch64` (other targes are also supported).
|
||||
`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).
|
||||
|
||||
[GitHub Actions]: https://github.com/khvzak/mlua/actions
|
||||
|
||||
@@ -25,7 +25,7 @@ Started as [rlua](https://github.com/amethyst/rlua/tree/0.15.3) fork, `mlua` sup
|
||||
|
||||
### Feature flags
|
||||
|
||||
`mlua` uses feature flags to reduce the amount of depenendies, compiled code and allow to choose only required set of features.
|
||||
`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
|
||||
@@ -37,7 +37,8 @@ Below is a list of the available feature flags. By default `mlua` does not enabl
|
||||
* `module`: enable module mode (building loadable `cdylib` library for Lua)
|
||||
* `async`: enable async/await support (any executor can be used, eg. [tokio] or [async-std])
|
||||
* `send`: make `mlua::Lua` transferable across thread boundaries (adds [`Send`] requirement to `mlua::Function` and `mlua::UserData`)
|
||||
* `serialize`: add serialization and deserialization support to `mlua` types usign [serde] framework
|
||||
* `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
|
||||
@@ -73,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.
|
||||
@@ -85,7 +86,7 @@ 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`.
|
||||
|
||||
@@ -96,7 +97,7 @@ Add to `Cargo.toml` :
|
||||
|
||||
``` toml
|
||||
[dependencies]
|
||||
mlua = { version = "0.5", features = ["lua53", "vendored"] }
|
||||
mlua = { version = "0.6", features = ["lua53", "vendored"] }
|
||||
```
|
||||
|
||||
`main.rs`
|
||||
@@ -131,7 +132,7 @@ Add to `Cargo.toml` :
|
||||
crate-type = ["cdylib"]
|
||||
|
||||
[dependencies]
|
||||
mlua = { version = "0.5", features = ["lua53", "vendored", "module"] }
|
||||
mlua = { version = "0.6", features = ["lua53", "vendored", "module"] }
|
||||
```
|
||||
|
||||
`lib.rs` :
|
||||
@@ -184,6 +185,15 @@ 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.
|
||||
@@ -198,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
|
||||
@@ -217,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
|
||||
@@ -225,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)
|
||||
|
||||
|
||||
@@ -59,6 +59,22 @@ fn create_string_table(c: &mut Criterion) {
|
||||
});
|
||||
}
|
||||
|
||||
fn create_function(c: &mut Criterion) {
|
||||
let lua = Lua::new();
|
||||
|
||||
c.bench_function("create [function] 10", |b| {
|
||||
b.iter_batched(
|
||||
|| collect_gc_twice(&lua),
|
||||
|_| {
|
||||
for i in 0..10 {
|
||||
lua.create_function(move |_, ()| Ok(i)).unwrap();
|
||||
}
|
||||
},
|
||||
BatchSize::SmallInput,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
fn call_lua_function(c: &mut Criterion) {
|
||||
let lua = Lua::new();
|
||||
|
||||
@@ -258,6 +274,7 @@ criterion_group! {
|
||||
create_table,
|
||||
create_array,
|
||||
create_string_table,
|
||||
create_function,
|
||||
call_lua_function,
|
||||
call_sum_callback,
|
||||
call_async_sum_callback,
|
||||
|
||||
@@ -22,12 +22,12 @@ pub fn probe_lua() -> PathBuf {
|
||||
|
||||
let need_lua_lib = cfg!(any(not(feature = "module"), target_os = "windows"));
|
||||
|
||||
if include_dir != "" {
|
||||
if !include_dir.is_empty() {
|
||||
if need_lua_lib {
|
||||
if lib_dir == "" {
|
||||
if lib_dir.is_empty() {
|
||||
panic!("LUA_LIB is not set");
|
||||
}
|
||||
if lua_lib == "" {
|
||||
if lua_lib.is_empty() {
|
||||
panic!("LUA_LIB_NAME is not set");
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
coverage:
|
||||
status:
|
||||
patch:
|
||||
default:
|
||||
only_pulls: true
|
||||
project:
|
||||
default:
|
||||
only_pulls: true
|
||||
@@ -1,12 +1,11 @@
|
||||
use std::collections::HashMap;
|
||||
use std::sync::Arc;
|
||||
|
||||
use bstr::BString;
|
||||
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>>);
|
||||
@@ -19,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;
|
||||
if let Some(bytes) = reader.data().await {
|
||||
let bytes = bytes.map_err(Error::external)?;
|
||||
return Ok(Some(BString::from(bytes.as_ref())));
|
||||
let bytes = bytes.to_lua_err()?;
|
||||
return Some(lua.create_string(&bytes)).transpose();
|
||||
}
|
||||
Ok(None)
|
||||
});
|
||||
@@ -36,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)?;
|
||||
@@ -56,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
|
||||
@@ -75,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,14 +1,12 @@
|
||||
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)]
|
||||
@@ -41,15 +39,15 @@ 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)
|
||||
});
|
||||
|
||||
@@ -66,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
|
||||
@@ -87,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
|
||||
@@ -106,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)
|
||||
}
|
||||
}
|
||||
+81
-12
@@ -1,3 +1,5 @@
|
||||
#![allow(clippy::wrong_self_convention)]
|
||||
|
||||
use std::borrow::Cow;
|
||||
use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet};
|
||||
use std::ffi::{CStr, CString};
|
||||
@@ -18,24 +20,28 @@ use crate::userdata::{AnyUserData, UserData};
|
||||
use crate::value::{FromLua, Nil, ToLua, Value};
|
||||
|
||||
impl<'lua> ToLua<'lua> for Value<'lua> {
|
||||
#[inline]
|
||||
fn to_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
|
||||
Ok(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> FromLua<'lua> for Value<'lua> {
|
||||
#[inline]
|
||||
fn from_lua(lua_value: Value<'lua>, _: &'lua Lua) -> Result<Self> {
|
||||
Ok(lua_value)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> ToLua<'lua> for String<'lua> {
|
||||
#[inline]
|
||||
fn to_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
|
||||
Ok(Value::String(self))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> FromLua<'lua> for String<'lua> {
|
||||
#[inline]
|
||||
fn from_lua(value: Value<'lua>, lua: &'lua Lua) -> Result<String<'lua>> {
|
||||
let ty = value.type_name();
|
||||
lua.coerce_string(value)?
|
||||
@@ -48,12 +54,14 @@ impl<'lua> FromLua<'lua> for String<'lua> {
|
||||
}
|
||||
|
||||
impl<'lua> ToLua<'lua> for Table<'lua> {
|
||||
#[inline]
|
||||
fn to_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
|
||||
Ok(Value::Table(self))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> FromLua<'lua> for Table<'lua> {
|
||||
#[inline]
|
||||
fn from_lua(value: Value<'lua>, _: &'lua Lua) -> Result<Table<'lua>> {
|
||||
match value {
|
||||
Value::Table(table) => Ok(table),
|
||||
@@ -67,12 +75,14 @@ impl<'lua> FromLua<'lua> for Table<'lua> {
|
||||
}
|
||||
|
||||
impl<'lua> ToLua<'lua> for Function<'lua> {
|
||||
#[inline]
|
||||
fn to_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
|
||||
Ok(Value::Function(self))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> FromLua<'lua> for Function<'lua> {
|
||||
#[inline]
|
||||
fn from_lua(value: Value<'lua>, _: &'lua Lua) -> Result<Function<'lua>> {
|
||||
match value {
|
||||
Value::Function(table) => Ok(table),
|
||||
@@ -86,12 +96,14 @@ impl<'lua> FromLua<'lua> for Function<'lua> {
|
||||
}
|
||||
|
||||
impl<'lua> ToLua<'lua> for Thread<'lua> {
|
||||
#[inline]
|
||||
fn to_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
|
||||
Ok(Value::Thread(self))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> FromLua<'lua> for Thread<'lua> {
|
||||
#[inline]
|
||||
fn from_lua(value: Value<'lua>, _: &'lua Lua) -> Result<Thread<'lua>> {
|
||||
match value {
|
||||
Value::Thread(t) => Ok(t),
|
||||
@@ -105,12 +117,14 @@ impl<'lua> FromLua<'lua> for Thread<'lua> {
|
||||
}
|
||||
|
||||
impl<'lua> ToLua<'lua> for AnyUserData<'lua> {
|
||||
#[inline]
|
||||
fn to_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
|
||||
Ok(Value::UserData(self))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> FromLua<'lua> for AnyUserData<'lua> {
|
||||
#[inline]
|
||||
fn from_lua(value: Value<'lua>, _: &'lua Lua) -> Result<AnyUserData<'lua>> {
|
||||
match value {
|
||||
Value::UserData(ud) => Ok(ud),
|
||||
@@ -124,12 +138,14 @@ impl<'lua> FromLua<'lua> for AnyUserData<'lua> {
|
||||
}
|
||||
|
||||
impl<'lua, T: 'static + MaybeSend + UserData> ToLua<'lua> for T {
|
||||
#[inline]
|
||||
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
|
||||
Ok(Value::UserData(lua.create_userdata(self)?))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua, T: 'static + UserData + Clone> FromLua<'lua> for T {
|
||||
#[inline]
|
||||
fn from_lua(value: Value<'lua>, _: &'lua Lua) -> Result<T> {
|
||||
match value {
|
||||
Value::UserData(ud) => Ok(ud.borrow::<T>()?.clone()),
|
||||
@@ -143,12 +159,14 @@ impl<'lua, T: 'static + UserData + Clone> FromLua<'lua> for T {
|
||||
}
|
||||
|
||||
impl<'lua> ToLua<'lua> for Error {
|
||||
#[inline]
|
||||
fn to_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
|
||||
Ok(Value::Error(self))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> FromLua<'lua> for Error {
|
||||
#[inline]
|
||||
fn from_lua(value: Value<'lua>, lua: &'lua Lua) -> Result<Error> {
|
||||
match value {
|
||||
Value::Error(err) => Ok(err),
|
||||
@@ -162,12 +180,14 @@ impl<'lua> FromLua<'lua> for Error {
|
||||
}
|
||||
|
||||
impl<'lua> ToLua<'lua> for bool {
|
||||
#[inline]
|
||||
fn to_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
|
||||
Ok(Value::Boolean(self))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> FromLua<'lua> for bool {
|
||||
#[inline]
|
||||
fn from_lua(v: Value<'lua>, _: &'lua Lua) -> Result<Self> {
|
||||
match v {
|
||||
Value::Nil => Ok(false),
|
||||
@@ -178,12 +198,14 @@ impl<'lua> FromLua<'lua> for bool {
|
||||
}
|
||||
|
||||
impl<'lua> ToLua<'lua> for LightUserData {
|
||||
#[inline]
|
||||
fn to_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
|
||||
Ok(Value::LightUserData(self))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> FromLua<'lua> for LightUserData {
|
||||
#[inline]
|
||||
fn from_lua(value: Value<'lua>, _: &'lua Lua) -> Result<Self> {
|
||||
match value {
|
||||
Value::LightUserData(ud) => Ok(ud),
|
||||
@@ -197,12 +219,14 @@ impl<'lua> FromLua<'lua> for LightUserData {
|
||||
}
|
||||
|
||||
impl<'lua> ToLua<'lua> for StdString {
|
||||
#[inline]
|
||||
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
|
||||
Ok(Value::String(lua.create_string(&self)?))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> FromLua<'lua> for StdString {
|
||||
#[inline]
|
||||
fn from_lua(value: Value<'lua>, lua: &'lua Lua) -> Result<Self> {
|
||||
let ty = value.type_name();
|
||||
Ok(lua
|
||||
@@ -218,6 +242,7 @@ impl<'lua> FromLua<'lua> for StdString {
|
||||
}
|
||||
|
||||
impl<'lua> ToLua<'lua> for &str {
|
||||
#[inline]
|
||||
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
|
||||
Ok(Value::String(lua.create_string(self)?))
|
||||
}
|
||||
@@ -229,6 +254,28 @@ impl<'lua> ToLua<'lua> for Cow<'_, str> {
|
||||
}
|
||||
}
|
||||
|
||||
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())?))
|
||||
@@ -436,6 +483,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)?))
|
||||
@@ -510,17 +577,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()),
|
||||
})
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -535,22 +602,23 @@ 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()),
|
||||
})
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua, T: ToLua<'lua>> ToLua<'lua> for Option<T> {
|
||||
#[inline]
|
||||
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
|
||||
match self {
|
||||
Some(val) => val.to_lua(lua),
|
||||
@@ -560,6 +628,7 @@ impl<'lua, T: ToLua<'lua>> ToLua<'lua> for Option<T> {
|
||||
}
|
||||
|
||||
impl<'lua, T: FromLua<'lua>> FromLua<'lua> for Option<T> {
|
||||
#[inline]
|
||||
fn from_lua(value: Value<'lua>, lua: &'lua Lua) -> Result<Self> {
|
||||
match value {
|
||||
Nil => Ok(None),
|
||||
|
||||
+28
-30
@@ -1,3 +1,5 @@
|
||||
#![allow(clippy::wrong_self_convention)]
|
||||
|
||||
use std::error::Error as StdError;
|
||||
use std::fmt;
|
||||
use std::io::Error as IoError;
|
||||
@@ -88,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
|
||||
@@ -131,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.
|
||||
@@ -140,7 +154,7 @@ pub enum Error {
|
||||
/// Original error returned by the Rust code.
|
||||
cause: Arc<Error>,
|
||||
},
|
||||
/// A Rust panic that was previosly resumed, returned again.
|
||||
/// 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.
|
||||
@@ -160,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 {
|
||||
@@ -203,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(()),
|
||||
@@ -230,11 +234,19 @@ 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")
|
||||
@@ -263,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>;
|
||||
}
|
||||
|
||||
+39
-2
@@ -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() {
|
||||
|
||||
+25
-3
@@ -80,6 +80,7 @@ pub const LUA_ERRERR: c_int = 5;
|
||||
#[cfg(any(feature = "lua53", feature = "lua52"))]
|
||||
pub const LUA_ERRERR: c_int = 6;
|
||||
|
||||
/// A raw Lua state associated with a thread.
|
||||
pub type lua_State = c_void;
|
||||
|
||||
// basic types
|
||||
@@ -111,20 +112,24 @@ 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;
|
||||
|
||||
/// A Lua integer, usually equivalent to `i64`.
|
||||
pub type lua_Integer = luaconf::LUA_INTEGER;
|
||||
|
||||
// unsigned integer type
|
||||
/// A Lua unsigned integer, usually equivalent to `u64`.
|
||||
pub type lua_Unsigned = luaconf::LUA_UNSIGNED;
|
||||
|
||||
// type for continuation-function contexts
|
||||
#[cfg(any(feature = "lua54", feature = "lua53"))]
|
||||
pub type lua_KContext = luaconf::LUA_KCONTEXT;
|
||||
|
||||
/// Type for native functions that can be passed to Lua.
|
||||
/// Type for native C functions that can be passed to Lua.
|
||||
pub type lua_CFunction = unsafe extern "C" fn(L: *mut lua_State) -> c_int;
|
||||
|
||||
// Type for continuation functions
|
||||
@@ -156,8 +161,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;
|
||||
|
||||
@@ -216,6 +226,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;
|
||||
@@ -531,6 +552,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,
|
||||
|
||||
+30
-5
@@ -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 {
|
||||
|
||||
+12
-16
@@ -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, 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,14 +192,14 @@ 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| {
|
||||
ffi::lua_pushcclosure(state, bind_call_impl, nargs + 2);
|
||||
protect_lua!(lua.state, nargs + 2, 1, fn(state) {
|
||||
ffi::lua_pushcclosure(state, bind_call_impl, ffi::lua_gettop(state));
|
||||
})?;
|
||||
|
||||
Ok(Function(lua.pop_ref()))
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+96
-6
@@ -1,5 +1,6 @@
|
||||
use std::ffi::CStr;
|
||||
use std::marker::PhantomData;
|
||||
use std::ops::{BitOr, BitOrAssign};
|
||||
use std::os::raw::{c_char, c_int};
|
||||
|
||||
use crate::ffi::{self, lua_Debug, lua_State};
|
||||
@@ -9,9 +10,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
|
||||
@@ -23,6 +24,25 @@ pub struct Debug<'a> {
|
||||
}
|
||||
|
||||
impl<'a> Debug<'a> {
|
||||
/// Returns the specific event that triggered the hook.
|
||||
///
|
||||
/// For [Lua 5.1] `DebugEvent::TailCall` is used for return events to indicate a return
|
||||
/// from a function that did a tail call.
|
||||
///
|
||||
/// [Lua 5.1]: https://www.lua.org/manual/5.1/manual.html#pdf-LUA_HOOKTAILRET
|
||||
pub fn event(&self) -> DebugEvent {
|
||||
unsafe {
|
||||
match (*self.ar).event {
|
||||
ffi::LUA_HOOKCALL => DebugEvent::Call,
|
||||
ffi::LUA_HOOKRET => DebugEvent::Ret,
|
||||
ffi::LUA_HOOKTAILCALL => DebugEvent::TailCall,
|
||||
ffi::LUA_HOOKLINE => DebugEvent::Line,
|
||||
ffi::LUA_HOOKCOUNT => DebugEvent::Count,
|
||||
event => mlua_panic!("Unknown Lua event code: {}", event),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Corresponds to the `n` what mask.
|
||||
pub fn names(&self) -> DebugNames<'a> {
|
||||
unsafe {
|
||||
@@ -95,6 +115,16 @@ impl<'a> Debug<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents a specific event that triggered the hook.
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub enum DebugEvent {
|
||||
Call,
|
||||
Ret,
|
||||
TailCall,
|
||||
Line,
|
||||
Count,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct DebugNames<'a> {
|
||||
pub name: Option<&'a [u8]>,
|
||||
@@ -130,7 +160,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
|
||||
@@ -140,6 +170,46 @@ pub struct HookTriggers {
|
||||
}
|
||||
|
||||
impl HookTriggers {
|
||||
/// Returns a new instance of `HookTriggers` with [`on_calls`] trigger set.
|
||||
///
|
||||
/// [`on_calls`]: #structfield.on_calls
|
||||
pub fn on_calls() -> Self {
|
||||
HookTriggers {
|
||||
on_calls: true,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a new instance of `HookTriggers` with [`on_returns`] trigger set.
|
||||
///
|
||||
/// [`on_returns`]: #structfield.on_returns
|
||||
pub fn on_returns() -> Self {
|
||||
HookTriggers {
|
||||
on_returns: true,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a new instance of `HookTriggers` with [`every_line`] trigger set.
|
||||
///
|
||||
/// [`every_line`]: #structfield.every_line
|
||||
pub fn every_line() -> Self {
|
||||
HookTriggers {
|
||||
every_line: true,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a new instance of `HookTriggers` with [`every_nth_instruction`] trigger set.
|
||||
///
|
||||
/// [`every_nth_instruction`]: #structfield.every_nth_instruction
|
||||
pub fn every_nth_instruction(n: u32) -> Self {
|
||||
HookTriggers {
|
||||
every_nth_instruction: Some(n),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
// Compute the mask to pass to `lua_sethook`.
|
||||
pub(crate) fn mask(&self) -> c_int {
|
||||
let mut mask: c_int = 0;
|
||||
@@ -165,6 +235,26 @@ impl HookTriggers {
|
||||
}
|
||||
}
|
||||
|
||||
impl BitOr for HookTriggers {
|
||||
type Output = Self;
|
||||
|
||||
fn bitor(mut self, rhs: Self) -> Self::Output {
|
||||
self.on_calls |= rhs.on_calls;
|
||||
self.on_returns |= rhs.on_returns;
|
||||
self.every_line |= rhs.every_line;
|
||||
if self.every_nth_instruction.is_none() && rhs.every_nth_instruction.is_some() {
|
||||
self.every_nth_instruction = rhs.every_nth_instruction;
|
||||
}
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl BitOrAssign for HookTriggers {
|
||||
fn bitor_assign(&mut self, rhs: Self) {
|
||||
*self = *self | rhs;
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) unsafe extern "C" fn hook_proc(state: *mut lua_State, ar: *mut lua_Debug) {
|
||||
callback_error(state, |_| {
|
||||
let debug = Debug {
|
||||
@@ -173,7 +263,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 +273,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]> {
|
||||
|
||||
+77
-13
@@ -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.4")]
|
||||
#![doc(html_root_url = "https://docs.rs/mlua/0.6.4")]
|
||||
// Deny warnings inside doc tests / examples. When this isn't present, rustdoc doesn't show *any*
|
||||
// warnings at all.
|
||||
#![doc(test(attr(deny(warnings))))]
|
||||
@@ -96,12 +98,12 @@ mod userdata;
|
||||
mod util;
|
||||
mod value;
|
||||
|
||||
pub use crate::ffi::lua_State;
|
||||
pub use crate::{ffi::lua_CFunction, 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::hook::{Debug, DebugEvent, DebugNames, DebugSource, DebugStack, HookTriggers};
|
||||
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 +111,87 @@ 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;
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "serialize")))]
|
||||
#[doc(inline)]
|
||||
pub use crate::serde::{
|
||||
de::Options as DeserializeOptions, ser::Options as SerializeOptions, LuaSerdeExt,
|
||||
};
|
||||
|
||||
pub mod prelude;
|
||||
#[cfg(feature = "serialize")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "serialize")))]
|
||||
pub mod serde;
|
||||
|
||||
// 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;
|
||||
|
||||
+1178
-474
File diff suppressed because it is too large
Load Diff
+19
-4
@@ -17,19 +17,19 @@ macro_rules! cstr {
|
||||
|
||||
macro_rules! mlua_panic {
|
||||
($msg:expr) => {
|
||||
panic!(bug_msg!($msg));
|
||||
panic!(bug_msg!($msg))
|
||||
};
|
||||
|
||||
($msg:expr,) => {
|
||||
mlua_panic!($msg);
|
||||
mlua_panic!($msg)
|
||||
};
|
||||
|
||||
($msg:expr, $($arg:expr),+) => {
|
||||
panic!(bug_msg!($msg), $($arg),+);
|
||||
panic!(bug_msg!($msg), $($arg),+)
|
||||
};
|
||||
|
||||
($msg:expr, $($arg:expr),+,) => {
|
||||
mlua_panic!($msg, $($arg),+);
|
||||
mlua_panic!($msg, $($arg),+)
|
||||
};
|
||||
}
|
||||
|
||||
@@ -94,3 +94,18 @@ macro_rules! require_module_feature {
|
||||
compile_error!("Feature `module` must be enabled in the `mlua` crate");
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! protect_lua {
|
||||
($state:expr, $nargs:expr, $nresults:expr, $f:expr) => {
|
||||
crate::util::protect_lua_closure($state, $nargs, $nresults, $f)
|
||||
};
|
||||
|
||||
($state:expr, $nargs:expr, $nresults:expr, fn($state_inner:ident) $code:expr) => {{
|
||||
unsafe extern "C" fn do_call($state_inner: *mut ffi::lua_State) -> ::std::os::raw::c_int {
|
||||
$code;
|
||||
$nresults
|
||||
}
|
||||
|
||||
crate::util::protect_lua_call($state, $nargs, do_call)
|
||||
}};
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#![allow(clippy::wrong_self_convention)]
|
||||
|
||||
use std::iter::FromIterator;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use std::result::Result as StdResult;
|
||||
|
||||
+9
-1
@@ -4,13 +4,21 @@ 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::{
|
||||
DeserializeOptions as LuaDeserializeOptions, LuaSerdeExt,
|
||||
SerializeOptions as LuaSerializeOptions,
|
||||
};
|
||||
|
||||
+314
-149
@@ -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,18 +13,20 @@ 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::types::{Callback, CallbackUpvalue, LuaRef, MaybeSend};
|
||||
use crate::userdata::{
|
||||
AnyUserData, MetaMethod, UserData, UserDataCell, UserDataFields, UserDataMethods,
|
||||
};
|
||||
use crate::util::{
|
||||
assert_stack, init_userdata_metatable, protect_lua_closure, push_string, push_userdata,
|
||||
assert_stack, check_stack, get_userdata, init_userdata_metatable, push_table, rawset_field,
|
||||
take_userdata, StackGuard,
|
||||
};
|
||||
use crate::value::{FromLuaMulti, MultiValue, ToLuaMulti, Value};
|
||||
use crate::value::{FromLua, FromLuaMulti, MultiValue, ToLua, ToLuaMulti, Value};
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
use {
|
||||
crate::types::AsyncCallback,
|
||||
futures_core::future::{Future, LocalBoxFuture},
|
||||
crate::types::{AsyncCallback, AsyncCallbackUpvalue, AsyncPollUpvalue},
|
||||
futures_core::future::Future,
|
||||
futures_util::future::{self, TryFutureExt},
|
||||
};
|
||||
|
||||
@@ -33,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 {
|
||||
@@ -52,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
|
||||
@@ -64,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.
|
||||
//
|
||||
@@ -81,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
|
||||
@@ -106,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"`
|
||||
///
|
||||
@@ -149,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.
|
||||
@@ -169,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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -203,11 +224,11 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
|
||||
/// use [`Scope::create_userdata`] instead.
|
||||
///
|
||||
/// 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
|
||||
/// will no longer have a `TypeId` associated with it, because `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
|
||||
/// "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.
|
||||
///
|
||||
@@ -219,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: *const 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, 2)?;
|
||||
lua.push_userdata_ref(&ud.0)?;
|
||||
if get_userdata(lua.state, -1) as *const _ == 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) }
|
||||
@@ -275,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)
|
||||
});
|
||||
@@ -304,73 +312,128 @@ 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)?;
|
||||
|
||||
// We need to wrap dummy userdata because their memory can be accessed by serializer
|
||||
push_userdata(lua.state, UserDataCell::new(UserDataWrapped::new(())))?;
|
||||
#[cfg(any(feature = "lua54", feature = "lua53"))]
|
||||
ffi::lua_pushlightuserdata(lua.state, data.as_ptr() as *mut c_void);
|
||||
#[cfg(any(feature = "lua52", feature = "lua51", feature = "luajit"))]
|
||||
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_ptr = 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_ptr, None);
|
||||
|
||||
#[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_ptr = ffi::lua_topointer(state, -1);
|
||||
ffi::lua_pop(state, 1);
|
||||
ud.lua.deregister_userdata_metatable(mt_ptr);
|
||||
|
||||
// Clear uservalue
|
||||
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
|
||||
ffi::lua_pushnil(state);
|
||||
#[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));
|
||||
|
||||
let mt_id = ffi::lua_topointer(lua.state, -1);
|
||||
ffi::lua_setmetatable(lua.state, -2);
|
||||
|
||||
let ud = AnyUserData(lua.pop_ref());
|
||||
lua.register_userdata_metatable(mt_id as isize);
|
||||
self.destructors.borrow_mut().push((ud.0.clone(), |ud| {
|
||||
let state = ud.lua.state;
|
||||
assert_stack(state, 2);
|
||||
ud.lua.push_ref(&ud);
|
||||
ffi::lua_getmetatable(state, -1);
|
||||
let mt_id = ffi::lua_topointer(state, -1);
|
||||
ffi::lua_pop(state, 1);
|
||||
ud.lua.deregister_userdata_metatable(mt_id as isize);
|
||||
vec![Box::new(take_userdata::<UserDataCell<()>>(state))]
|
||||
}));
|
||||
Ok(ud)
|
||||
}
|
||||
}
|
||||
@@ -378,7 +441,7 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
|
||||
// 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,
|
||||
@@ -387,27 +450,26 @@ 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.
|
||||
|
||||
ffi::lua_getupvalue(state, -1, 1);
|
||||
let ud1 = take_userdata::<Callback>(state);
|
||||
let ud = take_userdata::<CallbackUpvalue>(state);
|
||||
ffi::lua_pushnil(state);
|
||||
ffi::lua_setupvalue(state, -2, 1);
|
||||
|
||||
ffi::lua_getupvalue(state, -1, 2);
|
||||
let ud2 = take_userdata::<Lua>(state);
|
||||
ffi::lua_pushnil(state);
|
||||
ffi::lua_setupvalue(state, -2, 2);
|
||||
vec![Box::new(ud)]
|
||||
});
|
||||
self.destructors
|
||||
.borrow_mut()
|
||||
.push((f.0.clone(), destructor));
|
||||
|
||||
ffi::lua_pop(state, 1);
|
||||
vec![Box::new(ud1), Box::new(ud2)]
|
||||
}));
|
||||
Ok(f)
|
||||
}
|
||||
|
||||
@@ -419,10 +481,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.
|
||||
@@ -434,41 +500,33 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
|
||||
ffi::lua_getfenv(state, -1);
|
||||
|
||||
// Second, get the `get_poll()` closure using the corresponding key
|
||||
ffi::lua_pushstring(state, cstr!("get_poll"));
|
||||
f.lua.push_ref(&get_poll_str.0);
|
||||
ffi::lua_rawget(state, -2);
|
||||
|
||||
// Destroy all upvalues
|
||||
ffi::lua_getupvalue(state, -1, 1);
|
||||
let ud1 = take_userdata::<AsyncCallback>(state);
|
||||
let upvalue1 = take_userdata::<AsyncCallbackUpvalue>(state);
|
||||
ffi::lua_pushnil(state);
|
||||
ffi::lua_setupvalue(state, -2, 1);
|
||||
|
||||
ffi::lua_getupvalue(state, -1, 2);
|
||||
let ud2 = take_userdata::<Lua>(state);
|
||||
ffi::lua_pushnil(state);
|
||||
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)];
|
||||
let mut data: Vec<Box<dyn Any>> = vec![Box::new(upvalue1)];
|
||||
|
||||
// Finally, get polled future and destroy it
|
||||
ffi::lua_pushstring(state, cstr!("poll"));
|
||||
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);
|
||||
let upvalue2 = take_userdata::<AsyncPollUpvalue>(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.push(Box::new(upvalue2));
|
||||
}
|
||||
|
||||
data
|
||||
}));
|
||||
});
|
||||
self.destructors
|
||||
.borrow_mut()
|
||||
.push((f.0.clone(), destructor));
|
||||
|
||||
Ok(f)
|
||||
}
|
||||
@@ -477,7 +535,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
|
||||
@@ -592,7 +650,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>,
|
||||
@@ -604,59 +661,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)
|
||||
}),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
+261
-46
@@ -1,13 +1,101 @@
|
||||
use std::cell::RefCell;
|
||||
use std::collections::HashSet;
|
||||
use std::os::raw::c_void;
|
||||
use std::rc::Rc;
|
||||
use std::string::String as StdString;
|
||||
|
||||
use serde::de::{self, IntoDeserializer};
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
use crate::table::{TablePairs, TableSequence};
|
||||
use crate::table::{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: Value<'lua>,
|
||||
options: Options,
|
||||
visited: Rc<RefCell<HashSet<*const c_void>>>,
|
||||
}
|
||||
|
||||
/// A struct with options to change default deserializer behavior.
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
#[non_exhaustive]
|
||||
pub struct Options {
|
||||
/// If true, an attempt to serialize types such as `Thread`, `UserData`, `LightUserData`
|
||||
/// and `Error` will cause an error.
|
||||
/// Otherwise these types skipped when iterating or serialized as unit type.
|
||||
///
|
||||
/// Default: **true**
|
||||
pub deny_unsupported_types: bool,
|
||||
|
||||
/// If true, an attempt to serialize a recursive table (table that refers to itself)
|
||||
/// will cause an error.
|
||||
/// Otherwise subsequent attempts to serialize the same table will be ignored.
|
||||
///
|
||||
/// Default: **true**
|
||||
pub deny_recursive_tables: bool,
|
||||
}
|
||||
|
||||
impl Default for Options {
|
||||
fn default() -> Self {
|
||||
Options {
|
||||
deny_unsupported_types: true,
|
||||
deny_recursive_tables: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Options {
|
||||
/// Returns a new instance of `Options` with default parameters.
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
/// Sets [`deny_unsupported_types`] option.
|
||||
///
|
||||
/// [`deny_unsupported_types`]: #structfield.deny_unsupported_types
|
||||
pub fn deny_unsupported_types(mut self, enabled: bool) -> Self {
|
||||
self.deny_unsupported_types = enabled;
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets [`deny_recursive_tables`] option.
|
||||
///
|
||||
/// [`deny_recursive_tables`]: #structfield.deny_recursive_tables
|
||||
pub fn deny_recursive_tables(mut self, enabled: bool) -> Self {
|
||||
self.deny_recursive_tables = enabled;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> Deserializer<'lua> {
|
||||
/// Creates a new Lua Deserializer for the `Value`.
|
||||
pub fn new(value: Value<'lua>) -> Self {
|
||||
Self::new_with_options(value, Options::default())
|
||||
}
|
||||
|
||||
/// Creates a new Lua Deserializer for the `Value` with custom options.
|
||||
pub fn new_with_options(value: Value<'lua>, options: Options) -> Self {
|
||||
Deserializer {
|
||||
value,
|
||||
options,
|
||||
visited: Rc::new(RefCell::new(HashSet::new())),
|
||||
}
|
||||
}
|
||||
|
||||
fn from_parts(
|
||||
value: Value<'lua>,
|
||||
options: Options,
|
||||
visited: Rc<RefCell<HashSet<*const c_void>>>,
|
||||
) -> Self {
|
||||
Deserializer {
|
||||
value,
|
||||
options,
|
||||
visited,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua, 'de> serde::Deserializer<'de> for Deserializer<'lua> {
|
||||
type Error = Error;
|
||||
@@ -17,7 +105,7 @@ impl<'lua, 'de> serde::Deserializer<'de> for Deserializer<'lua> {
|
||||
where
|
||||
V: de::Visitor<'de>,
|
||||
{
|
||||
match self.0 {
|
||||
match self.value {
|
||||
Value::Nil => visitor.visit_unit(),
|
||||
Value::Boolean(b) => visitor.visit_bool(b),
|
||||
#[allow(clippy::useless_conversion)]
|
||||
@@ -35,7 +123,16 @@ impl<'lua, 'de> serde::Deserializer<'de> for Deserializer<'lua> {
|
||||
| Value::Thread(_)
|
||||
| Value::UserData(_)
|
||||
| Value::LightUserData(_)
|
||||
| Value::Error(_) => Err(de::Error::custom("invalid value type")),
|
||||
| Value::Error(_) => {
|
||||
if self.options.deny_unsupported_types {
|
||||
Err(de::Error::custom(format!(
|
||||
"unsupported value type `{}`",
|
||||
self.value.type_name()
|
||||
)))
|
||||
} else {
|
||||
visitor.visit_unit()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,7 +141,7 @@ impl<'lua, 'de> serde::Deserializer<'de> for Deserializer<'lua> {
|
||||
where
|
||||
V: de::Visitor<'de>,
|
||||
{
|
||||
match self.0 {
|
||||
match self.value {
|
||||
Value::Nil => visitor.visit_none(),
|
||||
Value::LightUserData(ud) if ud.0.is_null() => visitor.visit_none(),
|
||||
_ => visitor.visit_some(self),
|
||||
@@ -61,9 +158,11 @@ impl<'lua, 'de> serde::Deserializer<'de> for Deserializer<'lua> {
|
||||
where
|
||||
V: de::Visitor<'de>,
|
||||
{
|
||||
let (variant, value) = match self.0 {
|
||||
Value::Table(value) => {
|
||||
let mut iter = value.pairs::<StdString, Value>();
|
||||
let (variant, value, _guard) = match self.value {
|
||||
Value::Table(table) => {
|
||||
let _guard = RecursionGuard::new(&table, &self.visited);
|
||||
|
||||
let mut iter = table.pairs::<StdString, Value>();
|
||||
let (variant, value) = match iter.next() {
|
||||
Some(v) => v?,
|
||||
None => {
|
||||
@@ -80,13 +179,22 @@ impl<'lua, 'de> serde::Deserializer<'de> for Deserializer<'lua> {
|
||||
&"map with a single key",
|
||||
));
|
||||
}
|
||||
(variant, Some(value))
|
||||
if check_value_if_skip(&value, self.options, &self.visited)? {
|
||||
return Err(de::Error::custom("bad enum value"));
|
||||
}
|
||||
|
||||
(variant, Some(value), Some(_guard))
|
||||
}
|
||||
Value::String(variant) => (variant.to_str()?.to_owned(), None),
|
||||
Value::String(variant) => (variant.to_str()?.to_owned(), None, None),
|
||||
_ => return Err(de::Error::custom("bad enum value")),
|
||||
};
|
||||
|
||||
visitor.visit_enum(EnumDeserializer { variant, value })
|
||||
visitor.visit_enum(EnumDeserializer {
|
||||
variant,
|
||||
value,
|
||||
options: self.options,
|
||||
visited: self.visited,
|
||||
})
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@@ -94,12 +202,18 @@ impl<'lua, 'de> serde::Deserializer<'de> for Deserializer<'lua> {
|
||||
where
|
||||
V: de::Visitor<'de>,
|
||||
{
|
||||
match self.0 {
|
||||
match self.value {
|
||||
Value::Table(t) => {
|
||||
let _guard = RecursionGuard::new(&t, &self.visited);
|
||||
|
||||
let len = t.raw_len() as usize;
|
||||
let mut deserializer = SeqDeserializer(t.raw_sequence_values());
|
||||
let mut deserializer = SeqDeserializer {
|
||||
seq: t.raw_sequence_values(),
|
||||
options: self.options,
|
||||
visited: self.visited,
|
||||
};
|
||||
let seq = visitor.visit_seq(&mut deserializer)?;
|
||||
if deserializer.0.count() == 0 {
|
||||
if deserializer.seq.count() == 0 {
|
||||
Ok(seq)
|
||||
} else {
|
||||
Err(de::Error::invalid_length(
|
||||
@@ -108,7 +222,10 @@ impl<'lua, 'de> serde::Deserializer<'de> for Deserializer<'lua> {
|
||||
))
|
||||
}
|
||||
}
|
||||
_ => Err(de::Error::custom("invalid value type")),
|
||||
value => Err(de::Error::invalid_type(
|
||||
de::Unexpected::Other(value.type_name()),
|
||||
&"table",
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -138,9 +255,17 @@ impl<'lua, 'de> serde::Deserializer<'de> for Deserializer<'lua> {
|
||||
where
|
||||
V: de::Visitor<'de>,
|
||||
{
|
||||
match self.0 {
|
||||
match self.value {
|
||||
Value::Table(t) => {
|
||||
let mut deserializer = MapDeserializer::new(t.pairs());
|
||||
let _guard = RecursionGuard::new(&t, &self.visited);
|
||||
|
||||
let mut deserializer = MapDeserializer {
|
||||
pairs: t.pairs(),
|
||||
value: None,
|
||||
options: self.options,
|
||||
visited: self.visited,
|
||||
processed: 0,
|
||||
};
|
||||
let map = visitor.visit_map(&mut deserializer)?;
|
||||
let count = deserializer.pairs.count();
|
||||
if count == 0 {
|
||||
@@ -152,7 +277,10 @@ impl<'lua, 'de> serde::Deserializer<'de> for Deserializer<'lua> {
|
||||
))
|
||||
}
|
||||
}
|
||||
_ => Err(de::Error::custom("invalid value type")),
|
||||
value => Err(de::Error::invalid_type(
|
||||
de::Unexpected::Other(value.type_name()),
|
||||
&"table",
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -176,7 +304,11 @@ impl<'lua, 'de> serde::Deserializer<'de> for Deserializer<'lua> {
|
||||
}
|
||||
}
|
||||
|
||||
struct SeqDeserializer<'lua>(TableSequence<'lua, Value<'lua>>);
|
||||
struct SeqDeserializer<'lua> {
|
||||
seq: TableSequence<'lua, Value<'lua>>,
|
||||
options: Options,
|
||||
visited: Rc<RefCell<HashSet<*const c_void>>>,
|
||||
}
|
||||
|
||||
impl<'lua, 'de> de::SeqAccess<'de> for SeqDeserializer<'lua> {
|
||||
type Error = Error;
|
||||
@@ -185,14 +317,24 @@ impl<'lua, 'de> de::SeqAccess<'de> for SeqDeserializer<'lua> {
|
||||
where
|
||||
T: de::DeserializeSeed<'de>,
|
||||
{
|
||||
match self.0.next() {
|
||||
Some(value) => seed.deserialize(Deserializer(value?)).map(Some),
|
||||
None => Ok(None),
|
||||
loop {
|
||||
match self.seq.next() {
|
||||
Some(value) => {
|
||||
let value = value?;
|
||||
if check_value_if_skip(&value, self.options, &self.visited)? {
|
||||
continue;
|
||||
}
|
||||
let visited = Rc::clone(&self.visited);
|
||||
let deserializer = Deserializer::from_parts(value, self.options, visited);
|
||||
return seed.deserialize(deserializer).map(Some);
|
||||
}
|
||||
None => return Ok(None),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn size_hint(&self) -> Option<usize> {
|
||||
match self.0.size_hint() {
|
||||
match self.seq.size_hint() {
|
||||
(lower, Some(upper)) if lower == upper => Some(upper),
|
||||
_ => None,
|
||||
}
|
||||
@@ -202,19 +344,11 @@ impl<'lua, 'de> de::SeqAccess<'de> for SeqDeserializer<'lua> {
|
||||
struct MapDeserializer<'lua> {
|
||||
pairs: TablePairs<'lua, Value<'lua>, Value<'lua>>,
|
||||
value: Option<Value<'lua>>,
|
||||
options: Options,
|
||||
visited: Rc<RefCell<HashSet<*const c_void>>>,
|
||||
processed: usize,
|
||||
}
|
||||
|
||||
impl<'lua> MapDeserializer<'lua> {
|
||||
fn new(pairs: TablePairs<'lua, Value<'lua>, Value<'lua>>) -> Self {
|
||||
MapDeserializer {
|
||||
pairs,
|
||||
value: None,
|
||||
processed: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua, 'de> de::MapAccess<'de> for MapDeserializer<'lua> {
|
||||
type Error = Error;
|
||||
|
||||
@@ -222,15 +356,23 @@ impl<'lua, 'de> de::MapAccess<'de> for MapDeserializer<'lua> {
|
||||
where
|
||||
T: de::DeserializeSeed<'de>,
|
||||
{
|
||||
match self.pairs.next() {
|
||||
Some(item) => {
|
||||
let (key, value) = item?;
|
||||
self.processed += 1;
|
||||
self.value = Some(value);
|
||||
let key_de = Deserializer(key);
|
||||
seed.deserialize(key_de).map(Some)
|
||||
loop {
|
||||
match self.pairs.next() {
|
||||
Some(item) => {
|
||||
let (key, value) = item?;
|
||||
if check_value_if_skip(&key, self.options, &self.visited)?
|
||||
|| check_value_if_skip(&value, self.options, &self.visited)?
|
||||
{
|
||||
continue;
|
||||
}
|
||||
self.processed += 1;
|
||||
self.value = Some(value);
|
||||
let visited = Rc::clone(&self.visited);
|
||||
let key_de = Deserializer::from_parts(key, self.options, visited);
|
||||
return seed.deserialize(key_de).map(Some);
|
||||
}
|
||||
None => return Ok(None),
|
||||
}
|
||||
None => Ok(None),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -239,7 +381,10 @@ impl<'lua, 'de> de::MapAccess<'de> for MapDeserializer<'lua> {
|
||||
T: de::DeserializeSeed<'de>,
|
||||
{
|
||||
match self.value.take() {
|
||||
Some(value) => seed.deserialize(Deserializer(value)),
|
||||
Some(value) => {
|
||||
let visited = Rc::clone(&self.visited);
|
||||
seed.deserialize(Deserializer::from_parts(value, self.options, visited))
|
||||
}
|
||||
None => Err(de::Error::custom("value is missing")),
|
||||
}
|
||||
}
|
||||
@@ -255,6 +400,8 @@ impl<'lua, 'de> de::MapAccess<'de> for MapDeserializer<'lua> {
|
||||
struct EnumDeserializer<'lua> {
|
||||
variant: StdString,
|
||||
value: Option<Value<'lua>>,
|
||||
options: Options,
|
||||
visited: Rc<RefCell<HashSet<*const c_void>>>,
|
||||
}
|
||||
|
||||
impl<'lua, 'de> de::EnumAccess<'de> for EnumDeserializer<'lua> {
|
||||
@@ -266,13 +413,19 @@ impl<'lua, 'de> de::EnumAccess<'de> for EnumDeserializer<'lua> {
|
||||
T: de::DeserializeSeed<'de>,
|
||||
{
|
||||
let variant = self.variant.into_deserializer();
|
||||
let variant_access = VariantDeserializer { value: self.value };
|
||||
let variant_access = VariantDeserializer {
|
||||
value: self.value,
|
||||
options: self.options,
|
||||
visited: self.visited,
|
||||
};
|
||||
seed.deserialize(variant).map(|v| (v, variant_access))
|
||||
}
|
||||
}
|
||||
|
||||
struct VariantDeserializer<'lua> {
|
||||
value: Option<Value<'lua>>,
|
||||
options: Options,
|
||||
visited: Rc<RefCell<HashSet<*const c_void>>>,
|
||||
}
|
||||
|
||||
impl<'lua, 'de> de::VariantAccess<'de> for VariantDeserializer<'lua> {
|
||||
@@ -293,7 +446,9 @@ impl<'lua, 'de> de::VariantAccess<'de> for VariantDeserializer<'lua> {
|
||||
T: de::DeserializeSeed<'de>,
|
||||
{
|
||||
match self.value {
|
||||
Some(value) => seed.deserialize(Deserializer(value)),
|
||||
Some(value) => {
|
||||
seed.deserialize(Deserializer::from_parts(value, self.options, self.visited))
|
||||
}
|
||||
None => Err(de::Error::invalid_type(
|
||||
de::Unexpected::UnitVariant,
|
||||
&"newtype variant",
|
||||
@@ -306,7 +461,10 @@ impl<'lua, 'de> de::VariantAccess<'de> for VariantDeserializer<'lua> {
|
||||
V: de::Visitor<'de>,
|
||||
{
|
||||
match self.value {
|
||||
Some(value) => serde::Deserializer::deserialize_seq(Deserializer(value), visitor),
|
||||
Some(value) => serde::Deserializer::deserialize_seq(
|
||||
Deserializer::from_parts(value, self.options, self.visited),
|
||||
visitor,
|
||||
),
|
||||
None => Err(de::Error::invalid_type(
|
||||
de::Unexpected::UnitVariant,
|
||||
&"tuple variant",
|
||||
@@ -319,7 +477,10 @@ impl<'lua, 'de> de::VariantAccess<'de> for VariantDeserializer<'lua> {
|
||||
V: de::Visitor<'de>,
|
||||
{
|
||||
match self.value {
|
||||
Some(value) => serde::Deserializer::deserialize_map(Deserializer(value), visitor),
|
||||
Some(value) => serde::Deserializer::deserialize_map(
|
||||
Deserializer::from_parts(value, self.options, self.visited),
|
||||
visitor,
|
||||
),
|
||||
None => Err(de::Error::invalid_type(
|
||||
de::Unexpected::UnitVariant,
|
||||
&"struct variant",
|
||||
@@ -327,3 +488,57 @@ impl<'lua, 'de> de::VariantAccess<'de> for VariantDeserializer<'lua> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Adds `ptr` to the `visited` map and removes on drop
|
||||
// Used to track recursive tables but allow to traverse same tables multiple times
|
||||
struct RecursionGuard {
|
||||
ptr: *const c_void,
|
||||
visited: Rc<RefCell<HashSet<*const c_void>>>,
|
||||
}
|
||||
|
||||
impl RecursionGuard {
|
||||
#[inline]
|
||||
fn new(table: &Table, visited: &Rc<RefCell<HashSet<*const c_void>>>) -> Self {
|
||||
let visited = Rc::clone(visited);
|
||||
let ptr = unsafe { table.0.lua.get_ref_ptr(&table.0) };
|
||||
visited.borrow_mut().insert(ptr);
|
||||
RecursionGuard { ptr, visited }
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for RecursionGuard {
|
||||
fn drop(&mut self) {
|
||||
self.visited.borrow_mut().remove(&self.ptr);
|
||||
}
|
||||
}
|
||||
|
||||
// Checks `options` and decides should we emit an error or skip next element
|
||||
fn check_value_if_skip(
|
||||
value: &Value,
|
||||
options: Options,
|
||||
visited: &RefCell<HashSet<*const c_void>>,
|
||||
) -> Result<bool> {
|
||||
match value {
|
||||
Value::Table(table) => {
|
||||
let lua = table.0.lua;
|
||||
let ptr = unsafe { lua.get_ref_ptr(&table.0) };
|
||||
if visited.borrow().contains(&ptr) {
|
||||
if options.deny_recursive_tables {
|
||||
return Err(de::Error::custom("recursive table detected"));
|
||||
}
|
||||
return Ok(true); // skip
|
||||
}
|
||||
}
|
||||
Value::Function(_)
|
||||
| Value::Thread(_)
|
||||
| Value::UserData(_)
|
||||
| Value::LightUserData(_)
|
||||
| Value::Error(_)
|
||||
if !options.deny_unsupported_types =>
|
||||
{
|
||||
return Ok(true); // skip
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
Ok(false) // do not skip
|
||||
}
|
||||
|
||||
+111
-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, 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"`
|
||||
@@ -129,34 +158,56 @@ pub trait LuaSerdeExt<'lua> {
|
||||
/// }
|
||||
/// ```
|
||||
fn from_value<T: Deserialize<'lua>>(&'lua self, value: Value<'lua>) -> Result<T>;
|
||||
|
||||
/// Deserializes a `Value` into any serde deserializable object with options.
|
||||
///
|
||||
/// Requires `feature = "serialize"`
|
||||
///
|
||||
/// [`Value`]: enum.Value.html
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// use mlua::{Lua, Result, LuaSerdeExt, DeserializeOptions};
|
||||
/// use serde::Deserialize;
|
||||
///
|
||||
/// #[derive(Deserialize, Debug, PartialEq)]
|
||||
/// struct User {
|
||||
/// name: String,
|
||||
/// age: u8,
|
||||
/// }
|
||||
///
|
||||
/// fn main() -> Result<()> {
|
||||
/// let lua = Lua::new();
|
||||
/// let val = lua.load(r#"{name = "John Smith", age = 20, f = function() end}"#).eval()?;
|
||||
/// let options = DeserializeOptions::new().deny_unsupported_types(false);
|
||||
/// let u: User = lua.from_value_with(val, options)?;
|
||||
///
|
||||
/// assert_eq!(u, User { name: "John Smith".into(), age: 20 });
|
||||
///
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
fn from_value_with<T: Deserialize<'lua>>(
|
||||
&'lua self,
|
||||
value: Value<'lua>,
|
||||
options: de::Options,
|
||||
) -> Result<T>;
|
||||
}
|
||||
|
||||
impl<'lua> LuaSerdeExt<'lua> for Lua {
|
||||
fn null(&'lua self) -> Result<Value<'lua>> {
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(self.state);
|
||||
assert_stack(self.state, 3);
|
||||
|
||||
unsafe extern "C" fn push_null(state: *mut ffi::lua_State) -> c_int {
|
||||
ffi::lua_pushlightuserdata(state, ptr::null_mut());
|
||||
1
|
||||
}
|
||||
protect_lua(self.state, 0, push_null)?;
|
||||
Ok(self.pop_value())
|
||||
}
|
||||
fn 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 +215,57 @@ 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))
|
||||
}
|
||||
|
||||
fn from_value_with<T>(&'lua self, value: Value<'lua>, options: de::Options) -> Result<T>
|
||||
where
|
||||
T: Deserialize<'lua>,
|
||||
{
|
||||
T::deserialize(de::Deserializer::new_with_options(value, options))
|
||||
}
|
||||
}
|
||||
|
||||
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 2 stack spaces and calls checkstack.
|
||||
pub(crate) unsafe fn init_metatables(state: *mut ffi::lua_State) -> Result<()> {
|
||||
check_stack(state, 2)?;
|
||||
protect_lua!(state, 0, 0, fn(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;
|
||||
|
||||
+158
-44
@@ -9,17 +9,105 @@ use crate::lua::Lua;
|
||||
use crate::string::String;
|
||||
use crate::table::Table;
|
||||
use crate::types::Integer;
|
||||
use crate::util::{assert_stack, protect_lua, StackGuard};
|
||||
use crate::util::{check_stack, 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,
|
||||
}
|
||||
|
||||
/// 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 {
|
||||
/// Returns a new instance of `Options` with default parameters.
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
/// Sets [`set_array_metatable`] option.
|
||||
///
|
||||
/// [`set_array_metatable`]: #structfield.set_array_metatable
|
||||
pub fn set_array_metatable(mut self, enabled: bool) -> Self {
|
||||
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>> {
|
||||
value.to_lua(self.0)
|
||||
value.to_lua(self.lua)
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -62,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]
|
||||
@@ -106,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)
|
||||
}
|
||||
@@ -120,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))
|
||||
}
|
||||
@@ -132,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]
|
||||
@@ -159,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]
|
||||
@@ -169,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,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -186,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> {
|
||||
@@ -202,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, 4)?;
|
||||
|
||||
lua.push_ref(&self.table.0);
|
||||
lua.push_value(value)?;
|
||||
|
||||
unsafe extern "C" fn push_to_table(state: *mut ffi::lua_State) -> c_int {
|
||||
protect_lua!(lua.state, 2, 0, fn(state) {
|
||||
let len = ffi::lua_rawlen(state, -2) as Integer;
|
||||
ffi::lua_rawseti(state, -2, len + 1);
|
||||
1
|
||||
}
|
||||
|
||||
protect_lua(lua.state, 2, push_to_table)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -234,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)
|
||||
}
|
||||
@@ -250,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)
|
||||
}
|
||||
@@ -260,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> {
|
||||
@@ -271,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>> {
|
||||
@@ -286,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> {
|
||||
@@ -297,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)
|
||||
}
|
||||
|
||||
@@ -328,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)
|
||||
@@ -339,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> {
|
||||
@@ -350,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
|
||||
|
||||
+75
-100
@@ -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, 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, 5)?;
|
||||
|
||||
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, fn(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, 4)?;
|
||||
|
||||
lua.push_ref(&self.0);
|
||||
lua.push_value(key)?;
|
||||
protect_lua!(lua.state, 2, 1, fn(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,12 @@ impl<'lua> Table<'lua> {
|
||||
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(lua.state);
|
||||
assert_stack(lua.state, 5);
|
||||
check_stack(lua.state, 4)?;
|
||||
|
||||
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, fn(state) ffi::lua_gettable(state, -2))?;
|
||||
Ok(ffi::lua_isnil(lua.state, -1) == 0)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -207,19 +192,12 @@ impl<'lua> Table<'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(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, fn(state) ffi::lua_rawset(state, -3))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -227,13 +205,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 +231,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 +249,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 +264,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 +290,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 +303,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 +316,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 +335,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 +383,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 +391,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.
|
||||
///
|
||||
@@ -495,6 +475,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;
|
||||
@@ -667,7 +648,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)>,
|
||||
}
|
||||
|
||||
@@ -679,41 +660,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)),
|
||||
}
|
||||
@@ -746,31 +723,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
|
||||
|
||||
+74
-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, 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,32 @@ 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;
|
||||
#[inline(always)]
|
||||
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 +374,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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+41
-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;
|
||||
|
||||
@@ -26,13 +26,32 @@ pub struct LightUserData(pub *mut c_void);
|
||||
pub(crate) type Callback<'lua, 'a> =
|
||||
Box<dyn Fn(&'lua Lua, MultiValue<'lua>) -> Result<MultiValue<'lua>> + 'a>;
|
||||
|
||||
pub(crate) struct CallbackUpvalue<'lua> {
|
||||
pub(crate) lua: Lua,
|
||||
pub(crate) func: Callback<'lua, 'static>,
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
pub(crate) type AsyncCallback<'lua, 'a> =
|
||||
Box<dyn Fn(&'lua Lua, MultiValue<'lua>) -> LocalBoxFuture<'lua, Result<MultiValue<'lua>>> + 'a>;
|
||||
|
||||
pub(crate) type HookCallback = Arc<RefCell<dyn FnMut(&Lua, Debug) -> Result<()>>>;
|
||||
#[cfg(feature = "async")]
|
||||
pub(crate) struct AsyncCallbackUpvalue<'lua> {
|
||||
pub(crate) lua: Lua,
|
||||
pub(crate) func: AsyncCallback<'lua, 'static>,
|
||||
}
|
||||
|
||||
pub(crate) type UserDataCell<T> = RefCell<UserDataWrapped<T>>;
|
||||
#[cfg(feature = "async")]
|
||||
pub(crate) struct AsyncPollUpvalue<'lua> {
|
||||
pub(crate) lua: Lua,
|
||||
pub(crate) fut: LocalBoxFuture<'lua, Result<MultiValue<'lua>>>,
|
||||
}
|
||||
|
||||
#[cfg(feature = "send")]
|
||||
pub(crate) type HookCallback = Arc<RefCell<dyn FnMut(&Lua, Debug) -> Result<()> + Send>>;
|
||||
|
||||
#[cfg(not(feature = "send"))]
|
||||
pub(crate) type HookCallback = Arc<RefCell<dyn FnMut(&Lua, Debug) -> Result<()>>>;
|
||||
|
||||
#[cfg(feature = "send")]
|
||||
pub trait MaybeSend: Send {}
|
||||
@@ -44,9 +63,11 @@ pub trait MaybeSend {}
|
||||
#[cfg(not(feature = "send"))]
|
||||
impl<T> MaybeSend for T {}
|
||||
|
||||
pub(crate) struct DestructedUserdataMT;
|
||||
|
||||
/// 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 +91,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 +155,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
|
||||
}
|
||||
}
|
||||
|
||||
+490
-173
@@ -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;
|
||||
@@ -6,6 +11,7 @@ use std::future::Future;
|
||||
#[cfg(feature = "serialize")]
|
||||
use {
|
||||
serde::ser::{self, Serialize, Serializer},
|
||||
std::os::raw::c_void,
|
||||
std::result::Result as StdResult,
|
||||
};
|
||||
|
||||
@@ -13,10 +19,16 @@ 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_userdata, StackGuard};
|
||||
use crate::value::{FromLua, FromLuaMulti, ToLua, ToLuaMulti};
|
||||
|
||||
#[cfg(any(feature = "lua52", feature = "lua51", feature = "luajit"))]
|
||||
use crate::value::Value;
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
use crate::types::AsyncCallback;
|
||||
|
||||
/// Kinds of metamethods that can be overridden.
|
||||
///
|
||||
@@ -24,7 +36,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 +105,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 +126,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 +283,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 +295,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 +313,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 +343,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 +360,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 +374,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 +389,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 +401,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,17 +413,115 @@ 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.
|
||||
///
|
||||
/// By implementing this trait, a struct becomes eligible for use inside Lua code. Implementations
|
||||
/// of [`ToLua`] and [`FromLua`] are automatically provided.
|
||||
/// By implementing this trait, a struct becomes eligible for use inside Lua code.
|
||||
/// Implementation of [`ToLua`] is automatically provided, [`FromLua`] is implemented
|
||||
/// only for `T: UserData + Clone`.
|
||||
///
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
@@ -322,21 +541,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 +570,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,66 +581,111 @@ 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) {}
|
||||
}
|
||||
|
||||
pub(crate) struct UserDataWrapped<T> {
|
||||
pub(crate) data: *mut T,
|
||||
#[cfg(feature = "serialize")]
|
||||
ser: *mut dyn erased_serde::Serialize,
|
||||
}
|
||||
// Wraps UserData in a way to always implement `serde::Serialize` trait.
|
||||
pub(crate) struct UserDataCell<T>(RefCell<UserDataWrapped<T>>);
|
||||
|
||||
impl<T> Drop for UserDataWrapped<T> {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
drop(Box::from_raw(self.data));
|
||||
#[cfg(feature = "serialize")]
|
||||
if self.data as *mut () != self.ser as *mut () {
|
||||
drop(Box::from_raw(self.ser));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> UserDataWrapped<T> {
|
||||
impl<T> UserDataCell<T> {
|
||||
#[inline]
|
||||
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)),
|
||||
}
|
||||
UserDataCell(RefCell::new(UserDataWrapped::new(data)))
|
||||
}
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
#[inline]
|
||||
pub(crate) fn new_ser(data: T) -> Self
|
||||
where
|
||||
T: 'static + Serialize,
|
||||
{
|
||||
UserDataCell(RefCell::new(UserDataWrapped::new_ser(data)))
|
||||
}
|
||||
|
||||
// Immutably borrows the wrapped value.
|
||||
#[inline]
|
||||
pub(crate) fn try_borrow(&self) -> Result<Ref<T>> {
|
||||
self.0
|
||||
.try_borrow()
|
||||
.map(|r| Ref::map(r, |r| r.deref()))
|
||||
.map_err(|_| Error::UserDataBorrowError)
|
||||
}
|
||||
|
||||
// Mutably borrows the wrapped value.
|
||||
#[inline]
|
||||
pub(crate) fn try_borrow_mut(&self) -> Result<RefMut<T>> {
|
||||
self.0
|
||||
.try_borrow_mut()
|
||||
.map(|r| RefMut::map(r, |r| r.deref_mut()))
|
||||
.map_err(|_| Error::UserDataBorrowMutError)
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) enum UserDataWrapped<T> {
|
||||
Default(T),
|
||||
#[cfg(feature = "serialize")]
|
||||
Serializable(*mut T, *const dyn erased_serde::Serialize),
|
||||
}
|
||||
|
||||
impl<T> UserDataWrapped<T> {
|
||||
#[inline]
|
||||
fn new(data: T) -> Self {
|
||||
UserDataWrapped::Default(data)
|
||||
}
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
#[inline]
|
||||
fn new_ser(data: T) -> Self
|
||||
where
|
||||
T: 'static + Serialize,
|
||||
{
|
||||
let data_raw = Box::into_raw(Box::new(data));
|
||||
UserDataWrapped {
|
||||
data: data_raw,
|
||||
ser: data_raw,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> AsRef<T> for UserDataWrapped<T> {
|
||||
fn as_ref(&self) -> &T {
|
||||
unsafe { &*self.data }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> AsMut<T> for UserDataWrapped<T> {
|
||||
fn as_mut(&mut self) -> &mut T {
|
||||
unsafe { &mut *self.data }
|
||||
UserDataWrapped::Serializable(data_raw, data_raw)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
pub(crate) struct UserDataSerializeError;
|
||||
impl<T> Drop for UserDataWrapped<T> {
|
||||
fn drop(&mut self) {
|
||||
if let UserDataWrapped::Serializable(data, _) = *self {
|
||||
drop(unsafe { Box::from_raw(data) });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Deref for UserDataWrapped<T> {
|
||||
type Target = T;
|
||||
|
||||
#[inline]
|
||||
fn deref(&self) -> &Self::Target {
|
||||
match self {
|
||||
Self::Default(data) => data,
|
||||
#[cfg(feature = "serialize")]
|
||||
Self::Serializable(data, _) => unsafe { &**data },
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> DerefMut for UserDataWrapped<T> {
|
||||
#[inline]
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
match self {
|
||||
Self::Default(data) => data,
|
||||
#[cfg(feature = "serialize")]
|
||||
Self::Serializable(data, _) => unsafe { &mut **data },
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
struct UserDataSerializeError;
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
impl Serialize for UserDataSerializeError {
|
||||
@@ -468,26 +732,20 @@ 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`.
|
||||
#[inline]
|
||||
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 }))
|
||||
})
|
||||
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`.
|
||||
/// Returns a `UserDataBorrowMutError` if the userdata cannot be mutably borrowed.
|
||||
/// Returns a `UserDataTypeMismatch` if the userdata is not of type `T`.
|
||||
#[inline]
|
||||
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 }))
|
||||
})
|
||||
self.inspect(|cell| cell.try_borrow_mut())
|
||||
}
|
||||
|
||||
/// Sets an associated value to this `AnyUserData`.
|
||||
@@ -500,19 +758,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)?;
|
||||
lua.push_value(v)?;
|
||||
ffi::lua_setuservalue(lua.state, -2);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -526,57 +786,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)?;
|
||||
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)?;
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -594,34 +851,17 @@ impl<'lua> AnyUserData<'lua> {
|
||||
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, 2)?;
|
||||
|
||||
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,
|
||||
);
|
||||
|
||||
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)
|
||||
}
|
||||
} else {
|
||||
func(&*get_userdata::<UserDataCell<T>>(lua.state, -3))
|
||||
let type_id = lua.push_userdata_ref(&self.0)?;
|
||||
match type_id {
|
||||
Some(type_id) if type_id == TypeId::of::<T>() => {
|
||||
func(&*get_userdata::<UserDataCell<T>>(lua.state, -1))
|
||||
}
|
||||
_ => Err(Error::UserDataTypeMismatch),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -640,23 +880,100 @@ 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)?;
|
||||
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)
|
||||
lua.push_userdata_ref(&self.0).map_err(ser::Error::custom)?;
|
||||
let ud = &*get_userdata::<UserDataCell<c_void>>(lua.state, -1);
|
||||
let data =
|
||||
ud.0.try_borrow()
|
||||
.map_err(|_| ser::Error::custom(Error::UserDataBorrowError))?;
|
||||
match *data {
|
||||
UserDataWrapped::Default(_) => UserDataSerializeError.serialize(serializer),
|
||||
UserDataWrapped::Serializable(_, ser) => (&*ser).serialize(serializer),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+518
-363
File diff suppressed because it is too large
Load Diff
+19
-8
@@ -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,
|
||||
@@ -159,18 +157,21 @@ pub struct MultiValue<'lua>(Vec<Value<'lua>>);
|
||||
|
||||
impl<'lua> MultiValue<'lua> {
|
||||
/// Creates an empty `MultiValue` containing no values.
|
||||
#[inline]
|
||||
pub fn new() -> MultiValue<'lua> {
|
||||
MultiValue(Vec::new())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> Default for MultiValue<'lua> {
|
||||
#[inline]
|
||||
fn default() -> MultiValue<'lua> {
|
||||
MultiValue::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> FromIterator<Value<'lua>> for MultiValue<'lua> {
|
||||
#[inline]
|
||||
fn from_iter<I: IntoIterator<Item = Value<'lua>>>(iter: I) -> Self {
|
||||
MultiValue::from_vec(Vec::from_iter(iter))
|
||||
}
|
||||
@@ -180,6 +181,7 @@ impl<'lua> IntoIterator for MultiValue<'lua> {
|
||||
type Item = Value<'lua>;
|
||||
type IntoIter = iter::Rev<vec::IntoIter<Value<'lua>>>;
|
||||
|
||||
#[inline]
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.0.into_iter().rev()
|
||||
}
|
||||
@@ -189,43 +191,52 @@ impl<'a, 'lua> IntoIterator for &'a MultiValue<'lua> {
|
||||
type Item = &'a Value<'lua>;
|
||||
type IntoIter = iter::Rev<slice::Iter<'a, Value<'lua>>>;
|
||||
|
||||
#[inline]
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
(&self.0).iter().rev()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> MultiValue<'lua> {
|
||||
#[inline]
|
||||
pub fn from_vec(mut v: Vec<Value<'lua>>) -> MultiValue<'lua> {
|
||||
v.reverse();
|
||||
MultiValue(v)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn into_vec(self) -> Vec<Value<'lua>> {
|
||||
let mut v = self.0;
|
||||
v.reverse();
|
||||
v
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn reserve(&mut self, size: usize) {
|
||||
self.0.reserve(size);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn push_front(&mut self, value: Value<'lua>) {
|
||||
self.0.push(value);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn pop_front(&mut self) -> Option<Value<'lua>> {
|
||||
self.0.pop()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn len(&self) -> usize {
|
||||
self.0.len()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.0.len() == 0
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn iter(&self) -> iter::Rev<slice::Iter<Value<'lua>>> {
|
||||
self.0.iter().rev()
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ 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(
|
||||
|
||||
@@ -4,7 +4,7 @@ error[E0373]: closure may outlive the current function, but it borrows `test`, w
|
||||
9 | let _ = lua.create_function(|_, ()| -> Result<i32> {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^ may outlive borrowed value `test`
|
||||
10 | Ok(test.0)
|
||||
| ---- `test` is borrowed here
|
||||
| ------ `test` is borrowed here
|
||||
|
|
||||
note: function requires argument type to outlive `'static`
|
||||
--> $DIR/function_borrow.rs:9:13
|
||||
|
||||
@@ -14,3 +14,22 @@ error[E0277]: the type `UnsafeCell<()>` may contain interior mutability and a re
|
||||
= note: required because it appears within the type `Lua`
|
||||
= note: required because of the requirements on the impl of `UnwindSafe` for `&Lua`
|
||||
= note: required because it appears within the type `[closure@$DIR/tests/compile/lua_norefunwindsafe.rs:7:18: 7:48]`
|
||||
|
||||
error[E0277]: the type `UnsafeCell<mlua::lua::ExtraData>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
|
||||
--> $DIR/lua_norefunwindsafe.rs:7:5
|
||||
|
|
||||
7 | catch_unwind(|| lua.create_table().unwrap());
|
||||
| ^^^^^^^^^^^^ `UnsafeCell<mlua::lua::ExtraData>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
|
||||
|
|
||||
::: $RUST/std/src/panic.rs
|
||||
|
|
||||
| pub fn catch_unwind<F: FnOnce() -> R + UnwindSafe, R>(f: F) -> Result<R> {
|
||||
| ---------- required by this bound in `catch_unwind`
|
||||
|
|
||||
= help: within `Lua`, the trait `RefUnwindSafe` is not implemented for `UnsafeCell<mlua::lua::ExtraData>`
|
||||
= note: required because it appears within the type `alloc::sync::ArcInner<UnsafeCell<mlua::lua::ExtraData>>`
|
||||
= note: required because it appears within the type `PhantomData<alloc::sync::ArcInner<UnsafeCell<mlua::lua::ExtraData>>>`
|
||||
= note: required because it appears within the type `Arc<UnsafeCell<mlua::lua::ExtraData>>`
|
||||
= note: required because it appears within the type `Lua`
|
||||
= note: required because of the requirements on the impl of `UnwindSafe` for `&Lua`
|
||||
= note: required because it appears within the type `[closure@$DIR/tests/compile/lua_norefunwindsafe.rs:7:18: 7:48]`
|
||||
|
||||
@@ -16,3 +16,24 @@ error[E0277]: the type `UnsafeCell<()>` may contain interior mutability and a re
|
||||
= note: required because it appears within the type `mlua::types::LuaRef<'_>`
|
||||
= note: required because it appears within the type `LuaTable<'_>`
|
||||
= note: required because it appears within the type `[closure@$DIR/tests/compile/ref_nounwindsafe.rs:8:18: 8:54]`
|
||||
|
||||
error[E0277]: the type `UnsafeCell<mlua::lua::ExtraData>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
|
||||
--> $DIR/ref_nounwindsafe.rs:8:5
|
||||
|
|
||||
8 | catch_unwind(move || table.set("a", "b").unwrap());
|
||||
| ^^^^^^^^^^^^ `UnsafeCell<mlua::lua::ExtraData>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
|
||||
|
|
||||
::: $RUST/std/src/panic.rs
|
||||
|
|
||||
| pub fn catch_unwind<F: FnOnce() -> R + UnwindSafe, R>(f: F) -> Result<R> {
|
||||
| ---------- required by this bound in `catch_unwind`
|
||||
|
|
||||
= help: within `Lua`, the trait `RefUnwindSafe` is not implemented for `UnsafeCell<mlua::lua::ExtraData>`
|
||||
= note: required because it appears within the type `alloc::sync::ArcInner<UnsafeCell<mlua::lua::ExtraData>>`
|
||||
= note: required because it appears within the type `PhantomData<alloc::sync::ArcInner<UnsafeCell<mlua::lua::ExtraData>>>`
|
||||
= note: required because it appears within the type `Arc<UnsafeCell<mlua::lua::ExtraData>>`
|
||||
= note: required because it appears within the type `Lua`
|
||||
= note: required because of the requirements on the impl of `UnwindSafe` for `&Lua`
|
||||
= note: required because it appears within the type `mlua::types::LuaRef<'_>`
|
||||
= note: required because it appears within the type `LuaTable<'_>`
|
||||
= note: required because it appears within the type `[closure@$DIR/tests/compile/ref_nounwindsafe.rs:8:18: 8:54]`
|
||||
|
||||
@@ -7,7 +7,7 @@ error[E0373]: closure may outlive the current function, but it borrows `test`, w
|
||||
14 | .create_function_mut(|_, ()| {
|
||||
| ^^^^^^^ may outlive borrowed value `test`
|
||||
15 | test.field = 42;
|
||||
| ---- `test` is borrowed here
|
||||
| ---------- `test` is borrowed here
|
||||
|
|
||||
note: function requires argument type to outlive `'1`
|
||||
--> $DIR/scope_invariance.rs:13:13
|
||||
|
||||
@@ -2,7 +2,7 @@ use mlua::{Lua, UserData};
|
||||
|
||||
fn main() {
|
||||
struct MyUserData<'a>(&'a mut i32);
|
||||
impl<'a> UserData for MyUserData<'a> {};
|
||||
impl<'a> UserData for MyUserData<'a> {}
|
||||
|
||||
let mut i = 1;
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ use mlua::{Lua, UserData};
|
||||
fn main() {
|
||||
// Should not allow userdata borrow to outlive lifetime of AnyUserData handle
|
||||
struct MyUserData<'a>(&'a i32);
|
||||
impl<'a> UserData for MyUserData<'a> {};
|
||||
impl<'a> UserData for MyUserData<'a> {}
|
||||
|
||||
let igood = 1;
|
||||
|
||||
|
||||
@@ -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(())
|
||||
}
|
||||
+18
-1
@@ -76,6 +76,23 @@ fn test_rust_function() -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_c_function() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
unsafe extern "C" fn c_function(state: *mut mlua::lua_State) -> std::os::raw::c_int {
|
||||
let lua = Lua::init_from_ptr(state);
|
||||
lua.globals().set("c_function", true).unwrap();
|
||||
0
|
||||
}
|
||||
|
||||
let func = unsafe { lua.create_c_function(c_function)? };
|
||||
func.call(())?;
|
||||
assert_eq!(lua.globals().get::<_, bool>("c_function")?, true);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_dump() -> Result<()> {
|
||||
let lua = unsafe { Lua::unsafe_new() };
|
||||
@@ -83,7 +100,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");
|
||||
|
||||
|
||||
+54
-70
@@ -3,24 +3,32 @@ use std::ops::Deref;
|
||||
use std::str;
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use mlua::{Error, HookTriggers, Lua, Result, Value};
|
||||
use mlua::{DebugEvent, Error, HookTriggers, Lua, Result, Value};
|
||||
|
||||
#[test]
|
||||
fn line_counts() -> Result<()> {
|
||||
fn test_hook_triggers_bitor() {
|
||||
let trigger = HookTriggers::on_calls()
|
||||
| HookTriggers::on_returns()
|
||||
| HookTriggers::every_line()
|
||||
| HookTriggers::every_nth_instruction(5);
|
||||
|
||||
assert!(trigger.on_calls);
|
||||
assert!(trigger.on_returns);
|
||||
assert!(trigger.every_line);
|
||||
assert_eq!(trigger.every_nth_instruction, Some(5));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_line_counts() -> Result<()> {
|
||||
let output = Arc::new(Mutex::new(Vec::new()));
|
||||
let hook_output = output.clone();
|
||||
|
||||
let lua = Lua::new();
|
||||
lua.set_hook(
|
||||
HookTriggers {
|
||||
every_line: true,
|
||||
..Default::default()
|
||||
},
|
||||
move |_lua, debug| {
|
||||
hook_output.lock().unwrap().push(debug.curr_line());
|
||||
Ok(())
|
||||
},
|
||||
)?;
|
||||
lua.set_hook(HookTriggers::every_line(), move |_lua, debug| {
|
||||
assert_eq!(debug.event(), DebugEvent::Line);
|
||||
hook_output.lock().unwrap().push(debug.curr_line());
|
||||
Ok(())
|
||||
})?;
|
||||
lua.load(
|
||||
r#"
|
||||
local x = 2 + 3
|
||||
@@ -43,25 +51,20 @@ 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();
|
||||
|
||||
let lua = Lua::new();
|
||||
lua.set_hook(
|
||||
HookTriggers {
|
||||
on_calls: true,
|
||||
..Default::default()
|
||||
},
|
||||
move |_lua, debug| {
|
||||
let names = debug.names();
|
||||
let source = debug.source();
|
||||
let name = names.name.map(|s| str::from_utf8(s).unwrap().to_owned());
|
||||
let what = source.what.map(|s| str::from_utf8(s).unwrap().to_owned());
|
||||
hook_output.lock().unwrap().push((name, what));
|
||||
Ok(())
|
||||
},
|
||||
)?;
|
||||
lua.set_hook(HookTriggers::on_calls(), move |_lua, debug| {
|
||||
assert_eq!(debug.event(), DebugEvent::Call);
|
||||
let names = debug.names();
|
||||
let source = debug.source();
|
||||
let name = names.name.map(|s| str::from_utf8(s).unwrap().to_owned());
|
||||
let what = source.what.map(|s| str::from_utf8(s).unwrap().to_owned());
|
||||
hook_output.lock().unwrap().push((name, what));
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
lua.load(
|
||||
r#"
|
||||
@@ -95,19 +98,14 @@ fn function_calls() -> Result<()> {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn error_within_hook() -> Result<()> {
|
||||
fn test_error_within_hook() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
lua.set_hook(
|
||||
HookTriggers {
|
||||
every_line: true,
|
||||
..Default::default()
|
||||
},
|
||||
|_lua, _debug| {
|
||||
Err(Error::RuntimeError(
|
||||
"Something happened in there!".to_string(),
|
||||
))
|
||||
},
|
||||
)?;
|
||||
|
||||
lua.set_hook(HookTriggers::every_line(), |_lua, _debug| {
|
||||
Err(Error::RuntimeError(
|
||||
"Something happened in there!".to_string(),
|
||||
))
|
||||
})?;
|
||||
|
||||
let err = lua
|
||||
.load("x = 1")
|
||||
@@ -126,7 +124,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;
|
||||
|
||||
@@ -135,11 +133,9 @@ fn limit_execution_instructions() -> Result<()> {
|
||||
lua.load("jit.off()").exec()?;
|
||||
|
||||
lua.set_hook(
|
||||
HookTriggers {
|
||||
every_nth_instruction: Some(30),
|
||||
..Default::default()
|
||||
},
|
||||
move |_lua, _debug| {
|
||||
HookTriggers::every_nth_instruction(30),
|
||||
move |_lua, debug| {
|
||||
assert_eq!(debug.event(), DebugEvent::Count);
|
||||
max_instructions -= 30;
|
||||
if max_instructions < 0 {
|
||||
Err(Error::RuntimeError("time's up".to_string()))
|
||||
@@ -165,20 +161,14 @@ fn limit_execution_instructions() -> Result<()> {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hook_removal() -> Result<()> {
|
||||
fn test_hook_removal() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
lua.set_hook(
|
||||
HookTriggers {
|
||||
every_nth_instruction: Some(1),
|
||||
..Default::default()
|
||||
},
|
||||
|_lua, _debug| {
|
||||
Err(Error::RuntimeError(
|
||||
"this hook should've been removed by this time".to_string(),
|
||||
))
|
||||
},
|
||||
)?;
|
||||
lua.set_hook(HookTriggers::every_nth_instruction(1), |_lua, _debug| {
|
||||
Err(Error::RuntimeError(
|
||||
"this hook should've been removed by this time".to_string(),
|
||||
))
|
||||
})?;
|
||||
|
||||
assert!(lua.load("local x = 1").exec().is_err());
|
||||
lua.remove_hook();
|
||||
@@ -188,7 +178,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);
|
||||
}
|
||||
@@ -198,19 +188,14 @@ fn hook_swap_within_hook() -> Result<()> {
|
||||
});
|
||||
|
||||
TL_LUA.with(|tl| {
|
||||
tl.borrow().as_ref().unwrap().set_hook(
|
||||
HookTriggers {
|
||||
every_line: true,
|
||||
..Default::default()
|
||||
},
|
||||
move |lua, _debug| {
|
||||
tl.borrow()
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.set_hook(HookTriggers::every_line(), move |lua, _debug| {
|
||||
lua.globals().set("ok", 1i64)?;
|
||||
TL_LUA.with(|tl| {
|
||||
tl.borrow().as_ref().unwrap().set_hook(
|
||||
HookTriggers {
|
||||
every_line: true,
|
||||
..Default::default()
|
||||
},
|
||||
HookTriggers::every_line(),
|
||||
move |lua, _debug| {
|
||||
lua.load(
|
||||
r#"
|
||||
@@ -228,8 +213,7 @@ fn hook_swap_within_hook() -> Result<()> {
|
||||
},
|
||||
)
|
||||
})
|
||||
},
|
||||
)
|
||||
})
|
||||
})?;
|
||||
|
||||
TL_LUA.with(|tl| {
|
||||
|
||||
@@ -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(())
|
||||
}
|
||||
@@ -38,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());
|
||||
@@ -59,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",
|
||||
|
||||
+165
-82
@@ -1,12 +1,14 @@
|
||||
use std::cell::Cell;
|
||||
use std::rc::Rc;
|
||||
use std::sync::Arc;
|
||||
|
||||
use mlua::{
|
||||
AnyUserData, Error, Function, Lua, MetaMethod, Result, String, UserData, UserDataMethods,
|
||||
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));
|
||||
@@ -25,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),
|
||||
};
|
||||
|
||||
@@ -33,81 +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("static_ud", scope.create_userdata(MyUserdata(rc.clone()))?)?;
|
||||
assert_eq!(Rc::strong_count(&rc), 2);
|
||||
Ok(())
|
||||
})?;
|
||||
assert_eq!(Rc::strong_count(&rc), 1);
|
||||
|
||||
match lua.load("static_ud:method()").exec() {
|
||||
Err(Error::CallbackError { ref cause, .. }) => match cause.as_ref() {
|
||||
Error::CallbackDestructed => {}
|
||||
e => panic!("expected CallbackDestructed, got {:?}", e),
|
||||
},
|
||||
r => panic!("improper return for destructed userdata: {:?}", r),
|
||||
};
|
||||
|
||||
let static_ud = lua.globals().get::<_, AnyUserData>("static_ud")?;
|
||||
match static_ud.borrow::<MyUserdata>() {
|
||||
Ok(_) => panic!("borrowed destructed userdata"),
|
||||
Err(Error::UserDataDestructed) => {}
|
||||
Err(e) => panic!("expected UserDataDestructed, got {:?}", e),
|
||||
}
|
||||
|
||||
// Check non-static UserData drop
|
||||
struct MyUserDataRef<'a>(&'a Cell<i64>);
|
||||
|
||||
impl<'a> UserData for MyUserDataRef<'a> {
|
||||
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
|
||||
methods.add_method("inc", |_, data, ()| {
|
||||
data.0.set(data.0.get() + 1);
|
||||
Ok(())
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
let i = Cell::new(1);
|
||||
lua.scope(|scope| {
|
||||
lua.globals().set(
|
||||
"nonstatic_ud",
|
||||
scope.create_nonstatic_userdata(MyUserDataRef(&i))?,
|
||||
)
|
||||
})?;
|
||||
|
||||
match lua.load("nonstatic_ud:inc(1)").exec() {
|
||||
Err(Error::CallbackError { ref cause, .. }) => match cause.as_ref() {
|
||||
Error::CallbackDestructed => {}
|
||||
e => panic!("expected CallbackDestructed, got {:?}", e),
|
||||
},
|
||||
r => panic!("improper return for destructed userdata: {:?}", r),
|
||||
};
|
||||
|
||||
let nonstatic_ud = lua.globals().get::<_, AnyUserData>("nonstatic_ud")?;
|
||||
match nonstatic_ud.borrow::<MyUserDataRef>() {
|
||||
Ok(_) => panic!("borrowed destructed userdata"),
|
||||
Err(Error::UserDataDestructed) => {}
|
||||
Err(e) => panic!("expected UserDataDestructed, got {:?}", e),
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn scope_capture() -> Result<()> {
|
||||
fn test_scope_capture() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let mut i = 0;
|
||||
@@ -125,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()?;
|
||||
@@ -140,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> {
|
||||
@@ -181,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> {
|
||||
@@ -223,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> {
|
||||
@@ -273,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(())
|
||||
}
|
||||
|
||||
+164
-7
@@ -1,6 +1,11 @@
|
||||
#![cfg(feature = "serialize")]
|
||||
|
||||
use mlua::{Error, Lua, LuaSerdeExt, Result as LuaResult, UserData, Value};
|
||||
use std::collections::HashMap;
|
||||
|
||||
use mlua::{
|
||||
DeserializeOptions, Error, Lua, LuaSerdeExt, Result as LuaResult, SerializeOptions, UserData,
|
||||
Value,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[test]
|
||||
@@ -15,10 +20,10 @@ fn test_serialize() -> Result<(), Box<dyn std::error::Error>> {
|
||||
|
||||
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
|
||||
@@ -143,7 +148,7 @@ 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 {
|
||||
@@ -173,7 +178,7 @@ 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 {
|
||||
@@ -230,6 +235,106 @@ 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_nested_tables() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let value = lua
|
||||
.load(
|
||||
r#"
|
||||
local table_a = {a = "a"}
|
||||
local table_b = {"b"}
|
||||
return {
|
||||
a = table_a,
|
||||
b = {table_b, table_b},
|
||||
ab = {a = table_a, b = table_b}
|
||||
}
|
||||
"#,
|
||||
)
|
||||
.eval::<Value>()?;
|
||||
let got = lua.from_value::<serde_json::Value>(value)?;
|
||||
assert_eq!(
|
||||
got,
|
||||
serde_json::json!({
|
||||
"a": {"a": "a"},
|
||||
"b": [["b"], ["b"]],
|
||||
"ab": {"a": {"a": "a"}, "b": ["b"]},
|
||||
})
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_from_value_struct() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let lua = Lua::new();
|
||||
@@ -238,7 +343,7 @@ fn test_from_value_struct() -> Result<(), Box<dyn std::error::Error>> {
|
||||
struct Test {
|
||||
int: u32,
|
||||
seq: Vec<String>,
|
||||
map: std::collections::HashMap<i32, i32>,
|
||||
map: HashMap<i32, i32>,
|
||||
empty: Vec<()>,
|
||||
tuple: (u8, u8, u8),
|
||||
}
|
||||
@@ -305,7 +410,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)]
|
||||
@@ -341,3 +446,55 @@ fn test_from_value_enum_untagged() -> Result<(), Box<dyn std::error::Error>> {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_from_value_with_options() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let lua = Lua::new();
|
||||
|
||||
// Deny unsupported types by default
|
||||
let value = Value::Function(lua.create_function(|_, ()| Ok(()))?);
|
||||
match lua.from_value::<Option<String>>(value) {
|
||||
Ok(v) => panic!("expected deserialization error, got {:?}", v),
|
||||
Err(Error::DeserializeError(err)) => {
|
||||
assert!(err.contains("unsupported value type"))
|
||||
}
|
||||
Err(err) => panic!("expected `DeserializeError` error, got {:?}", err),
|
||||
};
|
||||
|
||||
// Allow unsupported types
|
||||
let value = Value::Function(lua.create_function(|_, ()| Ok(()))?);
|
||||
let options = DeserializeOptions::new().deny_unsupported_types(false);
|
||||
assert_eq!(lua.from_value_with::<()>(value, options)?, ());
|
||||
|
||||
// Allow unsupported types (in a table seq)
|
||||
let value = lua.load(r#"{"a", "b", function() end, "c"}"#).eval()?;
|
||||
let options = DeserializeOptions::new().deny_unsupported_types(false);
|
||||
assert_eq!(
|
||||
lua.from_value_with::<Vec<String>>(value, options)?,
|
||||
vec!["a".to_string(), "b".to_string(), "c".to_string()]
|
||||
);
|
||||
|
||||
// Deny recursive tables by default
|
||||
let value = lua.load(r#"local t = {}; t.t = t; return t"#).eval()?;
|
||||
match lua.from_value::<HashMap<String, Option<String>>>(value) {
|
||||
Ok(v) => panic!("expected deserialization error, got {:?}", v),
|
||||
Err(Error::DeserializeError(err)) => {
|
||||
assert!(err.contains("recursive table detected"))
|
||||
}
|
||||
Err(err) => panic!("expected `DeserializeError` error, got {:?}", err),
|
||||
};
|
||||
|
||||
// Serialize Lua globals table
|
||||
#[derive(Debug, Deserialize)]
|
||||
struct Globals {
|
||||
hello: String,
|
||||
}
|
||||
let options = DeserializeOptions::new()
|
||||
.deny_unsupported_types(false)
|
||||
.deny_recursive_tables(false);
|
||||
lua.load(r#"hello = "world""#).exec()?;
|
||||
let globals: Globals = lua.from_value_with(Value::Table(lua.globals()), options)?;
|
||||
assert_eq!(globals.hello, "world");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
+7
-3
@@ -3,7 +3,7 @@ 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());
|
||||
}
|
||||
@@ -21,7 +21,7 @@ fn compare() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn string_views() -> Result<()> {
|
||||
fn test_string_views() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
lua.load(
|
||||
@@ -39,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?"[..]
|
||||
@@ -55,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])?;
|
||||
|
||||
+161
-30
@@ -1,11 +1,14 @@
|
||||
use std::collections::HashMap;
|
||||
use std::iter::FromIterator;
|
||||
use std::panic::{catch_unwind, AssertUnwindSafe};
|
||||
use std::string::String as StdString;
|
||||
use std::sync::atomic::{AtomicU32, Ordering};
|
||||
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]
|
||||
@@ -23,7 +26,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"),
|
||||
@@ -52,7 +55,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"))
|
||||
@@ -63,7 +66,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() {
|
||||
@@ -81,6 +84,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);
|
||||
@@ -162,7 +166,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)
|
||||
@@ -221,6 +225,7 @@ fn test_coercion() -> Result<()> {
|
||||
int = 123
|
||||
str = "123"
|
||||
num = 123.0
|
||||
func = function() end
|
||||
"#,
|
||||
)
|
||||
.exec()?;
|
||||
@@ -229,6 +234,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(())
|
||||
}
|
||||
@@ -377,18 +383,23 @@ fn test_error() -> Result<()> {
|
||||
|
||||
#[test]
|
||||
fn test_panic() -> Result<()> {
|
||||
fn make_lua() -> Result<Lua> {
|
||||
let lua = Lua::new();
|
||||
fn make_lua(options: LuaOptions) -> Result<Lua> {
|
||||
let lua = Lua::new_with(StdLib::ALL_SAFE, options)?;
|
||||
let rust_panic_function =
|
||||
lua.create_function(|_, ()| -> Result<()> { panic!("rust panic") })?;
|
||||
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 triggerting Lua error passing Rust panic (must be resumed)
|
||||
// Test triggering Lua error with sending Rust panic (must be resumed)
|
||||
{
|
||||
let lua = make_lua()?;
|
||||
let lua = make_lua(LuaOptions::default())?;
|
||||
|
||||
match catch_unwind(AssertUnwindSafe(|| -> Result<()> {
|
||||
lua.load(
|
||||
@@ -414,7 +425,7 @@ fn test_panic() -> Result<()> {
|
||||
|
||||
// Test returning Rust panic (must be resumed)
|
||||
{
|
||||
let lua = make_lua()?;
|
||||
let lua = make_lua(LuaOptions::default())?;
|
||||
match catch_unwind(AssertUnwindSafe(|| -> Result<()> {
|
||||
let _catched_panic = lua
|
||||
.load(
|
||||
@@ -442,9 +453,9 @@ fn test_panic() -> Result<()> {
|
||||
}
|
||||
}
|
||||
|
||||
// Test representing rust panic as a string
|
||||
// Test representing Rust panic as a string
|
||||
match catch_unwind(|| -> Result<()> {
|
||||
let lua = make_lua()?;
|
||||
let lua = make_lua(LuaOptions::default())?;
|
||||
lua.load(
|
||||
r#"
|
||||
local _, err = pcall(rust_panic_function)
|
||||
@@ -459,6 +470,48 @@ fn test_panic() -> Result<()> {
|
||||
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(())
|
||||
}
|
||||
|
||||
@@ -561,7 +614,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#"
|
||||
@@ -747,6 +800,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();
|
||||
@@ -778,7 +848,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());
|
||||
@@ -786,7 +875,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);
|
||||
@@ -800,23 +889,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(
|
||||
@@ -837,7 +923,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();
|
||||
|
||||
@@ -872,7 +976,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>| {
|
||||
@@ -888,7 +992,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")?;
|
||||
@@ -930,7 +1034,7 @@ fn chunk_env() -> Result<()> {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn context_thread() -> Result<()> {
|
||||
fn test_context_thread() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let f = lua
|
||||
@@ -953,7 +1057,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(
|
||||
@@ -983,3 +1087,30 @@ fn test_jit_version() -> Result<()> {
|
||||
.contains("LuaJIT"));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_load_from_function() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let i = Arc::new(AtomicU32::new(0));
|
||||
let i2 = i.clone();
|
||||
let func = lua.create_function(move |lua, modname: String| {
|
||||
i2.fetch_add(1, Ordering::Relaxed);
|
||||
let t = lua.create_table()?;
|
||||
t.set("__name", modname)?;
|
||||
Ok(t)
|
||||
})?;
|
||||
|
||||
let t: Table = lua.load_from_function("my_module", func.clone())?;
|
||||
assert_eq!(t.get::<_, String>("__name")?, "my_module");
|
||||
assert_eq!(i.load(Ordering::Relaxed), 1);
|
||||
|
||||
let _: Value = lua.load_from_function("my_module", func)?;
|
||||
assert_eq!(i.load(Ordering::Relaxed), 1);
|
||||
|
||||
let func_nil = lua.create_function(move |_, _: String| Ok(Value::Nil))?;
|
||||
let v: Value = lua.load_from_function("my_module2", func_nil)?;
|
||||
assert_eq!(v, Value::Boolean(true));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
+45
-2
@@ -94,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(()))?;
|
||||
@@ -118,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();
|
||||
|
||||
+204
-10
@@ -1,11 +1,14 @@
|
||||
use std::sync::Arc;
|
||||
use std::sync::{Arc, Mutex, RwLock};
|
||||
|
||||
#[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]
|
||||
@@ -155,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(())
|
||||
}
|
||||
@@ -250,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 {}
|
||||
@@ -272,7 +275,7 @@ fn detroys_userdata() -> Result<()> {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn user_value() -> Result<()> {
|
||||
fn test_user_value() -> Result<()> {
|
||||
struct MyUserData;
|
||||
|
||||
impl UserData for MyUserData {}
|
||||
@@ -295,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(())
|
||||
});
|
||||
@@ -335,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(())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user