mirror of
https://github.com/mlua-rs/mlua
synced 2026-06-08 16:05:43 +00:00
Compare commits
154 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 | |||
| cb1ac28f2a | |||
| 3e7f25670a | |||
| 0d404ce4c3 | |||
| e26cec5db9 | |||
| 0bd36b42e7 | |||
| e0da6ac929 | |||
| 0c7db4916c | |||
| b9589491e4 | |||
| 58cb371f06 | |||
| 8add60b019 | |||
| c363fb9288 | |||
| 3900e23839 | |||
| 726fde7e1f | |||
| 7cb9c4f39c | |||
| b93ace0224 | |||
| 5f37bf812d | |||
| 1f7e760d20 | |||
| 90bea4aa34 | |||
| 7775b4a99c | |||
| 1d9cda10eb | |||
| 7332c6a28c | |||
| 94670e3fdb | |||
| 335f433df4 | |||
| 2aed548747 | |||
| 6a77b5f003 |
@@ -0,0 +1 @@
|
|||||||
|
github: khvzak
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
name: coverage
|
||||||
|
on: [push]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
test:
|
||||||
|
name: coverage
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
container:
|
||||||
|
image: xd009642/tarpaulin
|
||||||
|
options: --security-opt seccomp=unconfined
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- name: Generate code coverage
|
||||||
|
run: |
|
||||||
|
cargo tarpaulin --verbose --features lua53,vendored,async,send,serialize,macros --out xml --exclude-files benches --exclude-files build --exclude-files mlua_derive --exclude-files src/ffi --exclude-files tests
|
||||||
|
|
||||||
|
- name: Upload to codecov.io
|
||||||
|
uses: codecov/codecov-action@v1
|
||||||
|
with:
|
||||||
|
token: ${{secrets.CODECOV_TOKEN}}
|
||||||
|
fail_ci_if_error: false
|
||||||
+38
-31
@@ -24,17 +24,18 @@ jobs:
|
|||||||
toolchain: ${{ matrix.rust }}
|
toolchain: ${{ matrix.rust }}
|
||||||
target: ${{ matrix.target }}
|
target: ${{ matrix.target }}
|
||||||
override: true
|
override: true
|
||||||
|
- uses: Swatinem/rust-cache@v1
|
||||||
- name: Build ${{ matrix.lua }} vendored
|
- name: Build ${{ matrix.lua }} vendored
|
||||||
run: |
|
run: |
|
||||||
cargo build --release --features "${{ matrix.lua }} vendored"
|
cargo build --features "${{ matrix.lua }},vendored"
|
||||||
cargo build --release --features "${{ matrix.lua }} vendored async send serialize"
|
cargo build --features "${{ matrix.lua }},vendored,async,send,serialize,macros"
|
||||||
shell: bash
|
shell: bash
|
||||||
- name: Build ${{ matrix.lua }} pkg-config
|
- name: Build ${{ matrix.lua }} pkg-config
|
||||||
if: ${{ matrix.os == 'ubuntu-18.04' && matrix.lua != 'lua54' }}
|
if: ${{ matrix.os == 'ubuntu-18.04' && matrix.lua != 'lua54' }}
|
||||||
run: |
|
run: |
|
||||||
sudo apt-get update
|
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
|
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:
|
build_aarch64_cross_macos:
|
||||||
name: Cross-compile to aarch64-apple-darwin
|
name: Cross-compile to aarch64-apple-darwin
|
||||||
@@ -47,11 +48,11 @@ jobs:
|
|||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
- uses: actions-rs/toolchain@v1
|
- uses: actions-rs/toolchain@v1
|
||||||
with:
|
with:
|
||||||
toolchain: nightly
|
toolchain: stable
|
||||||
target: aarch64-apple-darwin
|
target: aarch64-apple-darwin
|
||||||
override: true
|
override: true
|
||||||
- name: Cross-compile
|
- 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:
|
build_aarch64_cross_ubuntu:
|
||||||
name: Cross-compile to aarch64-unknown-linux-gnu
|
name: Cross-compile to aarch64-unknown-linux-gnu
|
||||||
@@ -64,7 +65,7 @@ jobs:
|
|||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
- uses: actions-rs/toolchain@v1
|
- uses: actions-rs/toolchain@v1
|
||||||
with:
|
with:
|
||||||
toolchain: nightly
|
toolchain: stable
|
||||||
target: aarch64-unknown-linux-gnu
|
target: aarch64-unknown-linux-gnu
|
||||||
override: true
|
override: true
|
||||||
- name: Install ARM compiler toolchain
|
- 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
|
sudo apt-get install -y --no-install-recommends gcc-aarch64-linux-gnu libc6-dev-arm64-cross
|
||||||
shell: bash
|
shell: bash
|
||||||
- name: Cross-compile
|
- 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
|
shell: bash
|
||||||
|
|
||||||
build_armv7_cross_ubuntu:
|
build_armv7_cross_ubuntu:
|
||||||
@@ -87,7 +88,7 @@ jobs:
|
|||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
- uses: actions-rs/toolchain@v1
|
- uses: actions-rs/toolchain@v1
|
||||||
with:
|
with:
|
||||||
toolchain: nightly
|
toolchain: stable
|
||||||
target: armv7-unknown-linux-gnueabihf
|
target: armv7-unknown-linux-gnueabihf
|
||||||
override: true
|
override: true
|
||||||
- name: Install ARM compiler toolchain
|
- 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
|
sudo apt-get install -y --no-install-recommends gcc-arm-linux-gnueabihf libc-dev-armhf-cross
|
||||||
shell: bash
|
shell: bash
|
||||||
- name: Cross-compile
|
- 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
|
shell: bash
|
||||||
|
|
||||||
test:
|
test:
|
||||||
@@ -106,7 +107,7 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
os: [ubuntu-18.04, macos-latest, windows-latest]
|
os: [ubuntu-18.04, macos-latest, windows-latest]
|
||||||
rust: [stable]
|
rust: [stable, nightly]
|
||||||
lua: [lua54, lua53, lua52, lua51, luajit]
|
lua: [lua54, lua53, lua52, lua51, luajit]
|
||||||
include:
|
include:
|
||||||
- os: ubuntu-18.04
|
- os: ubuntu-18.04
|
||||||
@@ -122,38 +123,43 @@ jobs:
|
|||||||
toolchain: ${{ matrix.rust }}
|
toolchain: ${{ matrix.rust }}
|
||||||
target: ${{ matrix.target }}
|
target: ${{ matrix.target }}
|
||||||
override: true
|
override: true
|
||||||
|
- uses: Swatinem/rust-cache@v1
|
||||||
- name: Run ${{ matrix.lua }} tests
|
- name: Run ${{ matrix.lua }} tests
|
||||||
if: ${{ matrix.os != 'macos-latest' || matrix.lua != 'luajit' }}
|
|
||||||
run: |
|
run: |
|
||||||
cargo test --release --features "${{ matrix.lua }} vendored"
|
cargo test --features "${{ matrix.lua }},vendored"
|
||||||
cargo test --release --features "${{ matrix.lua }} vendored async send serialize"
|
cargo test --features "${{ matrix.lua }},vendored,async,send,serialize,macros"
|
||||||
shell: bash
|
shell: bash
|
||||||
- name: Run compile tests (macos lua53)
|
- name: Run compile tests (macos lua53)
|
||||||
if: ${{ matrix.os == 'macos-latest' && matrix.lua == 'lua53' }}
|
if: ${{ matrix.os == 'macos-latest' && matrix.lua == 'lua53' }}
|
||||||
run: |
|
run: |
|
||||||
TRYBUILD=overwrite cargo test --release --features "${{ matrix.lua }} vendored" -- --ignored
|
TRYBUILD=overwrite cargo test --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,async,send,serialize,macros" -- --ignored
|
||||||
shell: bash
|
shell: bash
|
||||||
|
|
||||||
test_luajit_macos:
|
test_with_sanitizer:
|
||||||
name: Test LuaJIT on macOS
|
name: Test with address sanitizer
|
||||||
runs-on: macos-latest
|
runs-on: ${{ matrix.os }}
|
||||||
needs: build
|
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:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
- uses: actions-rs/toolchain@v1
|
- uses: actions-rs/toolchain@v1
|
||||||
with:
|
with:
|
||||||
toolchain: nightly
|
toolchain: ${{ matrix.rust }}
|
||||||
target: x86_64-apple-darwin
|
target: ${{ matrix.target }}
|
||||||
override: true
|
override: true
|
||||||
- name: Run LuaJIT 2.0.5 tests
|
- uses: Swatinem/rust-cache@v1
|
||||||
|
- name: Run ${{ matrix.lua }} tests with address sanitizer
|
||||||
run: |
|
run: |
|
||||||
brew install luajit
|
RUSTFLAGS="-Z sanitizer=address" \
|
||||||
cargo test --tests --release --features "luajit async send serialize" -- --test-threads=1
|
cargo test --tests --features "${{ matrix.lua }},vendored,async,send,serialize,macros" --target x86_64-unknown-linux-gnu -- --skip test_too_many_recursions
|
||||||
shell: bash
|
|
||||||
- name: Run LuaJIT vendored tests
|
|
||||||
run: |
|
|
||||||
cargo test --release --features "luajit vendored async send serialize"
|
|
||||||
shell: bash
|
shell: bash
|
||||||
|
|
||||||
test_modules:
|
test_modules:
|
||||||
@@ -177,10 +183,11 @@ jobs:
|
|||||||
toolchain: ${{ matrix.rust }}
|
toolchain: ${{ matrix.rust }}
|
||||||
target: ${{ matrix.target }}
|
target: ${{ matrix.target }}
|
||||||
override: true
|
override: true
|
||||||
|
- uses: Swatinem/rust-cache@v1
|
||||||
- name: Run ${{ matrix.lua }} module tests
|
- name: Run ${{ matrix.lua }} module tests
|
||||||
run: |
|
run: |
|
||||||
(cd examples/module && cargo build --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")
|
(cd tests/module && cargo test --release --features "${{ matrix.lua }},vendored")
|
||||||
shell: bash
|
shell: bash
|
||||||
|
|
||||||
test_modules_windows:
|
test_modules_windows:
|
||||||
@@ -189,7 +196,7 @@ jobs:
|
|||||||
needs: build
|
needs: build
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
lua: [lua53, luajit]
|
lua: [lua54, luajit]
|
||||||
defaults:
|
defaults:
|
||||||
run:
|
run:
|
||||||
shell: msys2 {0}
|
shell: msys2 {0}
|
||||||
@@ -232,4 +239,4 @@ jobs:
|
|||||||
- uses: actions-rs/clippy-check@v1
|
- uses: actions-rs/clippy-check@v1
|
||||||
with:
|
with:
|
||||||
token: ${{ secrets.GITHUB_TOKEN }}
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
args: --features "${{ matrix.lua }},vendored,async,send,serialize
|
args: --features "${{ matrix.lua }},vendored,async,send,serialize,macros"
|
||||||
|
|||||||
@@ -1,3 +1,94 @@
|
|||||||
|
## 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
|
||||||
|
- Improvements in panic handling (resume panic on value popping)
|
||||||
|
- Fixed bug serializing 3rd party userdata (causes segfault)
|
||||||
|
- Make error::Error non exhaustive
|
||||||
|
|
||||||
|
## v0.5.3
|
||||||
|
|
||||||
|
- Fixed bug when returning nil-prefixed multi values from async function (+ test)
|
||||||
|
- Performance optimisation for async callbacks (polling)
|
||||||
|
|
||||||
|
## v0.5.2
|
||||||
|
|
||||||
|
- Some performance optimisations (callbacks)
|
||||||
|
- `ToLua` implementation for `Cow<str>` and `Cow<CStr>`
|
||||||
|
- Fixed bug with `Scope` destruction of partially polled futures
|
||||||
|
|
||||||
## v0.5.1
|
## v0.5.1
|
||||||
|
|
||||||
- Support cross compilation that should work well for vendored builds (including LuaJIT with some restrictions)
|
- Support cross compilation that should work well for vendored builds (including LuaJIT with some restrictions)
|
||||||
|
|||||||
+23
-12
@@ -1,12 +1,12 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "mlua"
|
name = "mlua"
|
||||||
version = "0.5.1" # 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>"]
|
authors = ["Aleksandr Orlenko <zxteam@pm.me>", "kyren <catherine@chucklefish.org>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
repository = "https://github.com/khvzak/mlua"
|
repository = "https://github.com/khvzak/mlua"
|
||||||
documentation = "https://docs.rs/mlua"
|
documentation = "https://docs.rs/mlua"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
keywords = ["lua", "luajit", "async", "futures"]
|
keywords = ["lua", "luajit", "async", "futures", "scripting"]
|
||||||
categories = ["api-bindings", "asynchronous"]
|
categories = ["api-bindings", "asynchronous"]
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
links = "lua"
|
links = "lua"
|
||||||
@@ -17,7 +17,7 @@ with async/await features and support of writing native lua modules in Rust.
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
[package.metadata.docs.rs]
|
[package.metadata.docs.rs]
|
||||||
features = ["lua53", "async", "send", "serialize"]
|
features = ["lua53", "async", "send", "serialize", "macros"]
|
||||||
rustdoc-args = ["--cfg", "docsrs"]
|
rustdoc-args = ["--cfg", "docsrs"]
|
||||||
|
|
||||||
[workspace]
|
[workspace]
|
||||||
@@ -38,11 +38,12 @@ module = ["mlua_derive"]
|
|||||||
async = ["futures-core", "futures-task", "futures-util"]
|
async = ["futures-core", "futures-task", "futures-util"]
|
||||||
send = []
|
send = []
|
||||||
serialize = ["serde", "erased-serde"]
|
serialize = ["serde", "erased-serde"]
|
||||||
|
macros = ["mlua_derive/macros"]
|
||||||
|
|
||||||
[dependencies]
|
[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 }
|
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" }
|
num-traits = { version = "0.2.14" }
|
||||||
futures-core = { version = "0.3.5", optional = true }
|
futures-core = { version = "0.3.5", optional = true }
|
||||||
futures-task = { version = "0.3.5", optional = true }
|
futures-task = { version = "0.3.5", optional = true }
|
||||||
@@ -57,8 +58,8 @@ lua-src = { version = ">= 540.0.0, < 550.0.0", optional = true }
|
|||||||
luajit-src = { version = ">= 210.1.2, < 220.0.0", optional = true }
|
luajit-src = { version = ">= 210.1.2, < 220.0.0", optional = true }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
rustyline = "7.0"
|
rustyline = "8.0"
|
||||||
criterion = "0.3"
|
criterion = { version = "0.3.4", features = ["html_reports", "async_tokio"] }
|
||||||
trybuild = "1.0"
|
trybuild = "1.0"
|
||||||
futures = "0.3.5"
|
futures = "0.3.5"
|
||||||
hyper = { version = "0.14", features = ["client", "server"] }
|
hyper = { version = "0.14", features = ["client", "server"] }
|
||||||
@@ -66,18 +67,20 @@ reqwest = { version = "0.11", features = ["json"] }
|
|||||||
tokio = { version = "1.0", features = ["full"] }
|
tokio = { version = "1.0", features = ["full"] }
|
||||||
futures-timer = "3.0"
|
futures-timer = "3.0"
|
||||||
serde_json = "1.0"
|
serde_json = "1.0"
|
||||||
|
maplit = "1.0"
|
||||||
|
|
||||||
[[bench]]
|
[[bench]]
|
||||||
name = "benchmark"
|
name = "benchmark"
|
||||||
harness = false
|
harness = false
|
||||||
|
|
||||||
[[example]]
|
|
||||||
name = "async_http_client"
|
|
||||||
required-features = ["async"]
|
required-features = ["async"]
|
||||||
|
|
||||||
|
[[example]]
|
||||||
|
name = "async_http_client"
|
||||||
|
required-features = ["async", "macros"]
|
||||||
|
|
||||||
[[example]]
|
[[example]]
|
||||||
name = "async_http_reqwest"
|
name = "async_http_reqwest"
|
||||||
required-features = ["async", "serialize"]
|
required-features = ["async", "serialize", "macros"]
|
||||||
|
|
||||||
[[example]]
|
[[example]]
|
||||||
name = "async_http_server"
|
name = "async_http_server"
|
||||||
@@ -85,8 +88,16 @@ required-features = ["async", "send"]
|
|||||||
|
|
||||||
[[example]]
|
[[example]]
|
||||||
name = "async_tcp_server"
|
name = "async_tcp_server"
|
||||||
required-features = ["async"]
|
required-features = ["async", "macros"]
|
||||||
|
|
||||||
|
[[example]]
|
||||||
|
name = "guided_tour"
|
||||||
|
required-features = ["macros"]
|
||||||
|
|
||||||
[[example]]
|
[[example]]
|
||||||
name = "serialize"
|
name = "serialize"
|
||||||
required-features = ["serialize"]
|
required-features = ["serialize"]
|
||||||
|
|
||||||
|
[[example]]
|
||||||
|
name = "userdata"
|
||||||
|
required-features = ["macros"]
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ below:
|
|||||||
|
|
||||||
MIT License
|
MIT License
|
||||||
|
|
||||||
Copyright (c) 2019 A. Orlenko
|
Copyright (c) 2019-2021 A. Orlenko
|
||||||
Copyright (c) 2017 rlua
|
Copyright (c) 2017 rlua
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# mlua
|
# mlua
|
||||||
[![Build Status]][github-actions] [![Latest Version]][crates.io] [![API Documentation]][docs.rs]
|
[![Build Status]][github-actions] [![Latest Version]][crates.io] [![API Documentation]][docs.rs] [![Coverage Status]][codecov.io]
|
||||||
|
|
||||||
[Build Status]: https://github.com/khvzak/mlua/workflows/CI/badge.svg
|
[Build Status]: https://github.com/khvzak/mlua/workflows/CI/badge.svg
|
||||||
[github-actions]: https://github.com/khvzak/mlua/actions
|
[github-actions]: https://github.com/khvzak/mlua/actions
|
||||||
@@ -7,29 +7,54 @@
|
|||||||
[crates.io]: https://crates.io/crates/mlua
|
[crates.io]: https://crates.io/crates/mlua
|
||||||
[API Documentation]: https://docs.rs/mlua/badge.svg
|
[API Documentation]: https://docs.rs/mlua/badge.svg
|
||||||
[docs.rs]: https://docs.rs/mlua
|
[docs.rs]: https://docs.rs/mlua
|
||||||
|
[Coverage Status]: https://codecov.io/gh/khvzak/mlua/branch/master/graph/badge.svg?token=99339FS1CG
|
||||||
|
[codecov.io]: https://codecov.io/gh/khvzak/mlua
|
||||||
|
|
||||||
[Guided Tour](examples/guided_tour.rs)
|
[Guided Tour](examples/guided_tour.rs)
|
||||||
|
|
||||||
`mlua` is bindings to [Lua](https://www.lua.org) programming language for Rust with a goal to provide
|
`mlua` is bindings to [Lua](https://www.lua.org) programming language for Rust with a goal to provide
|
||||||
_safe_ (as far as it's possible), high level, easy to use, practical and flexible API.
|
_safe_ (as far as it's possible), high level, easy to use, practical and flexible API.
|
||||||
|
|
||||||
Started as [rlua v0.15](https://github.com/amethyst/rlua/tree/0.15.3) fork, `mlua` supports *__all__* major Lua versions (including LuaJIT) and allows to write native Lua modules in Rust as well as use Lua in a standalone mode.
|
Started as [rlua](https://github.com/amethyst/rlua/tree/0.15.3) fork, `mlua` supports Lua 5.4, 5.3, 5.2 and 5.1 including LuaJIT (2.0.5 and 2.1 beta) and allows to write native Lua modules in Rust as well as use Lua in a standalone mode.
|
||||||
|
|
||||||
`mlua` supports the following Lua versions (and tested on Windows/macOS/Linux):
|
`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).
|
||||||
- Lua 5.4 (`feature = "lua54"`)
|
|
||||||
- Lua 5.3 (`feature = "lua53"`)
|
|
||||||
- Lua 5.2 (`feature = "lua52"`)
|
|
||||||
- Lua 5.1 (`feature = "lua51"`)
|
|
||||||
- LuaJIT 2.1.0 beta (`feature = "luajit"`)
|
|
||||||
- LuaJIT 2.0.5 stable (`feature = "luajit"`)
|
|
||||||
|
|
||||||
Additional `feature = "vendored"` enables building static Lua from sources during `mlua` compilation.
|
[GitHub Actions]: https://github.com/khvzak/mlua/actions
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
|
### Feature flags
|
||||||
|
|
||||||
|
`mlua` uses feature flags to reduce the amount of dependencies, compiled code and allow to choose only required set of features.
|
||||||
|
Below is a list of the available feature flags. By default `mlua` does not enable any features.
|
||||||
|
|
||||||
|
* `lua54`: activate Lua [5.4] support
|
||||||
|
* `lua53`: activate Lua [5.3] support
|
||||||
|
* `lua52`: activate Lua [5.2] support
|
||||||
|
* `lua51`: activate Lua [5.1] support
|
||||||
|
* `luajit`: activate [LuaJIT] support
|
||||||
|
* `vendored`: build static Lua(JIT) library from sources during `mlua` compilation using [lua-src] or [luajit-src] crates
|
||||||
|
* `module`: enable module mode (building loadable `cdylib` library for Lua)
|
||||||
|
* `async`: enable async/await support (any executor can be used, eg. [tokio] or [async-std])
|
||||||
|
* `send`: make `mlua::Lua` transferable across thread boundaries (adds [`Send`] requirement to `mlua::Function` and `mlua::UserData`)
|
||||||
|
* `serialize`: add serialization and deserialization support to `mlua` types using [serde] framework
|
||||||
|
* `macros`: enable procedural macros (such as `chunk!`)
|
||||||
|
|
||||||
|
[5.4]: https://www.lua.org/manual/5.4/manual.html
|
||||||
|
[5.3]: https://www.lua.org/manual/5.3/manual.html
|
||||||
|
[5.2]: https://www.lua.org/manual/5.2/manual.html
|
||||||
|
[5.1]: https://www.lua.org/manual/5.1/manual.html
|
||||||
|
[LuaJIT]: https://luajit.org/
|
||||||
|
[lua-src]: https://github.com/khvzak/lua-src-rs
|
||||||
|
[luajit-src]: https://github.com/khvzak/luajit-src-rs
|
||||||
|
[tokio]: https://github.com/tokio-rs/tokio
|
||||||
|
[async-std]: https://github.com/async-rs/async-std
|
||||||
|
[`Send`]: https://doc.rust-lang.org/std/marker/trait.Send.html
|
||||||
|
[serde]: https://github.com/serde-rs/serde
|
||||||
|
|
||||||
### Async/await support
|
### Async/await support
|
||||||
|
|
||||||
Starting from v0.3, `mlua` supports async/await for all Lua versions. This works using Lua [coroutines](https://www.lua.org/manual/5.3/manual.html#2.6) and require running [Thread](https://docs.rs/mlua/latest/mlua/struct.Thread.html) along with enabling `feature = "async"` in `Cargo.toml`.
|
`mlua` supports async/await for all Lua versions. This works using Lua [coroutines](https://www.lua.org/manual/5.3/manual.html#2.6) and require running [Thread](https://docs.rs/mlua/latest/mlua/struct.Thread.html) along with enabling `feature = "async"` in `Cargo.toml`.
|
||||||
|
|
||||||
**Examples**:
|
**Examples**:
|
||||||
- [HTTP Client](examples/async_http_client.rs)
|
- [HTTP Client](examples/async_http_client.rs)
|
||||||
@@ -39,7 +64,7 @@ Starting from v0.3, `mlua` supports async/await for all Lua versions. This works
|
|||||||
|
|
||||||
### Serialization (serde) support
|
### Serialization (serde) support
|
||||||
|
|
||||||
With `serialize` feature flag enabled, `mlua` allows you to serialize/deserialize any type that implements [`serde::Serialize`] and [`serde::Deserialize`] into/from [`mlua::Value`]. In addition `mlua` provides [`serde::Serialize`] trait implementation for it (including user data support).
|
With `serialize` feature flag enabled, `mlua` allows you to serialize/deserialize any type that implements [`serde::Serialize`] and [`serde::Deserialize`] into/from [`mlua::Value`]. In addition `mlua` provides [`serde::Serialize`] trait implementation for it (including `UserData` support).
|
||||||
|
|
||||||
[Example](examples/serialize.rs)
|
[Example](examples/serialize.rs)
|
||||||
|
|
||||||
@@ -49,7 +74,7 @@ With `serialize` feature flag enabled, `mlua` allows you to serialize/deserializ
|
|||||||
|
|
||||||
### Compiling
|
### 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.
|
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.
|
In most cases it works as desired, although sometimes could be more preferable to use a custom lua library.
|
||||||
@@ -61,16 +86,18 @@ An example how to use them:
|
|||||||
my_project $ LUA_INC=$HOME/tmp/lua-5.2.4/src LUA_LIB=$HOME/tmp/lua-5.2.4/src LUA_LIB_NAME=lua LUA_LINK=static cargo build
|
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).
|
[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`.
|
Just enable the `vendored` feature and cargo will automatically build and link specified lua/luajit version. This is the easiest way to get started with `mlua`.
|
||||||
|
|
||||||
### Standalone mode
|
### Standalone mode
|
||||||
|
In a standalone mode `mlua` allows to add to your application scripting support with a gently configured Lua runtime to ensure safety and soundness.
|
||||||
|
|
||||||
Add to `Cargo.toml` :
|
Add to `Cargo.toml` :
|
||||||
|
|
||||||
``` toml
|
``` toml
|
||||||
[dependencies]
|
[dependencies]
|
||||||
mlua = { version = "0.5", features = ["lua53"] }
|
mlua = { version = "0.6", features = ["lua53", "vendored"] }
|
||||||
```
|
```
|
||||||
|
|
||||||
`main.rs`
|
`main.rs`
|
||||||
@@ -94,6 +121,7 @@ fn main() -> LuaResult<()> {
|
|||||||
```
|
```
|
||||||
|
|
||||||
### Module mode
|
### Module mode
|
||||||
|
In a module mode `mlua` allows to create a compiled Lua module that can be loaded from Lua code using [`require`](https://www.lua.org/manual/5.3/manual.html#pdf-require). In this case `mlua` uses an external Lua runtime which could lead to potential unsafety due to unpredictability of the Lua environment and usage of libraries such as [`debug`](https://www.lua.org/manual/5.3/manual.html#6.10).
|
||||||
|
|
||||||
[Example](examples/module)
|
[Example](examples/module)
|
||||||
|
|
||||||
@@ -104,7 +132,7 @@ Add to `Cargo.toml` :
|
|||||||
crate-type = ["cdylib"]
|
crate-type = ["cdylib"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
mlua = { version = "0.5", features = ["lua53", "module"] }
|
mlua = { version = "0.6", features = ["lua53", "vendored", "module"] }
|
||||||
```
|
```
|
||||||
|
|
||||||
`lib.rs` :
|
`lib.rs` :
|
||||||
@@ -157,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.
|
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
|
## Safety
|
||||||
|
|
||||||
One of the `mlua` goals is to provide *safe* API between Rust and Lua.
|
One of the `mlua` goals is to provide *safe* API between Rust and Lua.
|
||||||
@@ -171,7 +208,7 @@ It is surprisingly, fiendishly difficult to use the Lua C API without the potent
|
|||||||
## Panic handling
|
## Panic handling
|
||||||
|
|
||||||
`mlua` wraps panics that are generated inside Rust callbacks in a regular Lua error. Panics could be
|
`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:
|
For example:
|
||||||
``` rust
|
``` rust
|
||||||
@@ -190,6 +227,10 @@ let _ = lua.load(r#"
|
|||||||
unreachable!()
|
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
|
`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
|
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
|
leak Lua stack space. This is mostly important to safely use `mlua` types in Drop impls, as you should not be
|
||||||
@@ -198,15 +239,14 @@ using panics for general error handling.
|
|||||||
Below is a list of `mlua` behaviors that should be considered a bug.
|
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 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.
|
+ 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
|
## License
|
||||||
|
|
||||||
This project is licensed under the [MIT license](LICENSE)
|
This project is licensed under the [MIT license](LICENSE)
|
||||||
|
|
||||||
|
|||||||
+191
-114
@@ -1,23 +1,22 @@
|
|||||||
#![cfg_attr(
|
|
||||||
all(feature = "luajit", target_os = "macos", target_arch = "x86_64"),
|
|
||||||
feature(link_args)
|
|
||||||
)]
|
|
||||||
|
|
||||||
#[cfg_attr(
|
|
||||||
all(feature = "luajit", target_os = "macos", target_arch = "x86_64"),
|
|
||||||
link_args = "-pagezero_size 10000 -image_base 100000000",
|
|
||||||
allow(unused_attributes)
|
|
||||||
)]
|
|
||||||
extern "system" {}
|
|
||||||
|
|
||||||
use criterion::{criterion_group, criterion_main, BatchSize, Criterion};
|
use criterion::{criterion_group, criterion_main, BatchSize, Criterion};
|
||||||
|
use std::time::Duration;
|
||||||
|
use tokio::runtime::Runtime;
|
||||||
|
use tokio::task;
|
||||||
|
|
||||||
use mlua::prelude::*;
|
use mlua::prelude::*;
|
||||||
|
|
||||||
|
fn collect_gc_twice(lua: &Lua) {
|
||||||
|
lua.gc_collect().unwrap();
|
||||||
|
lua.gc_collect().unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
fn create_table(c: &mut Criterion) {
|
fn create_table(c: &mut Criterion) {
|
||||||
c.bench_function("create table", |b| {
|
let lua = Lua::new();
|
||||||
b.iter_batched_ref(
|
|
||||||
|| Lua::new(),
|
c.bench_function("create [table empty]", |b| {
|
||||||
|lua| {
|
b.iter_batched(
|
||||||
|
|| collect_gc_twice(&lua),
|
||||||
|
|_| {
|
||||||
lua.create_table().unwrap();
|
lua.create_table().unwrap();
|
||||||
},
|
},
|
||||||
BatchSize::SmallInput,
|
BatchSize::SmallInput,
|
||||||
@@ -26,12 +25,14 @@ fn create_table(c: &mut Criterion) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn create_array(c: &mut Criterion) {
|
fn create_array(c: &mut Criterion) {
|
||||||
c.bench_function("create array 10", |b| {
|
let lua = Lua::new();
|
||||||
b.iter_batched_ref(
|
|
||||||
|| Lua::new(),
|
c.bench_function("create [array] 10", |b| {
|
||||||
|lua| {
|
b.iter_batched(
|
||||||
|
|| collect_gc_twice(&lua),
|
||||||
|
|_| {
|
||||||
let table = lua.create_table().unwrap();
|
let table = lua.create_table().unwrap();
|
||||||
for i in 1..11 {
|
for i in 1..=10 {
|
||||||
table.set(i, i).unwrap();
|
table.set(i, i).unwrap();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -41,10 +42,12 @@ fn create_array(c: &mut Criterion) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn create_string_table(c: &mut Criterion) {
|
fn create_string_table(c: &mut Criterion) {
|
||||||
c.bench_function("create string table 10", |b| {
|
let lua = Lua::new();
|
||||||
b.iter_batched_ref(
|
|
||||||
|| Lua::new(),
|
c.bench_function("create [table string] 10", |b| {
|
||||||
|lua| {
|
b.iter_batched(
|
||||||
|
|| collect_gc_twice(&lua),
|
||||||
|
|_| {
|
||||||
let table = lua.create_table().unwrap();
|
let table = lua.create_table().unwrap();
|
||||||
for &s in &["1", "2", "3", "4", "5", "6", "7", "8", "9", "10"] {
|
for &s in &["1", "2", "3", "4", "5", "6", "7", "8", "9", "10"] {
|
||||||
let s = lua.create_string(s).unwrap();
|
let s = lua.create_string(s).unwrap();
|
||||||
@@ -56,30 +59,15 @@ fn create_string_table(c: &mut Criterion) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn call_add_function(c: &mut Criterion) {
|
fn create_function(c: &mut Criterion) {
|
||||||
c.bench_function("call add function 3 10", |b| {
|
let lua = Lua::new();
|
||||||
b.iter_batched_ref(
|
|
||||||
|| {
|
c.bench_function("create [function] 10", |b| {
|
||||||
let lua = Lua::new();
|
b.iter_batched(
|
||||||
let f = {
|
|| collect_gc_twice(&lua),
|
||||||
let f: LuaFunction = lua
|
|_| {
|
||||||
.load(
|
|
||||||
r#"
|
|
||||||
function(a, b, c)
|
|
||||||
return a + b + c
|
|
||||||
end
|
|
||||||
"#,
|
|
||||||
)
|
|
||||||
.eval()
|
|
||||||
.unwrap();
|
|
||||||
lua.create_registry_value(f).unwrap()
|
|
||||||
};
|
|
||||||
(lua, f)
|
|
||||||
},
|
|
||||||
|(lua, f)| {
|
|
||||||
let add_function: LuaFunction = lua.registry_value(f).unwrap();
|
|
||||||
for i in 0..10 {
|
for i in 0..10 {
|
||||||
let _result: i64 = add_function.call((i, i + 1, i + 2)).unwrap();
|
lua.create_function(move |_, ()| Ok(i)).unwrap();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
BatchSize::SmallInput,
|
BatchSize::SmallInput,
|
||||||
@@ -87,72 +75,96 @@ fn call_add_function(c: &mut Criterion) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn call_add_callback(c: &mut Criterion) {
|
fn call_lua_function(c: &mut Criterion) {
|
||||||
c.bench_function("call callback add 2 10", |b| {
|
let lua = Lua::new();
|
||||||
|
|
||||||
|
c.bench_function("call Lua function [sum] 3 10", |b| {
|
||||||
b.iter_batched_ref(
|
b.iter_batched_ref(
|
||||||
|| {
|
|| {
|
||||||
let lua = Lua::new();
|
collect_gc_twice(&lua);
|
||||||
let f = {
|
lua.load("function(a, b, c) return a + b + c end")
|
||||||
let c: LuaFunction = lua
|
.eval::<LuaFunction>()
|
||||||
.create_function(|_, (a, b, c): (i64, i64, i64)| Ok(a + b + c))
|
.unwrap()
|
||||||
.unwrap();
|
|
||||||
lua.globals().set("callback", c).unwrap();
|
|
||||||
let f: LuaFunction = lua
|
|
||||||
.load(
|
|
||||||
r#"
|
|
||||||
function()
|
|
||||||
for i = 1,10 do
|
|
||||||
callback(i, i, i)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
"#,
|
|
||||||
)
|
|
||||||
.eval()
|
|
||||||
.unwrap();
|
|
||||||
lua.create_registry_value(f).unwrap()
|
|
||||||
};
|
|
||||||
(lua, f)
|
|
||||||
},
|
},
|
||||||
|(lua, f)| {
|
|function| {
|
||||||
let entry_function: LuaFunction = lua.registry_value(f).unwrap();
|
for i in 0..10 {
|
||||||
entry_function.call::<_, ()>(()).unwrap();
|
let _result: i64 = function.call((i, i + 1, i + 2)).unwrap();
|
||||||
|
}
|
||||||
},
|
},
|
||||||
BatchSize::SmallInput,
|
BatchSize::SmallInput,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn call_append_callback(c: &mut Criterion) {
|
fn call_sum_callback(c: &mut Criterion) {
|
||||||
c.bench_function("call callback append 10", |b| {
|
let lua = Lua::new();
|
||||||
|
let callback = lua
|
||||||
|
.create_function(|_, (a, b, c): (i64, i64, i64)| Ok(a + b + c))
|
||||||
|
.unwrap();
|
||||||
|
lua.globals().set("callback", callback).unwrap();
|
||||||
|
|
||||||
|
c.bench_function("call Rust callback [sum] 3 10", |b| {
|
||||||
b.iter_batched_ref(
|
b.iter_batched_ref(
|
||||||
|| {
|
|| {
|
||||||
let lua = Lua::new();
|
collect_gc_twice(&lua);
|
||||||
let f = {
|
lua.load("function() for i = 1,10 do callback(i, i+1, i+2) end end")
|
||||||
let c: LuaFunction = lua
|
.eval::<LuaFunction>()
|
||||||
.create_function(|_, (a, b): (LuaString, LuaString)| {
|
.unwrap()
|
||||||
Ok(format!("{}{}", a.to_str()?, b.to_str()?))
|
|
||||||
})
|
|
||||||
.unwrap();
|
|
||||||
lua.globals().set("callback", c).unwrap();
|
|
||||||
let f: LuaFunction = lua
|
|
||||||
.load(
|
|
||||||
r#"
|
|
||||||
function()
|
|
||||||
for _ = 1,10 do
|
|
||||||
callback("a", "b")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
"#,
|
|
||||||
)
|
|
||||||
.eval()
|
|
||||||
.unwrap();
|
|
||||||
lua.create_registry_value(f).unwrap()
|
|
||||||
};
|
|
||||||
(lua, f)
|
|
||||||
},
|
},
|
||||||
|(lua, f)| {
|
|function| {
|
||||||
let entry_function: LuaFunction = lua.registry_value(f).unwrap();
|
function.call::<_, ()>(()).unwrap();
|
||||||
entry_function.call::<_, ()>(()).unwrap();
|
},
|
||||||
|
BatchSize::SmallInput,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn call_async_sum_callback(c: &mut Criterion) {
|
||||||
|
let lua = Lua::new();
|
||||||
|
let callback = lua
|
||||||
|
.create_async_function(|_, (a, b, c): (i64, i64, i64)| async move {
|
||||||
|
task::yield_now().await;
|
||||||
|
Ok(a + b + c)
|
||||||
|
})
|
||||||
|
.unwrap();
|
||||||
|
lua.globals().set("callback", callback).unwrap();
|
||||||
|
|
||||||
|
c.bench_function("call async Rust callback [sum] 3 10", |b| {
|
||||||
|
let rt = Runtime::new().unwrap();
|
||||||
|
b.to_async(rt).iter_batched(
|
||||||
|
|| {
|
||||||
|
collect_gc_twice(&lua);
|
||||||
|
lua.load("function() for i = 1,10 do callback(i, i+1, i+2) end end")
|
||||||
|
.eval::<LuaFunction>()
|
||||||
|
.unwrap()
|
||||||
|
},
|
||||||
|
|function| async move {
|
||||||
|
function.call_async::<_, ()>(()).await.unwrap();
|
||||||
|
},
|
||||||
|
BatchSize::SmallInput,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn call_concat_callback(c: &mut Criterion) {
|
||||||
|
let lua = Lua::new();
|
||||||
|
let callback = lua
|
||||||
|
.create_function(|_, (a, b): (LuaString, LuaString)| {
|
||||||
|
Ok(format!("{}{}", a.to_str()?, b.to_str()?))
|
||||||
|
})
|
||||||
|
.unwrap();
|
||||||
|
lua.globals().set("callback", callback).unwrap();
|
||||||
|
|
||||||
|
c.bench_function("call Rust callback [concat string] 10", |b| {
|
||||||
|
b.iter_batched_ref(
|
||||||
|
|| {
|
||||||
|
collect_gc_twice(&lua);
|
||||||
|
lua.load("function() for i = 1,10 do callback('a', tostring(i)) end end")
|
||||||
|
.eval::<LuaFunction>()
|
||||||
|
.unwrap()
|
||||||
|
},
|
||||||
|
|function| {
|
||||||
|
function.call::<_, ()>(()).unwrap();
|
||||||
},
|
},
|
||||||
BatchSize::SmallInput,
|
BatchSize::SmallInput,
|
||||||
);
|
);
|
||||||
@@ -160,10 +172,12 @@ fn call_append_callback(c: &mut Criterion) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn create_registry_values(c: &mut Criterion) {
|
fn create_registry_values(c: &mut Criterion) {
|
||||||
c.bench_function("create registry 10", |b| {
|
let lua = Lua::new();
|
||||||
b.iter_batched_ref(
|
|
||||||
|| Lua::new(),
|
c.bench_function("create [registry value] 10", |b| {
|
||||||
|lua| {
|
b.iter_batched(
|
||||||
|
|| collect_gc_twice(&lua),
|
||||||
|
|_| {
|
||||||
for _ in 0..10 {
|
for _ in 0..10 {
|
||||||
lua.create_registry_value(lua.pack(true).unwrap()).unwrap();
|
lua.create_registry_value(lua.pack(true).unwrap()).unwrap();
|
||||||
}
|
}
|
||||||
@@ -178,10 +192,12 @@ fn create_userdata(c: &mut Criterion) {
|
|||||||
struct UserData(i64);
|
struct UserData(i64);
|
||||||
impl LuaUserData for UserData {}
|
impl LuaUserData for UserData {}
|
||||||
|
|
||||||
c.bench_function("create userdata 10", |b| {
|
let lua = Lua::new();
|
||||||
b.iter_batched_ref(
|
|
||||||
|| Lua::new(),
|
c.bench_function("create [table userdata] 10", |b| {
|
||||||
|lua| {
|
b.iter_batched(
|
||||||
|
|| collect_gc_twice(&lua),
|
||||||
|
|_| {
|
||||||
let table: LuaTable = lua.create_table().unwrap();
|
let table: LuaTable = lua.create_table().unwrap();
|
||||||
for i in 1..11 {
|
for i in 1..11 {
|
||||||
table.set(i, UserData(i)).unwrap();
|
table.set(i, UserData(i)).unwrap();
|
||||||
@@ -192,20 +208,81 @@ fn create_userdata(c: &mut Criterion) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn call_userdata_method(c: &mut Criterion) {
|
||||||
|
struct UserData(i64);
|
||||||
|
impl LuaUserData for UserData {
|
||||||
|
fn add_methods<'lua, M: LuaUserDataMethods<'lua, Self>>(methods: &mut M) {
|
||||||
|
methods.add_method("method", |_, this, ()| Ok(this.0));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let lua = Lua::new();
|
||||||
|
lua.globals().set("userdata", UserData(10)).unwrap();
|
||||||
|
|
||||||
|
c.bench_function("call [userdata method] 10", |b| {
|
||||||
|
b.iter_batched_ref(
|
||||||
|
|| {
|
||||||
|
collect_gc_twice(&lua);
|
||||||
|
lua.load("function() for i = 1,10 do userdata:method() end end")
|
||||||
|
.eval::<LuaFunction>()
|
||||||
|
.unwrap()
|
||||||
|
},
|
||||||
|
|function| {
|
||||||
|
function.call::<_, ()>(()).unwrap();
|
||||||
|
},
|
||||||
|
BatchSize::SmallInput,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn call_async_userdata_method(c: &mut Criterion) {
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
struct UserData(i64);
|
||||||
|
impl LuaUserData for UserData {
|
||||||
|
fn add_methods<'lua, M: LuaUserDataMethods<'lua, Self>>(methods: &mut M) {
|
||||||
|
methods.add_async_method("method", |_, this, ()| async move { Ok(this.0) });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let lua = Lua::new();
|
||||||
|
lua.globals().set("userdata", UserData(10)).unwrap();
|
||||||
|
|
||||||
|
c.bench_function("call async [userdata method] 10", |b| {
|
||||||
|
let rt = Runtime::new().unwrap();
|
||||||
|
b.to_async(rt).iter_batched(
|
||||||
|
|| {
|
||||||
|
collect_gc_twice(&lua);
|
||||||
|
lua.load("function() for i = 1,10 do userdata:method() end end")
|
||||||
|
.eval::<LuaFunction>()
|
||||||
|
.unwrap()
|
||||||
|
},
|
||||||
|
|function| async move {
|
||||||
|
function.call_async::<_, ()>(()).await.unwrap();
|
||||||
|
},
|
||||||
|
BatchSize::SmallInput,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
criterion_group! {
|
criterion_group! {
|
||||||
name = benches;
|
name = benches;
|
||||||
config = Criterion::default()
|
config = Criterion::default()
|
||||||
.sample_size(200)
|
.sample_size(300)
|
||||||
|
.measurement_time(Duration::from_secs(10))
|
||||||
.noise_threshold(0.02);
|
.noise_threshold(0.02);
|
||||||
targets =
|
targets =
|
||||||
create_table,
|
create_table,
|
||||||
create_array,
|
create_array,
|
||||||
create_string_table,
|
create_string_table,
|
||||||
call_add_function,
|
create_function,
|
||||||
call_add_callback,
|
call_lua_function,
|
||||||
call_append_callback,
|
call_sum_callback,
|
||||||
|
call_async_sum_callback,
|
||||||
|
call_concat_callback,
|
||||||
create_registry_values,
|
create_registry_values,
|
||||||
create_userdata,
|
create_userdata,
|
||||||
|
call_userdata_method,
|
||||||
|
call_async_userdata_method,
|
||||||
}
|
}
|
||||||
|
|
||||||
criterion_main!(benches);
|
criterion_main!(benches);
|
||||||
|
|||||||
+27
-46
@@ -1,13 +1,19 @@
|
|||||||
use std::env;
|
use std::env;
|
||||||
use std::fs::File;
|
|
||||||
use std::io::{BufRead, BufReader, Result};
|
|
||||||
use std::ops::Bound;
|
use std::ops::Bound;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
fn get_env_var(name: &str) -> String {
|
||||||
|
match env::var(name) {
|
||||||
|
Ok(val) => val,
|
||||||
|
Err(env::VarError::NotPresent) => String::new(),
|
||||||
|
Err(err) => panic!("cannot get {}: {}", name, err),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn probe_lua() -> PathBuf {
|
pub fn probe_lua() -> PathBuf {
|
||||||
let include_dir = env::var_os("LUA_INC").unwrap_or_default();
|
let include_dir = get_env_var("LUA_INC");
|
||||||
let lib_dir = env::var_os("LUA_LIB").unwrap_or_default();
|
let lib_dir = get_env_var("LUA_LIB");
|
||||||
let lua_lib = env::var_os("LUA_LIB_NAME").unwrap_or_default();
|
let lua_lib = get_env_var("LUA_LIB_NAME");
|
||||||
|
|
||||||
println!("cargo:rerun-if-env-changed=LUA_INC");
|
println!("cargo:rerun-if-env-changed=LUA_INC");
|
||||||
println!("cargo:rerun-if-env-changed=LUA_LIB");
|
println!("cargo:rerun-if-env-changed=LUA_LIB");
|
||||||
@@ -16,11 +22,22 @@ pub fn probe_lua() -> PathBuf {
|
|||||||
|
|
||||||
let need_lua_lib = cfg!(any(not(feature = "module"), target_os = "windows"));
|
let need_lua_lib = cfg!(any(not(feature = "module"), target_os = "windows"));
|
||||||
|
|
||||||
if include_dir != "" && (!need_lua_lib || lib_dir != "") {
|
if !include_dir.is_empty() {
|
||||||
if lua_lib == "" {
|
if need_lua_lib {
|
||||||
panic!("LUA_LIB_NAME is not set");
|
if lib_dir.is_empty() {
|
||||||
|
panic!("LUA_LIB is not set");
|
||||||
|
}
|
||||||
|
if lua_lib.is_empty() {
|
||||||
|
panic!("LUA_LIB_NAME is not set");
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut link_lib = "";
|
||||||
|
if get_env_var("LUA_LINK") == "static" {
|
||||||
|
link_lib = "static=";
|
||||||
|
};
|
||||||
|
println!("cargo:rustc-link-search=native={}", lib_dir);
|
||||||
|
println!("cargo:rustc-link-lib={}{}", link_lib, lua_lib);
|
||||||
}
|
}
|
||||||
let _version = use_custom_lua(&include_dir, &lib_dir, &lua_lib).unwrap();
|
|
||||||
return PathBuf::from(include_dir);
|
return PathBuf::from(include_dir);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -100,39 +117,3 @@ pub fn probe_lua() -> PathBuf {
|
|||||||
lua.unwrap().include_paths[0].clone()
|
lua.unwrap().include_paths[0].clone()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn use_custom_lua<S: AsRef<Path>>(include_dir: &S, lib_dir: &S, lua_lib: &S) -> Result<String> {
|
|
||||||
let mut version_found = String::new();
|
|
||||||
|
|
||||||
// Find LUA_VERSION_NUM
|
|
||||||
let mut lua_h_path = include_dir.as_ref().to_owned();
|
|
||||||
lua_h_path.push("lua.h");
|
|
||||||
let f = File::open(lua_h_path)?;
|
|
||||||
let reader = BufReader::new(f);
|
|
||||||
for line in reader.lines() {
|
|
||||||
let line = line?;
|
|
||||||
let parts = line.split_whitespace().collect::<Vec<_>>();
|
|
||||||
if parts.len() == 3 && parts[1] == "LUA_VERSION_NUM" {
|
|
||||||
version_found = parts[2].to_string();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let link_lib = match env::var("LUA_LINK") {
|
|
||||||
Ok(s) if s == "static" => "static=",
|
|
||||||
_ => "",
|
|
||||||
};
|
|
||||||
|
|
||||||
if cfg!(any(not(feature = "module"), target_os = "windows")) {
|
|
||||||
println!(
|
|
||||||
"cargo:rustc-link-search=native={}",
|
|
||||||
lib_dir.as_ref().display()
|
|
||||||
);
|
|
||||||
println!(
|
|
||||||
"cargo:rustc-link-lib={}{}",
|
|
||||||
link_lib,
|
|
||||||
lua_lib.as_ref().display()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(version_found)
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -236,5 +236,8 @@ fn main() {
|
|||||||
generate_glue().unwrap();
|
generate_glue().unwrap();
|
||||||
} else {
|
} else {
|
||||||
build_glue(&include_dir);
|
build_glue(&include_dir);
|
||||||
|
println!("cargo:rerun-if-changed=src/ffi/glue/glue.c");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
println!("cargo:rerun-if-changed=build");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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::collections::HashMap;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use bstr::BString;
|
|
||||||
use hyper::body::{Body as HyperBody, HttpBody as _};
|
use hyper::body::{Body as HyperBody, HttpBody as _};
|
||||||
use hyper::Client as HyperClient;
|
use hyper::Client as HyperClient;
|
||||||
use tokio::sync::Mutex;
|
use tokio::sync::Mutex;
|
||||||
|
|
||||||
use mlua::{Error, Lua, Result, UserData, UserDataMethods};
|
use mlua::{chunk, ExternalResult, Lua, Result, UserData, UserDataMethods};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
struct BodyReader(Arc<Mutex<HyperBody>>);
|
struct BodyReader(Arc<Mutex<HyperBody>>);
|
||||||
@@ -19,11 +18,11 @@ impl BodyReader {
|
|||||||
|
|
||||||
impl UserData for BodyReader {
|
impl UserData for BodyReader {
|
||||||
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
|
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
|
||||||
methods.add_async_method("read", |_, reader, ()| async move {
|
methods.add_async_method("read", |lua, reader, ()| async move {
|
||||||
let mut reader = reader.0.lock().await;
|
let mut reader = reader.0.lock().await;
|
||||||
if let Some(bytes) = reader.data().await {
|
if let Some(bytes) = reader.data().await {
|
||||||
let bytes = bytes.map_err(Error::external)?;
|
let bytes = bytes.to_lua_err()?;
|
||||||
return Ok(Some(BString::from(bytes.as_ref())));
|
return Some(lua.create_string(&bytes)).transpose();
|
||||||
}
|
}
|
||||||
Ok(None)
|
Ok(None)
|
||||||
});
|
});
|
||||||
@@ -36,18 +35,18 @@ async fn main() -> Result<()> {
|
|||||||
|
|
||||||
let fetch_url = lua.create_async_function(|lua, uri: String| async move {
|
let fetch_url = lua.create_async_function(|lua, uri: String| async move {
|
||||||
let client = HyperClient::new();
|
let client = HyperClient::new();
|
||||||
let uri = uri.parse().map_err(Error::external)?;
|
let uri = uri.parse().to_lua_err()?;
|
||||||
let resp = client.get(uri).await.map_err(Error::external)?;
|
let resp = client.get(uri).await.to_lua_err()?;
|
||||||
|
|
||||||
let lua_resp = lua.create_table()?;
|
let lua_resp = lua.create_table()?;
|
||||||
lua_resp.set("status", resp.status().as_u16())?;
|
lua_resp.set("status", resp.status().as_u16())?;
|
||||||
|
|
||||||
let mut headers = HashMap::new();
|
let mut headers = HashMap::new();
|
||||||
for (key, value) in resp.headers().iter() {
|
for (key, value) in resp.headers() {
|
||||||
headers
|
headers
|
||||||
.entry(key.as_str())
|
.entry(key.as_str())
|
||||||
.or_insert(Vec::new())
|
.or_insert(Vec::new())
|
||||||
.push(value.to_str().unwrap());
|
.push(value.to_str().to_lua_err()?);
|
||||||
}
|
}
|
||||||
|
|
||||||
lua_resp.set("headers", headers)?;
|
lua_resp.set("headers", headers)?;
|
||||||
@@ -56,13 +55,9 @@ async fn main() -> Result<()> {
|
|||||||
Ok(lua_resp)
|
Ok(lua_resp)
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
let globals = lua.globals();
|
|
||||||
globals.set("fetch_url", fetch_url)?;
|
|
||||||
|
|
||||||
let f = lua
|
let f = lua
|
||||||
.load(
|
.load(chunk! {
|
||||||
r#"
|
local res = $fetch_url(...)
|
||||||
local res = fetch_url(...)
|
|
||||||
print(res.status)
|
print(res.status)
|
||||||
for key, vals in pairs(res.headers) do
|
for key, vals in pairs(res.headers) do
|
||||||
for _, val in ipairs(vals) do
|
for _, val in ipairs(vals) do
|
||||||
@@ -75,8 +70,7 @@ async fn main() -> Result<()> {
|
|||||||
print(body)
|
print(body)
|
||||||
end
|
end
|
||||||
until not body
|
until not body
|
||||||
"#,
|
})
|
||||||
)
|
|
||||||
.into_function()?;
|
.into_function()?;
|
||||||
|
|
||||||
f.call_async("http://httpbin.org/ip").await
|
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]
|
#[tokio::main]
|
||||||
async fn main() -> Result<()> {
|
async fn main() -> Result<()> {
|
||||||
let lua = Lua::new();
|
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 fetch_json = lua.create_async_function(|lua, uri: String| async move {
|
||||||
let resp = reqwest::get(&uri)
|
let resp = reqwest::get(&uri)
|
||||||
.await
|
.await
|
||||||
.and_then(|resp| resp.error_for_status())
|
.and_then(|resp| resp.error_for_status())
|
||||||
.map_err(Error::external)?;
|
.to_lua_err()?;
|
||||||
let json = resp
|
let json = resp.json::<serde_json::Value>().await.to_lua_err()?;
|
||||||
.json::<serde_json::Value>()
|
|
||||||
.await
|
|
||||||
.map_err(Error::external)?;
|
|
||||||
lua.to_value(&json)
|
lua.to_value(&json)
|
||||||
})?;
|
})?;
|
||||||
globals.set("fetch_json", fetch_json)?;
|
|
||||||
|
|
||||||
let f = lua
|
let f = lua
|
||||||
.load(
|
.load(chunk! {
|
||||||
r#"
|
|
||||||
function print_r(t, indent)
|
function print_r(t, indent)
|
||||||
local indent = indent or ''
|
local indent = indent or ""
|
||||||
for k, v in pairs(t) do
|
for k, v in pairs(t) do
|
||||||
io.write(indent, tostring(k))
|
io.write(indent, tostring(k))
|
||||||
if type(v) == "table" then io.write(':\n') print_r(v, indent..' ')
|
if type(v) == "table" then io.write(":\n") print_r(v, indent.." ")
|
||||||
else io.write(': ', v == null and "null" or tostring(v), '\n') end
|
else io.write(": ", v == $null and "null" or tostring(v), "\n") end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local res = fetch_json(...)
|
local res = $fetch_json(...)
|
||||||
print_r(res)
|
print_r(res)
|
||||||
"#,
|
})
|
||||||
)
|
|
||||||
.into_function()?;
|
.into_function()?;
|
||||||
|
|
||||||
f.call_async("https://httpbin.org/anything?arg0=val0").await
|
f.call_async("https://httpbin.org/anything?arg0=val0").await
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
use std::net::SocketAddr;
|
use std::net::SocketAddr;
|
||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
use hyper::server::conn::AddrStream;
|
use hyper::server::conn::AddrStream;
|
||||||
use hyper::service::{make_service_fn, service_fn};
|
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};
|
use mlua::{Error, Function, Lua, Result, Table, UserData, UserDataMethods};
|
||||||
|
|
||||||
#[derive(Clone)]
|
struct LuaRequest(SocketAddr, Request<Body>);
|
||||||
struct LuaRequest(Arc<(SocketAddr, Request<Body>)>);
|
|
||||||
|
|
||||||
impl UserData for LuaRequest {
|
impl UserData for LuaRequest {
|
||||||
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
|
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("remote_addr", |_lua, req, ()| Ok((req.0).to_string()));
|
||||||
methods.add_method("method", |_lua, req, ()| Ok((req.0).1.method().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>| {
|
Ok::<_, Error>(service_fn(move |req: Request<Body>| {
|
||||||
let handler = handler.clone();
|
let handler = handler.clone();
|
||||||
async move {
|
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 lua_resp: Table = handler.call_async(lua_req).await?;
|
||||||
let body = lua_resp
|
let body = lua_resp
|
||||||
.get::<_, Option<String>>("body")?
|
.get::<_, Option<String>>("body")?
|
||||||
@@ -72,7 +70,7 @@ async fn main() -> Result<()> {
|
|||||||
["X-Req-Method"] = req:method(),
|
["X-Req-Method"] = req:method(),
|
||||||
["X-Remote-Addr"] = req:remote_addr(),
|
["X-Remote-Addr"] = req:remote_addr(),
|
||||||
},
|
},
|
||||||
body = "Hello, World!"
|
body = "Hello, World!\n"
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
"#,
|
"#,
|
||||||
|
|||||||
@@ -1,14 +1,12 @@
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use bstr::BString;
|
|
||||||
use tokio::io::{AsyncReadExt, AsyncWriteExt};
|
use tokio::io::{AsyncReadExt, AsyncWriteExt};
|
||||||
use tokio::net::{TcpListener, TcpStream};
|
use tokio::net::{TcpListener, TcpStream};
|
||||||
use tokio::sync::Mutex;
|
use tokio::sync::Mutex;
|
||||||
use tokio::task;
|
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;
|
struct LuaTcp;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
@@ -41,15 +39,15 @@ impl UserData for LuaTcpStream {
|
|||||||
Ok(stream.0.lock().await.peer_addr()?.to_string())
|
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 mut buf = vec![0; size];
|
||||||
let n = stream.0.lock().await.read(&mut buf).await?;
|
let n = stream.0.lock().await.read(&mut buf).await?;
|
||||||
buf.truncate(n);
|
buf.truncate(n);
|
||||||
Ok(BString::from(buf))
|
lua.create_string(&buf)
|
||||||
});
|
});
|
||||||
|
|
||||||
methods.add_async_method("write", |_, stream, data: BString| async move {
|
methods.add_async_method("write", |_, stream, data: LuaString| async move {
|
||||||
let n = stream.0.lock().await.write(&data).await?;
|
let n = stream.0.lock().await.write(&data.as_bytes()).await?;
|
||||||
Ok(n)
|
Ok(n)
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -66,15 +64,12 @@ async fn run_server(lua: &'static Lua) -> Result<()> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
let globals = lua.globals();
|
let tcp = LuaTcp;
|
||||||
globals.set("tcp", LuaTcp)?;
|
|
||||||
globals.set("spawn", spawn)?;
|
|
||||||
|
|
||||||
let server = lua
|
let server = lua
|
||||||
.load(
|
.load(chunk! {
|
||||||
r#"
|
|
||||||
local addr = ...
|
local addr = ...
|
||||||
local listener = tcp.bind(addr)
|
local listener = $tcp.bind(addr)
|
||||||
print("listening on "..addr)
|
print("listening on "..addr)
|
||||||
|
|
||||||
local accept_new = true
|
local accept_new = true
|
||||||
@@ -87,7 +82,7 @@ async fn run_server(lua: &'static Lua) -> Result<()> {
|
|||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
spawn(function()
|
$spawn(function()
|
||||||
while true do
|
while true do
|
||||||
local data = stream:read(100)
|
local data = stream:read(100)
|
||||||
data = data:match("^%s*(.-)%s*$") -- trim
|
data = data:match("^%s*(.-)%s*$") -- trim
|
||||||
@@ -106,8 +101,7 @@ async fn run_server(lua: &'static Lua) -> Result<()> {
|
|||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
"#,
|
})
|
||||||
)
|
|
||||||
.into_function()?;
|
.into_function()?;
|
||||||
|
|
||||||
task::LocalSet::new()
|
task::LocalSet::new()
|
||||||
|
|||||||
+24
-13
@@ -1,15 +1,15 @@
|
|||||||
use std::f32;
|
use std::f32;
|
||||||
use std::iter::FromIterator;
|
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<()> {
|
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.
|
// *without* the debug library.
|
||||||
let lua = Lua::new();
|
let lua = Lua::new();
|
||||||
|
|
||||||
// You can get and set global variables. Notice that the globals table here is a permanent
|
// 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
|
// 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).
|
// based heavily around sharing and internal mutation (just like Lua itself).
|
||||||
|
|
||||||
let globals = lua.globals();
|
let globals = lua.globals();
|
||||||
@@ -20,8 +20,8 @@ fn main() -> Result<()> {
|
|||||||
assert_eq!(globals.get::<_, String>("string_var")?, "hello");
|
assert_eq!(globals.get::<_, String>("string_var")?, "hello");
|
||||||
assert_eq!(globals.get::<_, i64>("int_var")?, 42);
|
assert_eq!(globals.get::<_, i64>("int_var")?, 42);
|
||||||
|
|
||||||
// You can load and evaluate Lua code. The returned type of `Lua::load` is a builder
|
// 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
|
// 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
|
// the name of the laoded chunk to "example code", which will be used when Lua error
|
||||||
// messages are printed.
|
// messages are printed.
|
||||||
|
|
||||||
@@ -38,6 +38,17 @@ fn main() -> Result<()> {
|
|||||||
assert_eq!(lua.load("false == false").eval::<bool>()?, true);
|
assert_eq!(lua.load("false == false").eval::<bool>()?, true);
|
||||||
assert_eq!(lua.load("return 1 + 2").eval::<i32>()?, 3);
|
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
|
// You can create and manage Lua tables
|
||||||
|
|
||||||
let array_table = lua.create_table()?;
|
let array_table = lua.create_table()?;
|
||||||
@@ -76,7 +87,7 @@ fn main() -> Result<()> {
|
|||||||
let print: Function = globals.get("print")?;
|
let print: Function = globals.get("print")?;
|
||||||
print.call::<_, ()>("hello from rust")?;
|
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:
|
// multiple parameters:
|
||||||
|
|
||||||
print.call::<_, ()>(("hello", "again", "from", "rust"))?;
|
print.call::<_, ()>(("hello", "again", "from", "rust"))?;
|
||||||
@@ -87,15 +98,15 @@ fn main() -> Result<()> {
|
|||||||
["hello", "yet", "again", "from", "rust"].iter().cloned(),
|
["hello", "yet", "again", "from", "rust"].iter().cloned(),
|
||||||
))?;
|
))?;
|
||||||
|
|
||||||
// You can bind rust functions to Lua as well. Callbacks receive the Lua state inself as their
|
// 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
|
// 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
|
// of the arguments can be anything that is convertible from the parameters given by Lua, in
|
||||||
// this case, the function expects two string sequences.
|
// this case, the function expects two string sequences.
|
||||||
|
|
||||||
let check_equal = lua.create_function(|_, (list1, list2): (Vec<String>, Vec<String>)| {
|
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.
|
// 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
|
// 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)
|
Ok(list1 == list2)
|
||||||
})?;
|
})?;
|
||||||
globals.set("check_equal", check_equal)?;
|
globals.set("check_equal", check_equal)?;
|
||||||
@@ -174,7 +185,7 @@ fn main() -> Result<()> {
|
|||||||
|
|
||||||
lua.scope(|scope| {
|
lua.scope(|scope| {
|
||||||
// We create a 'sketchy' Lua callback that holds a mutable reference to the variable
|
// 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.
|
// because it could be unsafe.
|
||||||
|
|
||||||
lua.globals().set(
|
lua.globals().set(
|
||||||
@@ -191,9 +202,9 @@ fn main() -> Result<()> {
|
|||||||
assert_eq!(rust_val, 42);
|
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
|
// 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.
|
// might be able to improperly access the freed `rust_val` which would be unsafe.
|
||||||
assert!(lua.load("sketchy()").exec().is_err());
|
assert!(lua.load("sketchy()").exec().is_err());
|
||||||
|
|
||||||
|
|||||||
@@ -8,10 +8,32 @@ fn used_memory(lua: &Lua, _: ()) -> LuaResult<usize> {
|
|||||||
Ok(lua.used_memory())
|
Ok(lua.used_memory())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn check_userdata(_: &Lua, ud: MyUserData) -> LuaResult<i32> {
|
||||||
|
Ok(ud.0)
|
||||||
|
}
|
||||||
|
|
||||||
#[mlua::lua_module]
|
#[mlua::lua_module]
|
||||||
fn rust_module(lua: &Lua) -> LuaResult<LuaTable> {
|
fn rust_module(lua: &Lua) -> LuaResult<LuaTable> {
|
||||||
let exports = lua.create_table()?;
|
let exports = lua.create_table()?;
|
||||||
exports.set("sum", lua.create_function(sum)?)?;
|
exports.set("sum", lua.create_function(sum)?)?;
|
||||||
exports.set("used_memory", lua.create_function(used_memory)?)?;
|
exports.set("used_memory", lua.create_function(used_memory)?)?;
|
||||||
|
exports.set("check_userdata", lua.create_function(check_userdata)?)?;
|
||||||
Ok(exports)
|
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()?)?;
|
"#).eval()?)?;
|
||||||
|
|
||||||
// Set it as (serializable) userdata
|
// Set it as (serializable) userdata
|
||||||
globals.set("null", lua.null()?)?;
|
globals.set("null", lua.null())?;
|
||||||
globals.set("array_mt", lua.array_metatable()?)?;
|
globals.set("array_mt", lua.array_metatable())?;
|
||||||
globals.set("car", lua.create_ser_userdata(car)?)?;
|
globals.set("car", lua.create_ser_userdata(car)?)?;
|
||||||
|
|
||||||
// Create a Lua table with multiple data types
|
// 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]
|
[package]
|
||||||
name = "mlua_derive"
|
name = "mlua_derive"
|
||||||
version = "0.5.0"
|
version = "0.6.0"
|
||||||
authors = ["Aleksandr Orlenko <zxteam@pm.me>"]
|
authors = ["Aleksandr Orlenko <zxteam@pm.me>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
description = "Procedural macros for the mlua crate."
|
description = "Procedural macros for the mlua crate."
|
||||||
@@ -11,7 +11,14 @@ license = "MIT"
|
|||||||
[lib]
|
[lib]
|
||||||
proc-macro = true
|
proc-macro = true
|
||||||
|
|
||||||
|
[features]
|
||||||
|
macros = ["proc-macro-error", "itertools", "regex", "once_cell"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
proc-macro2 = "1.0"
|
|
||||||
quote = "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"] }
|
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_macro::TokenStream;
|
||||||
use proc_macro2::{Ident, Span};
|
use proc_macro2::{Ident, Span};
|
||||||
use quote::quote_spanned;
|
use quote::quote;
|
||||||
use syn::{parse_macro_input, spanned::Spanned, AttributeArgs, Error, ItemFn};
|
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]
|
#[proc_macro_attribute]
|
||||||
pub fn lua_module(attr: TokenStream, item: TokenStream) -> TokenStream {
|
pub fn lua_module(attr: TokenStream, item: TokenStream) -> TokenStream {
|
||||||
let args = parse_macro_input!(attr as AttributeArgs);
|
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 {
|
if !args.is_empty() {
|
||||||
let err = Error::new(Span::call_site(), "the number of arguments must be zero")
|
let err = Error::new(Span::call_site(), "the macro does not support arguments")
|
||||||
.to_compile_error();
|
.to_compile_error();
|
||||||
return err.into();
|
return err.into();
|
||||||
}
|
}
|
||||||
|
|
||||||
let span = item.span();
|
let func_name = func.sig.ident.clone();
|
||||||
let item_name = item.sig.ident.clone();
|
let ext_entrypoint_name = Ident::new(&format!("luaopen_{}", func_name), Span::call_site());
|
||||||
let ext_entrypoint_name = Ident::new(&format!("luaopen_{}", item.sig.ident), Span::call_site());
|
|
||||||
|
|
||||||
let wrapped = quote_spanned! { span =>
|
let wrapped = quote! {
|
||||||
mlua::require_module_feature!();
|
::mlua::require_module_feature!();
|
||||||
|
|
||||||
|
#func
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
unsafe extern "C" fn #ext_entrypoint_name(state: *mut mlua::lua_State) -> std::os::raw::c_int {
|
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(#func_name)
|
||||||
mlua::Lua::init_from_ptr(state)
|
.expect("cannot initialize module")
|
||||||
.entrypoint1(#item_name)
|
|
||||||
.unwrap()
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
wrapped.into()
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
+94
-12
@@ -1,3 +1,6 @@
|
|||||||
|
#![allow(clippy::wrong_self_convention)]
|
||||||
|
|
||||||
|
use std::borrow::Cow;
|
||||||
use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet};
|
use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet};
|
||||||
use std::ffi::{CStr, CString};
|
use std::ffi::{CStr, CString};
|
||||||
use std::hash::{BuildHasher, Hash};
|
use std::hash::{BuildHasher, Hash};
|
||||||
@@ -17,24 +20,28 @@ use crate::userdata::{AnyUserData, UserData};
|
|||||||
use crate::value::{FromLua, Nil, ToLua, Value};
|
use crate::value::{FromLua, Nil, ToLua, Value};
|
||||||
|
|
||||||
impl<'lua> ToLua<'lua> for Value<'lua> {
|
impl<'lua> ToLua<'lua> for Value<'lua> {
|
||||||
|
#[inline]
|
||||||
fn to_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
|
fn to_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
|
||||||
Ok(self)
|
Ok(self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'lua> FromLua<'lua> for Value<'lua> {
|
impl<'lua> FromLua<'lua> for Value<'lua> {
|
||||||
|
#[inline]
|
||||||
fn from_lua(lua_value: Value<'lua>, _: &'lua Lua) -> Result<Self> {
|
fn from_lua(lua_value: Value<'lua>, _: &'lua Lua) -> Result<Self> {
|
||||||
Ok(lua_value)
|
Ok(lua_value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'lua> ToLua<'lua> for String<'lua> {
|
impl<'lua> ToLua<'lua> for String<'lua> {
|
||||||
|
#[inline]
|
||||||
fn to_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
|
fn to_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
|
||||||
Ok(Value::String(self))
|
Ok(Value::String(self))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'lua> FromLua<'lua> for String<'lua> {
|
impl<'lua> FromLua<'lua> for String<'lua> {
|
||||||
|
#[inline]
|
||||||
fn from_lua(value: Value<'lua>, lua: &'lua Lua) -> Result<String<'lua>> {
|
fn from_lua(value: Value<'lua>, lua: &'lua Lua) -> Result<String<'lua>> {
|
||||||
let ty = value.type_name();
|
let ty = value.type_name();
|
||||||
lua.coerce_string(value)?
|
lua.coerce_string(value)?
|
||||||
@@ -47,12 +54,14 @@ impl<'lua> FromLua<'lua> for String<'lua> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'lua> ToLua<'lua> for Table<'lua> {
|
impl<'lua> ToLua<'lua> for Table<'lua> {
|
||||||
|
#[inline]
|
||||||
fn to_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
|
fn to_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
|
||||||
Ok(Value::Table(self))
|
Ok(Value::Table(self))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'lua> FromLua<'lua> for Table<'lua> {
|
impl<'lua> FromLua<'lua> for Table<'lua> {
|
||||||
|
#[inline]
|
||||||
fn from_lua(value: Value<'lua>, _: &'lua Lua) -> Result<Table<'lua>> {
|
fn from_lua(value: Value<'lua>, _: &'lua Lua) -> Result<Table<'lua>> {
|
||||||
match value {
|
match value {
|
||||||
Value::Table(table) => Ok(table),
|
Value::Table(table) => Ok(table),
|
||||||
@@ -66,12 +75,14 @@ impl<'lua> FromLua<'lua> for Table<'lua> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'lua> ToLua<'lua> for Function<'lua> {
|
impl<'lua> ToLua<'lua> for Function<'lua> {
|
||||||
|
#[inline]
|
||||||
fn to_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
|
fn to_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
|
||||||
Ok(Value::Function(self))
|
Ok(Value::Function(self))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'lua> FromLua<'lua> for Function<'lua> {
|
impl<'lua> FromLua<'lua> for Function<'lua> {
|
||||||
|
#[inline]
|
||||||
fn from_lua(value: Value<'lua>, _: &'lua Lua) -> Result<Function<'lua>> {
|
fn from_lua(value: Value<'lua>, _: &'lua Lua) -> Result<Function<'lua>> {
|
||||||
match value {
|
match value {
|
||||||
Value::Function(table) => Ok(table),
|
Value::Function(table) => Ok(table),
|
||||||
@@ -85,12 +96,14 @@ impl<'lua> FromLua<'lua> for Function<'lua> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'lua> ToLua<'lua> for Thread<'lua> {
|
impl<'lua> ToLua<'lua> for Thread<'lua> {
|
||||||
|
#[inline]
|
||||||
fn to_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
|
fn to_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
|
||||||
Ok(Value::Thread(self))
|
Ok(Value::Thread(self))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'lua> FromLua<'lua> for Thread<'lua> {
|
impl<'lua> FromLua<'lua> for Thread<'lua> {
|
||||||
|
#[inline]
|
||||||
fn from_lua(value: Value<'lua>, _: &'lua Lua) -> Result<Thread<'lua>> {
|
fn from_lua(value: Value<'lua>, _: &'lua Lua) -> Result<Thread<'lua>> {
|
||||||
match value {
|
match value {
|
||||||
Value::Thread(t) => Ok(t),
|
Value::Thread(t) => Ok(t),
|
||||||
@@ -104,12 +117,14 @@ impl<'lua> FromLua<'lua> for Thread<'lua> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'lua> ToLua<'lua> for AnyUserData<'lua> {
|
impl<'lua> ToLua<'lua> for AnyUserData<'lua> {
|
||||||
|
#[inline]
|
||||||
fn to_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
|
fn to_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
|
||||||
Ok(Value::UserData(self))
|
Ok(Value::UserData(self))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'lua> FromLua<'lua> for AnyUserData<'lua> {
|
impl<'lua> FromLua<'lua> for AnyUserData<'lua> {
|
||||||
|
#[inline]
|
||||||
fn from_lua(value: Value<'lua>, _: &'lua Lua) -> Result<AnyUserData<'lua>> {
|
fn from_lua(value: Value<'lua>, _: &'lua Lua) -> Result<AnyUserData<'lua>> {
|
||||||
match value {
|
match value {
|
||||||
Value::UserData(ud) => Ok(ud),
|
Value::UserData(ud) => Ok(ud),
|
||||||
@@ -123,12 +138,14 @@ impl<'lua> FromLua<'lua> for AnyUserData<'lua> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'lua, T: 'static + MaybeSend + UserData> ToLua<'lua> for T {
|
impl<'lua, T: 'static + MaybeSend + UserData> ToLua<'lua> for T {
|
||||||
|
#[inline]
|
||||||
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
|
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
|
||||||
Ok(Value::UserData(lua.create_userdata(self)?))
|
Ok(Value::UserData(lua.create_userdata(self)?))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'lua, T: 'static + UserData + Clone> FromLua<'lua> for T {
|
impl<'lua, T: 'static + UserData + Clone> FromLua<'lua> for T {
|
||||||
|
#[inline]
|
||||||
fn from_lua(value: Value<'lua>, _: &'lua Lua) -> Result<T> {
|
fn from_lua(value: Value<'lua>, _: &'lua Lua) -> Result<T> {
|
||||||
match value {
|
match value {
|
||||||
Value::UserData(ud) => Ok(ud.borrow::<T>()?.clone()),
|
Value::UserData(ud) => Ok(ud.borrow::<T>()?.clone()),
|
||||||
@@ -142,12 +159,14 @@ impl<'lua, T: 'static + UserData + Clone> FromLua<'lua> for T {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'lua> ToLua<'lua> for Error {
|
impl<'lua> ToLua<'lua> for Error {
|
||||||
|
#[inline]
|
||||||
fn to_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
|
fn to_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
|
||||||
Ok(Value::Error(self))
|
Ok(Value::Error(self))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'lua> FromLua<'lua> for Error {
|
impl<'lua> FromLua<'lua> for Error {
|
||||||
|
#[inline]
|
||||||
fn from_lua(value: Value<'lua>, lua: &'lua Lua) -> Result<Error> {
|
fn from_lua(value: Value<'lua>, lua: &'lua Lua) -> Result<Error> {
|
||||||
match value {
|
match value {
|
||||||
Value::Error(err) => Ok(err),
|
Value::Error(err) => Ok(err),
|
||||||
@@ -161,12 +180,14 @@ impl<'lua> FromLua<'lua> for Error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'lua> ToLua<'lua> for bool {
|
impl<'lua> ToLua<'lua> for bool {
|
||||||
|
#[inline]
|
||||||
fn to_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
|
fn to_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
|
||||||
Ok(Value::Boolean(self))
|
Ok(Value::Boolean(self))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'lua> FromLua<'lua> for bool {
|
impl<'lua> FromLua<'lua> for bool {
|
||||||
|
#[inline]
|
||||||
fn from_lua(v: Value<'lua>, _: &'lua Lua) -> Result<Self> {
|
fn from_lua(v: Value<'lua>, _: &'lua Lua) -> Result<Self> {
|
||||||
match v {
|
match v {
|
||||||
Value::Nil => Ok(false),
|
Value::Nil => Ok(false),
|
||||||
@@ -177,12 +198,14 @@ impl<'lua> FromLua<'lua> for bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'lua> ToLua<'lua> for LightUserData {
|
impl<'lua> ToLua<'lua> for LightUserData {
|
||||||
|
#[inline]
|
||||||
fn to_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
|
fn to_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
|
||||||
Ok(Value::LightUserData(self))
|
Ok(Value::LightUserData(self))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'lua> FromLua<'lua> for LightUserData {
|
impl<'lua> FromLua<'lua> for LightUserData {
|
||||||
|
#[inline]
|
||||||
fn from_lua(value: Value<'lua>, _: &'lua Lua) -> Result<Self> {
|
fn from_lua(value: Value<'lua>, _: &'lua Lua) -> Result<Self> {
|
||||||
match value {
|
match value {
|
||||||
Value::LightUserData(ud) => Ok(ud),
|
Value::LightUserData(ud) => Ok(ud),
|
||||||
@@ -196,12 +219,14 @@ impl<'lua> FromLua<'lua> for LightUserData {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'lua> ToLua<'lua> for StdString {
|
impl<'lua> ToLua<'lua> for StdString {
|
||||||
|
#[inline]
|
||||||
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
|
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
|
||||||
Ok(Value::String(lua.create_string(&self)?))
|
Ok(Value::String(lua.create_string(&self)?))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'lua> FromLua<'lua> for StdString {
|
impl<'lua> FromLua<'lua> for StdString {
|
||||||
|
#[inline]
|
||||||
fn from_lua(value: Value<'lua>, lua: &'lua Lua) -> Result<Self> {
|
fn from_lua(value: Value<'lua>, lua: &'lua Lua) -> Result<Self> {
|
||||||
let ty = value.type_name();
|
let ty = value.type_name();
|
||||||
Ok(lua
|
Ok(lua
|
||||||
@@ -217,11 +242,40 @@ impl<'lua> FromLua<'lua> for StdString {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'lua> ToLua<'lua> for &str {
|
impl<'lua> ToLua<'lua> for &str {
|
||||||
|
#[inline]
|
||||||
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
|
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
|
||||||
Ok(Value::String(lua.create_string(self)?))
|
Ok(Value::String(lua.create_string(self)?))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<'lua> ToLua<'lua> for Cow<'_, str> {
|
||||||
|
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
|
||||||
|
Ok(Value::String(lua.create_string(self.as_bytes())?))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'lua> ToLua<'lua> for Box<str> {
|
||||||
|
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
|
||||||
|
Ok(Value::String(lua.create_string(&*self)?))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'lua> FromLua<'lua> for Box<str> {
|
||||||
|
fn from_lua(value: Value<'lua>, lua: &'lua Lua) -> Result<Self> {
|
||||||
|
let ty = value.type_name();
|
||||||
|
Ok(lua
|
||||||
|
.coerce_string(value)?
|
||||||
|
.ok_or_else(|| Error::FromLuaConversionError {
|
||||||
|
from: ty,
|
||||||
|
to: "Box<str>",
|
||||||
|
message: Some("expected string or number".to_string()),
|
||||||
|
})?
|
||||||
|
.to_str()?
|
||||||
|
.to_owned()
|
||||||
|
.into_boxed_str())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<'lua> ToLua<'lua> for CString {
|
impl<'lua> ToLua<'lua> for CString {
|
||||||
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
|
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
|
||||||
Ok(Value::String(lua.create_string(self.as_bytes())?))
|
Ok(Value::String(lua.create_string(self.as_bytes())?))
|
||||||
@@ -256,6 +310,12 @@ impl<'lua> ToLua<'lua> for &CStr {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<'lua> ToLua<'lua> for Cow<'_, CStr> {
|
||||||
|
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
|
||||||
|
Ok(Value::String(lua.create_string(self.to_bytes())?))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<'lua> ToLua<'lua> for BString {
|
impl<'lua> ToLua<'lua> for BString {
|
||||||
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
|
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
|
||||||
Ok(Value::String(lua.create_string(&self)?))
|
Ok(Value::String(lua.create_string(&self)?))
|
||||||
@@ -423,6 +483,26 @@ lua_convert_array! {
|
|||||||
30 31 32
|
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> {
|
impl<'lua, T: ToLua<'lua>> ToLua<'lua> for Vec<T> {
|
||||||
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
|
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
|
||||||
Ok(Value::Table(lua.create_sequence_from(self)?))
|
Ok(Value::Table(lua.create_sequence_from(self)?))
|
||||||
@@ -497,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> {
|
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> {
|
fn from_lua(value: Value<'lua>, _: &'lua Lua) -> Result<Self> {
|
||||||
if let Value::Table(table) = value {
|
match value {
|
||||||
table
|
Value::Table(table) if table.len()? > 0 => table.sequence_values().collect(),
|
||||||
|
Value::Table(table) => table
|
||||||
.pairs::<T, Value<'lua>>()
|
.pairs::<T, Value<'lua>>()
|
||||||
.map(|res| res.map(|(k, _)| k))
|
.map(|res| res.map(|(k, _)| k))
|
||||||
.collect()
|
.collect(),
|
||||||
} else {
|
_ => Err(Error::FromLuaConversionError {
|
||||||
Err(Error::FromLuaConversionError {
|
|
||||||
from: value.type_name(),
|
from: value.type_name(),
|
||||||
to: "HashSet",
|
to: "HashSet",
|
||||||
message: Some("expected table".to_string()),
|
message: Some("expected table".to_string()),
|
||||||
})
|
}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -522,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> {
|
impl<'lua, T: Ord + FromLua<'lua>> FromLua<'lua> for BTreeSet<T> {
|
||||||
fn from_lua(value: Value<'lua>, _: &'lua Lua) -> Result<Self> {
|
fn from_lua(value: Value<'lua>, _: &'lua Lua) -> Result<Self> {
|
||||||
if let Value::Table(table) = value {
|
match value {
|
||||||
table
|
Value::Table(table) if table.len()? > 0 => table.sequence_values().collect(),
|
||||||
|
Value::Table(table) => table
|
||||||
.pairs::<T, Value<'lua>>()
|
.pairs::<T, Value<'lua>>()
|
||||||
.map(|res| res.map(|(k, _)| k))
|
.map(|res| res.map(|(k, _)| k))
|
||||||
.collect()
|
.collect(),
|
||||||
} else {
|
_ => Err(Error::FromLuaConversionError {
|
||||||
Err(Error::FromLuaConversionError {
|
|
||||||
from: value.type_name(),
|
from: value.type_name(),
|
||||||
to: "BTreeSet",
|
to: "BTreeSet",
|
||||||
message: Some("expected table".to_string()),
|
message: Some("expected table".to_string()),
|
||||||
})
|
}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'lua, T: ToLua<'lua>> ToLua<'lua> for Option<T> {
|
impl<'lua, T: ToLua<'lua>> ToLua<'lua> for Option<T> {
|
||||||
|
#[inline]
|
||||||
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
|
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
|
||||||
match self {
|
match self {
|
||||||
Some(val) => val.to_lua(lua),
|
Some(val) => val.to_lua(lua),
|
||||||
@@ -547,6 +628,7 @@ impl<'lua, T: ToLua<'lua>> ToLua<'lua> for Option<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'lua, T: FromLua<'lua>> FromLua<'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> {
|
fn from_lua(value: Value<'lua>, lua: &'lua Lua) -> Result<Self> {
|
||||||
match value {
|
match value {
|
||||||
Nil => Ok(None),
|
Nil => Ok(None),
|
||||||
|
|||||||
+36
-29
@@ -1,3 +1,5 @@
|
|||||||
|
#![allow(clippy::wrong_self_convention)]
|
||||||
|
|
||||||
use std::error::Error as StdError;
|
use std::error::Error as StdError;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::io::Error as IoError;
|
use std::io::Error as IoError;
|
||||||
@@ -9,6 +11,7 @@ use std::sync::Arc;
|
|||||||
|
|
||||||
/// Error type returned by `mlua` methods.
|
/// Error type returned by `mlua` methods.
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
|
#[non_exhaustive]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
/// Syntax error while parsing Lua source code.
|
/// Syntax error while parsing Lua source code.
|
||||||
SyntaxError {
|
SyntaxError {
|
||||||
@@ -87,7 +90,7 @@ pub enum Error {
|
|||||||
},
|
},
|
||||||
/// [`Thread::resume`] was called on an inactive coroutine.
|
/// [`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.
|
/// the coroutine.
|
||||||
///
|
///
|
||||||
/// [`Thread::status`] can be used to check if the coroutine can be resumed without causing this
|
/// [`Thread::status`] can be used to check if the coroutine can be resumed without causing this
|
||||||
@@ -130,6 +133,18 @@ pub enum Error {
|
|||||||
/// [`AnyUserData`]: struct.AnyUserData.html
|
/// [`AnyUserData`]: struct.AnyUserData.html
|
||||||
/// [`UserData`]: trait.UserData.html
|
/// [`UserData`]: trait.UserData.html
|
||||||
UserDataBorrowMutError,
|
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.
|
/// A `RegistryKey` produced from a different Lua state was used.
|
||||||
MismatchedRegistryKey,
|
MismatchedRegistryKey,
|
||||||
/// A Rust callback returned `Err`, raising the contained `Error` as a Lua error.
|
/// A Rust callback returned `Err`, raising the contained `Error` as a Lua error.
|
||||||
@@ -139,6 +154,11 @@ pub enum Error {
|
|||||||
/// Original error returned by the Rust code.
|
/// Original error returned by the Rust code.
|
||||||
cause: Arc<Error>,
|
cause: Arc<Error>,
|
||||||
},
|
},
|
||||||
|
/// A Rust panic that was previously resumed, returned again.
|
||||||
|
///
|
||||||
|
/// This error can occur only when a Rust panic resumed previously was recovered
|
||||||
|
/// and returned again.
|
||||||
|
PreviouslyResumedPanic,
|
||||||
/// Serialization error.
|
/// Serialization error.
|
||||||
#[cfg(feature = "serialize")]
|
#[cfg(feature = "serialize")]
|
||||||
#[cfg_attr(docsrs, doc(cfg(feature = "serialize")))]
|
#[cfg_attr(docsrs, doc(cfg(feature = "serialize")))]
|
||||||
@@ -154,15 +174,13 @@ pub enum Error {
|
|||||||
/// Returning `Err(ExternalError(...))` from a Rust callback will raise the error as a Lua
|
/// 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`,
|
/// 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.
|
/// from which the original error (and a stack traceback) can be recovered.
|
||||||
#[cfg(feature = "send")]
|
|
||||||
ExternalError(Arc<dyn StdError + Send + Sync>),
|
ExternalError(Arc<dyn StdError + Send + Sync>),
|
||||||
#[cfg(not(feature = "send"))]
|
|
||||||
ExternalError(Arc<dyn StdError>),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A specialized `Result` type used by `mlua`'s API.
|
/// A specialized `Result` type used by `mlua`'s API.
|
||||||
pub type Result<T> = StdResult<T, Error>;
|
pub type Result<T> = StdResult<T, Error>;
|
||||||
|
|
||||||
|
#[cfg(not(tarpaulin_include))]
|
||||||
impl fmt::Display for Error {
|
impl fmt::Display for Error {
|
||||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||||
match *self {
|
match *self {
|
||||||
@@ -197,22 +215,14 @@ impl fmt::Display for Error {
|
|||||||
fmt,
|
fmt,
|
||||||
"too many arguments to Function::bind"
|
"too many arguments to Function::bind"
|
||||||
),
|
),
|
||||||
Error::ToLuaConversionError {
|
Error::ToLuaConversionError { from, to, ref message } => {
|
||||||
from,
|
|
||||||
to,
|
|
||||||
ref message,
|
|
||||||
} => {
|
|
||||||
write!(fmt, "error converting {} to Lua {}", from, to)?;
|
write!(fmt, "error converting {} to Lua {}", from, to)?;
|
||||||
match *message {
|
match *message {
|
||||||
None => Ok(()),
|
None => Ok(()),
|
||||||
Some(ref message) => write!(fmt, " ({})", message),
|
Some(ref message) => write!(fmt, " ({})", message),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Error::FromLuaConversionError {
|
Error::FromLuaConversionError { from, to, ref message } => {
|
||||||
from,
|
|
||||||
to,
|
|
||||||
ref message,
|
|
||||||
} => {
|
|
||||||
write!(fmt, "error converting Lua {} to {}", from, to)?;
|
write!(fmt, "error converting Lua {} to {}", from, to)?;
|
||||||
match *message {
|
match *message {
|
||||||
None => Ok(()),
|
None => Ok(()),
|
||||||
@@ -224,11 +234,22 @@ impl fmt::Display for Error {
|
|||||||
Error::UserDataDestructed => write!(fmt, "userdata has been destructed"),
|
Error::UserDataDestructed => write!(fmt, "userdata has been destructed"),
|
||||||
Error::UserDataBorrowError => write!(fmt, "userdata already mutably borrowed"),
|
Error::UserDataBorrowError => write!(fmt, "userdata already mutably borrowed"),
|
||||||
Error::UserDataBorrowMutError => write!(fmt, "userdata already 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 => {
|
Error::MismatchedRegistryKey => {
|
||||||
write!(fmt, "RegistryKey used from different Lua state")
|
write!(fmt, "RegistryKey used from different Lua state")
|
||||||
}
|
}
|
||||||
Error::CallbackError { ref traceback, .. } => {
|
Error::CallbackError { ref traceback, .. } => {
|
||||||
write!(fmt, "callback error: {}", traceback)
|
write!(fmt, "callback error\n{}", traceback)
|
||||||
|
}
|
||||||
|
Error::PreviouslyResumedPanic => {
|
||||||
|
write!(fmt, "previously resumed panic returned again")
|
||||||
}
|
}
|
||||||
#[cfg(feature = "serialize")]
|
#[cfg(feature = "serialize")]
|
||||||
Error::SerializeError(ref err) => {
|
Error::SerializeError(ref err) => {
|
||||||
@@ -254,35 +275,21 @@ impl StdError for Error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Error {
|
impl Error {
|
||||||
#[cfg(feature = "send")]
|
|
||||||
pub fn external<T: Into<Box<dyn StdError + Send + Sync>>>(err: T) -> Error {
|
pub fn external<T: Into<Box<dyn StdError + Send + Sync>>>(err: T) -> Error {
|
||||||
Error::ExternalError(err.into().into())
|
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 {
|
pub trait ExternalError {
|
||||||
fn to_lua_err(self) -> Error;
|
fn to_lua_err(self) -> Error;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "send")]
|
|
||||||
impl<E: Into<Box<dyn StdError + Send + Sync>>> ExternalError for E {
|
impl<E: Into<Box<dyn StdError + Send + Sync>>> ExternalError for E {
|
||||||
fn to_lua_err(self) -> Error {
|
fn to_lua_err(self) -> Error {
|
||||||
Error::external(self)
|
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> {
|
pub trait ExternalResult<T> {
|
||||||
fn to_lua_err(self) -> Result<T>;
|
fn to_lua_err(self) -> Result<T>;
|
||||||
}
|
}
|
||||||
|
|||||||
+40
-3
@@ -1,6 +1,6 @@
|
|||||||
// The MIT License (MIT)
|
// The MIT License (MIT)
|
||||||
//
|
//
|
||||||
// Copyright (c) 2019 A. Orlenko
|
// Copyright (c) 2019-2021 A. Orlenko
|
||||||
//
|
//
|
||||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
// of this software and associated documentation files (the "Software"), to deal
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -228,6 +228,7 @@ pub fn lua_upvalueindex(i: c_int) -> c_int {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(any(feature = "lua51", feature = "luajit"))]
|
#[cfg(any(feature = "lua51", feature = "luajit"))]
|
||||||
|
#[inline(always)]
|
||||||
pub unsafe fn lua_absindex(L: *mut lua_State, mut idx: c_int) -> c_int {
|
pub unsafe fn lua_absindex(L: *mut lua_State, mut idx: c_int) -> c_int {
|
||||||
if idx < 0 && idx > lua::LUA_REGISTRYINDEX {
|
if idx < 0 && idx > lua::LUA_REGISTRYINDEX {
|
||||||
idx += lua_gettop(L) + 1;
|
idx += lua_gettop(L) + 1;
|
||||||
@@ -250,6 +251,7 @@ end
|
|||||||
|
|
||||||
#[cfg(any(feature = "lua51", feature = "luajit"))]
|
#[cfg(any(feature = "lua51", feature = "luajit"))]
|
||||||
pub unsafe fn lua_arith(L: *mut lua_State, op: c_int) {
|
pub unsafe fn lua_arith(L: *mut lua_State, op: c_int) {
|
||||||
|
#[allow(clippy::manual_range_contains)]
|
||||||
if op < LUA_OPADD || op > LUA_OPUNM {
|
if op < LUA_OPADD || op > LUA_OPUNM {
|
||||||
luaL_error(L, cstr!("invalid 'op' argument for lua_arith"));
|
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"))]
|
#[cfg(any(feature = "lua51", feature = "luajit"))]
|
||||||
|
#[inline(always)]
|
||||||
pub unsafe fn lua_copy(L: *mut lua_State, fromidx: c_int, toidx: c_int) {
|
pub unsafe fn lua_copy(L: *mut lua_State, fromidx: c_int, toidx: c_int) {
|
||||||
let abs_to = lua_absindex(L, toidx);
|
let abs_to = lua_absindex(L, toidx);
|
||||||
luaL_checkstack(L, 1, cstr!("not enough stack slots"));
|
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);
|
lua_replace(L, abs_to);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
pub unsafe fn lua_isinteger(L: *mut lua_State, idx: c_int) -> c_int {
|
pub unsafe fn lua_isinteger(L: *mut lua_State, idx: c_int) -> c_int {
|
||||||
if lua_type(L, idx) == lua::LUA_TNUMBER {
|
if lua_type(L, idx) == lua::LUA_TNUMBER {
|
||||||
let n = lua_tonumber(L, idx);
|
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"))]
|
#[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 {
|
pub unsafe fn lua_tonumberx(L: *mut lua_State, i: c_int, isnum: *mut c_int) -> lua_Number {
|
||||||
let n = lua_tonumber(L, i);
|
let n = lua_tonumber(L, i);
|
||||||
if !isnum.is_null() {
|
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
|
// Implemented for Lua 5.2 as well
|
||||||
// See https://github.com/keplerproject/lua-compat-5.3/issues/40
|
// 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 {
|
pub unsafe fn lua_tointegerx(L: *mut lua_State, i: c_int, isnum: *mut c_int) -> lua_Integer {
|
||||||
let mut ok = 0;
|
let mut ok = 0;
|
||||||
let n = lua_tonumberx(L, i, &mut ok);
|
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"))]
|
#[cfg(any(feature = "lua51", feature = "luajit"))]
|
||||||
|
#[inline(always)]
|
||||||
pub unsafe fn lua_rawlen(L: *mut lua_State, idx: c_int) -> usize {
|
pub unsafe fn lua_rawlen(L: *mut lua_State, idx: c_int) -> usize {
|
||||||
lua_objlen(L, idx)
|
lua_objlen(L, idx)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(any(feature = "lua51", feature = "luajit"))]
|
#[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 {
|
pub unsafe fn lua_compare(L: *mut lua_State, mut idx1: c_int, mut idx2: c_int, op: c_int) -> c_int {
|
||||||
match op {
|
match op {
|
||||||
lua::LUA_OPEQ => lua_equal(L, idx1, idx2),
|
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"))]
|
#[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 {
|
pub unsafe fn lua_pushlstring(L: *mut lua_State, s: *const c_char, l: usize) -> *const c_char {
|
||||||
if l == 0 {
|
if l == 0 {
|
||||||
lua_pushlstring_old(L, cstr!(""), 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")]
|
#[cfg(feature = "lua52")]
|
||||||
|
#[inline(always)]
|
||||||
pub unsafe fn lua_pushlstring(L: *mut lua_State, s: *const c_char, l: usize) -> *const c_char {
|
pub unsafe fn lua_pushlstring(L: *mut lua_State, s: *const c_char, l: usize) -> *const c_char {
|
||||||
if l == 0 {
|
if l == 0 {
|
||||||
lua_pushlstring_old(L, cstr!(""), 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"))]
|
#[cfg(any(feature = "lua51", feature = "luajit"))]
|
||||||
|
#[inline(always)]
|
||||||
pub unsafe fn lua_pushstring(L: *mut lua_State, s: *const c_char) -> *const c_char {
|
pub unsafe fn lua_pushstring(L: *mut lua_State, s: *const c_char) -> *const c_char {
|
||||||
lua_pushstring_old(L, s);
|
lua_pushstring_old(L, s);
|
||||||
lua_tostring(L, -1)
|
lua_tostring(L, -1)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "lua52")]
|
#[cfg(feature = "lua52")]
|
||||||
|
#[inline(always)]
|
||||||
pub unsafe fn lua_getglobal(L: *mut lua_State, var: *const c_char) -> c_int {
|
pub unsafe fn lua_getglobal(L: *mut lua_State, var: *const c_char) -> c_int {
|
||||||
lua_getglobal_old(L, var);
|
lua_getglobal_old(L, var);
|
||||||
lua_type(L, -1)
|
lua_type(L, -1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
pub unsafe fn lua_gettable(L: *mut lua_State, idx: c_int) -> c_int {
|
pub unsafe fn lua_gettable(L: *mut lua_State, idx: c_int) -> c_int {
|
||||||
lua_gettable_old(L, idx);
|
lua_gettable_old(L, idx);
|
||||||
lua_type(L, -1)
|
lua_type(L, -1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
pub unsafe fn lua_getfield(L: *mut lua_State, idx: c_int, k: *const c_char) -> c_int {
|
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_getfield_old(L, idx, k);
|
||||||
lua_type(L, -1)
|
lua_type(L, -1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
pub unsafe fn lua_geti(L: *mut lua_State, mut idx: c_int, n: lua_Integer) -> c_int {
|
pub unsafe fn lua_geti(L: *mut lua_State, mut idx: c_int, n: lua_Integer) -> c_int {
|
||||||
idx = lua_absindex(L, idx);
|
idx = lua_absindex(L, idx);
|
||||||
lua_pushinteger(L, n);
|
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
|
// A new version which returns c_int
|
||||||
|
#[inline(always)]
|
||||||
pub unsafe fn lua_rawget(L: *mut lua_State, idx: c_int) -> c_int {
|
pub unsafe fn lua_rawget(L: *mut lua_State, idx: c_int) -> c_int {
|
||||||
lua_rawget_old(L, idx);
|
lua_rawget_old(L, idx);
|
||||||
lua_type(L, -1)
|
lua_type(L, -1)
|
||||||
}
|
}
|
||||||
|
|
||||||
// A new version which returns c_int
|
// 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 {
|
pub unsafe fn lua_rawgeti(L: *mut lua_State, idx: c_int, n: lua_Integer) -> c_int {
|
||||||
lua_rawgeti_old(L, idx, n);
|
lua_rawgeti_old(L, idx, n);
|
||||||
lua_type(L, -1)
|
lua_type(L, -1)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(any(feature = "lua51", feature = "luajit"))]
|
#[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 {
|
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);
|
let abs_i = lua_absindex(L, idx);
|
||||||
lua_pushlightuserdata(L, p as *mut c_void);
|
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")]
|
#[cfg(feature = "lua52")]
|
||||||
|
#[inline(always)]
|
||||||
pub unsafe fn lua_rawgetp(L: *mut lua_State, idx: c_int, p: *const c_void) -> c_int {
|
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_rawgetp_old(L, idx, p);
|
||||||
lua_type(L, -1)
|
lua_type(L, -1)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(any(feature = "lua51", feature = "luajit"))]
|
#[cfg(any(feature = "lua51", feature = "luajit"))]
|
||||||
|
#[inline(always)]
|
||||||
pub unsafe fn lua_getuservalue(L: *mut lua_State, idx: c_int) -> c_int {
|
pub unsafe fn lua_getuservalue(L: *mut lua_State, idx: c_int) -> c_int {
|
||||||
lua_getfenv(L, idx);
|
lua_getfenv(L, idx);
|
||||||
lua_type(L, -1)
|
lua_type(L, -1)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "lua52")]
|
#[cfg(feature = "lua52")]
|
||||||
|
#[inline(always)]
|
||||||
pub unsafe fn lua_getuservalue(L: *mut lua_State, idx: c_int) -> c_int {
|
pub unsafe fn lua_getuservalue(L: *mut lua_State, idx: c_int) -> c_int {
|
||||||
lua_getuservalue_old(L, idx);
|
lua_getuservalue_old(L, idx);
|
||||||
lua_type(L, -1)
|
lua_type(L, -1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
pub unsafe fn lua_seti(L: *mut lua_State, mut idx: c_int, n: lua_Integer) {
|
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"));
|
luaL_checkstack(L, 1, cstr!("not enough stack slots available"));
|
||||||
idx = lua_absindex(L, idx);
|
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"))]
|
#[cfg(any(feature = "lua51", feature = "luajit"))]
|
||||||
|
#[inline(always)]
|
||||||
pub unsafe fn lua_rawsetp(L: *mut lua_State, idx: c_int, p: *const c_void) {
|
pub unsafe fn lua_rawsetp(L: *mut lua_State, idx: c_int, p: *const c_void) {
|
||||||
let abs_i = lua_absindex(L, idx);
|
let abs_i = lua_absindex(L, idx);
|
||||||
luaL_checkstack(L, 1, cstr!("not enough stack slots"));
|
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"))]
|
#[cfg(any(feature = "lua51", feature = "luajit"))]
|
||||||
|
#[inline(always)]
|
||||||
pub unsafe fn lua_setuservalue(L: *mut lua_State, idx: c_int) {
|
pub unsafe fn lua_setuservalue(L: *mut lua_State, idx: c_int) {
|
||||||
luaL_checktype(L, -1, lua::LUA_TTABLE);
|
luaL_checktype(L, -1, lua::LUA_TTABLE);
|
||||||
lua_setfenv(L, idx);
|
lua_setfenv(L, idx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
pub unsafe fn lua_dump(
|
pub unsafe fn lua_dump(
|
||||||
L: *mut lua_State,
|
L: *mut lua_State,
|
||||||
writer: lua_Writer,
|
writer: lua_Writer,
|
||||||
@@ -471,11 +496,13 @@ pub unsafe fn lua_dump(
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(any(feature = "lua51", feature = "luajit"))]
|
#[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 {
|
pub unsafe fn lua_resume(L: *mut lua_State, _from: *mut lua_State, narg: c_int) -> c_int {
|
||||||
lua_resume_old(L, narg)
|
lua_resume_old(L, narg)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(any(feature = "lua51", feature = "luajit"))]
|
#[cfg(any(feature = "lua51", feature = "luajit"))]
|
||||||
|
#[inline(always)]
|
||||||
pub unsafe fn lua_len(L: *mut lua_State, idx: c_int) {
|
pub unsafe fn lua_len(L: *mut lua_State, idx: c_int) {
|
||||||
match lua_type(L, idx) {
|
match lua_type(L, idx) {
|
||||||
lua::LUA_TSTRING => {
|
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 {
|
pub unsafe fn lua_stringtonumber(L: *mut lua_State, s: *const c_char) -> usize {
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
@@ -510,6 +538,7 @@ pub unsafe fn lua_stringtonumber(L: *mut lua_State, s: *const c_char) -> usize {
|
|||||||
0
|
0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::branches_sharing_code)]
|
||||||
pub unsafe fn lua_getextraspace(L: *mut lua_State) -> *mut c_void {
|
pub unsafe fn lua_getextraspace(L: *mut lua_State) -> *mut c_void {
|
||||||
use super::glue::LUA_EXTRASPACE;
|
use super::glue::LUA_EXTRASPACE;
|
||||||
|
|
||||||
@@ -574,6 +603,7 @@ pub unsafe fn lua_pushglobaltable(L: *mut lua_State) {
|
|||||||
//
|
//
|
||||||
|
|
||||||
#[cfg(any(feature = "lua51", feature = "luajit"))]
|
#[cfg(any(feature = "lua51", feature = "luajit"))]
|
||||||
|
#[inline(always)]
|
||||||
pub unsafe fn luaL_checkstack(L: *mut lua_State, sz: c_int, msg: *const c_char) {
|
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 lua_checkstack(L, sz + lua::LUA_MINSTACK) == 0 {
|
||||||
if !msg.is_null() {
|
if !msg.is_null() {
|
||||||
@@ -589,6 +619,7 @@ pub unsafe fn luaL_checkversion(_L: *mut lua_State) {
|
|||||||
// Void
|
// Void
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
pub unsafe fn luaL_getmetafield(L: *mut lua_State, obj: c_int, e: *const c_char) -> c_int {
|
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 {
|
if luaL_getmetafield_old(L, obj, e) != 0 {
|
||||||
lua_type(L, -1)
|
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 {
|
pub unsafe fn luaL_newmetatable(L: *mut lua_State, tname: *const c_char) -> c_int {
|
||||||
if luaL_newmetatable_old(L, tname) != 0 {
|
if luaL_newmetatable_old(L, tname) != 0 {
|
||||||
lua_pushstring(L, tname);
|
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"))]
|
#[cfg(any(feature = "lua51", feature = "luajit"))]
|
||||||
|
#[inline(always)]
|
||||||
pub unsafe fn luaL_loadbufferx(
|
pub unsafe fn luaL_loadbufferx(
|
||||||
L: *mut lua_State,
|
L: *mut lua_State,
|
||||||
buff: *const c_char,
|
buff: *const c_char,
|
||||||
@@ -627,6 +660,7 @@ pub unsafe fn luaL_loadbufferx(
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(any(feature = "lua51", feature = "luajit"))]
|
#[cfg(any(feature = "lua51", feature = "luajit"))]
|
||||||
|
#[inline(always)]
|
||||||
pub unsafe fn luaL_len(L: *mut lua_State, idx: c_int) -> lua_Integer {
|
pub unsafe fn luaL_len(L: *mut lua_State, idx: c_int) -> lua_Integer {
|
||||||
let mut isnum = 0;
|
let mut isnum = 0;
|
||||||
luaL_checkstack(L, 1, cstr!("not enough stack slots"));
|
luaL_checkstack(L, 1, cstr!("not enough stack slots"));
|
||||||
@@ -646,7 +680,7 @@ pub unsafe fn luaL_traceback(
|
|||||||
msg: *const c_char,
|
msg: *const c_char,
|
||||||
mut level: c_int,
|
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 top = lua_gettop(L);
|
||||||
let numlevels = compat53_countlevels(L1);
|
let numlevels = compat53_countlevels(L1);
|
||||||
let mark = if numlevels > COMPAT53_LEVELS1 + COMPAT53_LEVELS2 {
|
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
|
level = numlevels - COMPAT53_LEVELS2; // and skip to last ones
|
||||||
} else {
|
} else {
|
||||||
lua_getinfo(L1, cstr!("Slnt"), &mut ar);
|
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 {
|
if ar.currentline > 0 {
|
||||||
lua_pushfstring(L, cstr!("%d:"), ar.currentline);
|
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"))]
|
#[cfg(any(feature = "lua51", feature = "luajit"))]
|
||||||
|
#[inline(always)]
|
||||||
pub unsafe fn luaL_setmetatable(L: *mut lua_State, tname: *const c_char) {
|
pub unsafe fn luaL_setmetatable(L: *mut lua_State, tname: *const c_char) {
|
||||||
luaL_checkstack(L, 1, cstr!("not enough stack slots"));
|
luaL_checkstack(L, 1, cstr!("not enough stack slots"));
|
||||||
luaL_getmetatable(L, tname);
|
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"))]
|
#[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 {
|
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);
|
let mut p = lua_touserdata(L, i);
|
||||||
luaL_checkstack(L, 2, cstr!("not enough stack slots"));
|
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"))]
|
#[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) {
|
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"));
|
luaL_checkstack(L, nup + 1, cstr!("too many upvalues"));
|
||||||
while !(*l).name.is_null() {
|
while !(*l).name.is_null() {
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
// The MIT License (MIT)
|
// The MIT License (MIT)
|
||||||
//
|
//
|
||||||
// Copyright (c) 2019-2020 A. Orlenko
|
// Copyright (c) 2019-2021 A. Orlenko
|
||||||
// Copyright (c) 2014 J.C. Moyer
|
// Copyright (c) 2014 J.C. Moyer
|
||||||
//
|
//
|
||||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
// The MIT License (MIT)
|
// The MIT License (MIT)
|
||||||
//
|
//
|
||||||
// Copyright (c) 2019-2020 A. Orlenko
|
// Copyright (c) 2019-2021 A. Orlenko
|
||||||
// Copyright (c) 2014 J.C. Moyer
|
// Copyright (c) 2014 J.C. Moyer
|
||||||
//
|
//
|
||||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
|||||||
+26
-4
@@ -1,6 +1,6 @@
|
|||||||
// The MIT License (MIT)
|
// The MIT License (MIT)
|
||||||
//
|
//
|
||||||
// Copyright (c) 2019-2020 A. Orlenko
|
// Copyright (c) 2019-2021 A. Orlenko
|
||||||
// Copyright (c) 2014 J.C. Moyer
|
// Copyright (c) 2014 J.C. Moyer
|
||||||
//
|
//
|
||||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
@@ -80,6 +80,7 @@ pub const LUA_ERRERR: c_int = 5;
|
|||||||
#[cfg(any(feature = "lua53", feature = "lua52"))]
|
#[cfg(any(feature = "lua53", feature = "lua52"))]
|
||||||
pub const LUA_ERRERR: c_int = 6;
|
pub const LUA_ERRERR: c_int = 6;
|
||||||
|
|
||||||
|
/// A raw Lua state associated with a thread.
|
||||||
pub type lua_State = c_void;
|
pub type lua_State = c_void;
|
||||||
|
|
||||||
// basic types
|
// basic types
|
||||||
@@ -111,20 +112,24 @@ pub const LUA_RIDX_GLOBALS: lua_Integer = 2;
|
|||||||
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
|
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
|
||||||
pub const LUA_RIDX_LAST: lua_Integer = LUA_RIDX_GLOBALS;
|
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`.
|
/// A Lua number, usually equivalent to `f64`.
|
||||||
pub type lua_Number = luaconf::LUA_NUMBER;
|
pub type lua_Number = luaconf::LUA_NUMBER;
|
||||||
|
|
||||||
/// A Lua integer, usually equivalent to `i64`.
|
/// A Lua integer, usually equivalent to `i64`.
|
||||||
pub type lua_Integer = luaconf::LUA_INTEGER;
|
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;
|
pub type lua_Unsigned = luaconf::LUA_UNSIGNED;
|
||||||
|
|
||||||
// type for continuation-function contexts
|
// type for continuation-function contexts
|
||||||
#[cfg(any(feature = "lua54", feature = "lua53"))]
|
#[cfg(any(feature = "lua54", feature = "lua53"))]
|
||||||
pub type lua_KContext = luaconf::LUA_KCONTEXT;
|
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;
|
pub type lua_CFunction = unsafe extern "C" fn(L: *mut lua_State) -> c_int;
|
||||||
|
|
||||||
// Type for continuation functions
|
// 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_newstate(f: lua_Alloc, ud: *mut c_void) -> *mut lua_State;
|
||||||
pub fn lua_close(L: *mut lua_State);
|
pub fn lua_close(L: *mut lua_State);
|
||||||
pub fn lua_newthread(L: *mut lua_State) -> *mut lua_State;
|
pub fn lua_newthread(L: *mut lua_State) -> *mut lua_State;
|
||||||
|
|
||||||
#[cfg(feature = "lua54")]
|
#[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;
|
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;
|
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
|
// Comparison and arithmetic functions
|
||||||
pub const LUA_OPADD: c_int = 0;
|
pub const LUA_OPADD: c_int = 0;
|
||||||
pub const LUA_OPSUB: c_int = 1;
|
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 = "lua51",
|
||||||
feature = "luajit"
|
feature = "luajit"
|
||||||
))]
|
))]
|
||||||
|
#[inline(always)]
|
||||||
pub unsafe fn lua_resume(
|
pub unsafe fn lua_resume(
|
||||||
L: *mut lua_State,
|
L: *mut lua_State,
|
||||||
from: *mut lua_State,
|
from: *mut lua_State,
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
// The MIT License (MIT)
|
// The MIT License (MIT)
|
||||||
//
|
//
|
||||||
// Copyright (c) 2019-2020 A. Orlenko
|
// Copyright (c) 2019-2021 A. Orlenko
|
||||||
// Copyright (c) 2014 J.C. Moyer
|
// Copyright (c) 2014 J.C. Moyer
|
||||||
//
|
//
|
||||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
// The MIT License (MIT)
|
// The MIT License (MIT)
|
||||||
//
|
//
|
||||||
// Copyright (c) 2019-2020 A. Orlenko
|
// Copyright (c) 2019-2021 A. Orlenko
|
||||||
// Copyright (c) 2014 J.C. Moyer
|
// Copyright (c) 2014 J.C. Moyer
|
||||||
//
|
//
|
||||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
|||||||
+31
-6
@@ -1,6 +1,6 @@
|
|||||||
// The MIT License (MIT)
|
// The MIT License (MIT)
|
||||||
//
|
//
|
||||||
// Copyright (c) 2019-2020 A. Orlenko
|
// Copyright (c) 2019-2021 A. Orlenko
|
||||||
// Copyright (c) 2014 J.C. Moyer
|
// Copyright (c) 2014 J.C. Moyer
|
||||||
//
|
//
|
||||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
@@ -160,8 +160,8 @@ pub use self::lua::{
|
|||||||
|
|
||||||
#[cfg(feature = "lua54")]
|
#[cfg(feature = "lua54")]
|
||||||
pub use self::lua::{
|
pub use self::lua::{
|
||||||
lua_getiuservalue, lua_newuserdatauv, lua_resetthread, lua_setcstacklimit, lua_setiuservalue,
|
lua_getiuservalue, lua_newuserdatauv, lua_setcstacklimit, lua_setiuservalue, lua_setwarnf,
|
||||||
lua_setwarnf, lua_toclose, lua_warning,
|
lua_toclose, lua_warning,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(any(feature = "lua54", feature = "lua53"))]
|
#[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"))]
|
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
|
||||||
pub use self::lua::{lua_callk, lua_pcallk, lua_upvalueid, lua_upvaluejoin, lua_yieldk};
|
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
|
// auxiliary library types
|
||||||
pub use self::lauxlib::luaL_Reg;
|
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_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_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_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"))]
|
#[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
|
// Not actually defined in lua.h / luaconf.h
|
||||||
pub const LUA_MAX_UPVALUES: c_int = 255;
|
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(
|
#[cfg(all(any(
|
||||||
target_arch = "x86",
|
target_arch = "x86",
|
||||||
target_arch = "arm",
|
target_arch = "arm",
|
||||||
target_arch = "mips",
|
target_arch = "mips",
|
||||||
target_arch = "powerpc",
|
target_arch = "powerpc",
|
||||||
target_arch = "powerpc64",
|
target_arch = "powerpc64",
|
||||||
|
target_arch = "sparc",
|
||||||
target_arch = "asmjs",
|
target_arch = "asmjs",
|
||||||
target_arch = "wasm32",
|
target_arch = "wasm32",
|
||||||
target_arch = "hexagon"
|
target_arch = "hexagon",
|
||||||
|
target_arch = "riscv32"
|
||||||
)))]
|
)))]
|
||||||
pub const SYS_MIN_ALIGN: usize = 8;
|
pub const SYS_MIN_ALIGN: usize = 8;
|
||||||
#[cfg(all(any(
|
#[cfg(all(any(
|
||||||
@@ -275,6 +281,25 @@ pub const SYS_MIN_ALIGN: usize = 8;
|
|||||||
)))]
|
)))]
|
||||||
pub const SYS_MIN_ALIGN: usize = 16;
|
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(unused_imports, dead_code, non_camel_case_types)]
|
||||||
#[allow(clippy::unreadable_literal)]
|
#[allow(clippy::unreadable_literal)]
|
||||||
mod glue {
|
mod glue {
|
||||||
|
|||||||
+12
-16
@@ -1,12 +1,11 @@
|
|||||||
use std::os::raw::{c_int, c_void};
|
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::error::{Error, Result};
|
||||||
use crate::ffi;
|
use crate::ffi;
|
||||||
use crate::types::LuaRef;
|
use crate::types::LuaRef;
|
||||||
use crate::util::{
|
use crate::util::{assert_stack, check_stack, error_traceback, pop_error, StackGuard};
|
||||||
assert_stack, check_stack, error_traceback, pop_error, protect_lua_closure, StackGuard,
|
|
||||||
};
|
|
||||||
use crate::value::{FromLuaMulti, MultiValue, ToLuaMulti};
|
use crate::value::{FromLuaMulti, MultiValue, ToLuaMulti};
|
||||||
|
|
||||||
#[cfg(feature = "async")]
|
#[cfg(feature = "async")]
|
||||||
@@ -92,7 +91,7 @@ impl<'lua> Function<'lua> {
|
|||||||
/// Returns a Feature that, when polled, calls `self`, passing `args` as function arguments,
|
/// Returns a Feature that, when polled, calls `self`, passing `args` as function arguments,
|
||||||
/// and drives the execution.
|
/// and drives the execution.
|
||||||
///
|
///
|
||||||
/// Internaly it wraps the function to an [`AsyncThread`].
|
/// Internally it wraps the function to an [`AsyncThread`].
|
||||||
///
|
///
|
||||||
/// Requires `feature = "async"`
|
/// Requires `feature = "async"`
|
||||||
///
|
///
|
||||||
@@ -193,14 +192,14 @@ impl<'lua> Function<'lua> {
|
|||||||
unsafe {
|
unsafe {
|
||||||
let _sg = StackGuard::new(lua.state);
|
let _sg = StackGuard::new(lua.state);
|
||||||
check_stack(lua.state, nargs + 5)?;
|
check_stack(lua.state, nargs + 5)?;
|
||||||
|
|
||||||
lua.push_ref(&self.0);
|
lua.push_ref(&self.0);
|
||||||
ffi::lua_pushinteger(lua.state, nargs as ffi::lua_Integer);
|
ffi::lua_pushinteger(lua.state, nargs as ffi::lua_Integer);
|
||||||
for arg in args {
|
for arg in args {
|
||||||
lua.push_value(arg)?;
|
lua.push_value(arg)?;
|
||||||
}
|
}
|
||||||
|
protect_lua!(lua.state, nargs + 2, 1, fn(state) {
|
||||||
protect_lua_closure(lua.state, nargs + 2, 1, |state| {
|
ffi::lua_pushcclosure(state, bind_call_impl, ffi::lua_gettop(state));
|
||||||
ffi::lua_pushcclosure(state, bind_call_impl, nargs + 2);
|
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
Ok(Function(lua.pop_ref()))
|
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
|
/// If `strip` is true, the binary representation may not include all debug information
|
||||||
/// about the function, to save space.
|
/// 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(
|
unsafe extern "C" fn writer(
|
||||||
_state: *mut ffi::lua_State,
|
_state: *mut ffi::lua_State,
|
||||||
buf: *const c_void,
|
buf: *const c_void,
|
||||||
@@ -229,18 +228,15 @@ impl<'lua> Function<'lua> {
|
|||||||
unsafe {
|
unsafe {
|
||||||
let _sg = StackGuard::new(lua.state);
|
let _sg = StackGuard::new(lua.state);
|
||||||
assert_stack(lua.state, 1);
|
assert_stack(lua.state, 1);
|
||||||
|
|
||||||
lua.push_ref(&self.0);
|
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 };
|
let strip = if strip { 1 } else { 0 };
|
||||||
ffi::lua_dump(
|
ffi::lua_dump(lua.state, writer, data_ptr, strip);
|
||||||
lua.state,
|
|
||||||
writer,
|
|
||||||
&mut data as *mut Vec<u8> as *mut c_void,
|
|
||||||
strip,
|
|
||||||
);
|
|
||||||
ffi::lua_pop(lua.state, 1);
|
ffi::lua_pop(lua.state, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(data)
|
data
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+96
-6
@@ -1,5 +1,6 @@
|
|||||||
use std::ffi::CStr;
|
use std::ffi::CStr;
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
|
use std::ops::{BitOr, BitOrAssign};
|
||||||
use std::os::raw::{c_char, c_int};
|
use std::os::raw::{c_char, c_int};
|
||||||
|
|
||||||
use crate::ffi::{self, lua_Debug, lua_State};
|
use crate::ffi::{self, lua_Debug, lua_State};
|
||||||
@@ -9,9 +10,9 @@ use crate::util::callback_error;
|
|||||||
/// Contains information about currently executing Lua code.
|
/// Contains information about currently executing Lua code.
|
||||||
///
|
///
|
||||||
/// The `Debug` structure is provided as a parameter to the hook function set with
|
/// 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::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
|
/// 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].
|
/// found in the [Lua 5.3 documentation][lua_doc].
|
||||||
///
|
///
|
||||||
/// [lua_doc]: https://www.lua.org/manual/5.3/manual.html#lua_Debug
|
/// [lua_doc]: https://www.lua.org/manual/5.3/manual.html#lua_Debug
|
||||||
/// [`Lua::set_hook`]: struct.Lua.html#method.set_hook
|
/// [`Lua::set_hook`]: struct.Lua.html#method.set_hook
|
||||||
@@ -23,6 +24,25 @@ pub struct Debug<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> 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.
|
/// Corresponds to the `n` what mask.
|
||||||
pub fn names(&self) -> DebugNames<'a> {
|
pub fn names(&self) -> DebugNames<'a> {
|
||||||
unsafe {
|
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)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct DebugNames<'a> {
|
pub struct DebugNames<'a> {
|
||||||
pub name: Option<&'a [u8]>,
|
pub name: Option<&'a [u8]>,
|
||||||
@@ -130,7 +160,7 @@ pub struct HookTriggers {
|
|||||||
pub on_returns: bool,
|
pub on_returns: bool,
|
||||||
/// Before executing a new line, or returning from a function call.
|
/// Before executing a new line, or returning from a function call.
|
||||||
pub every_line: bool,
|
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.
|
/// `count` is the number of VM instructions to execute before calling the hook.
|
||||||
///
|
///
|
||||||
/// # Performance
|
/// # Performance
|
||||||
@@ -140,6 +170,46 @@ pub struct HookTriggers {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl 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`.
|
// Compute the mask to pass to `lua_sethook`.
|
||||||
pub(crate) fn mask(&self) -> c_int {
|
pub(crate) fn mask(&self) -> c_int {
|
||||||
let mut mask: c_int = 0;
|
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) {
|
pub(crate) unsafe extern "C" fn hook_proc(state: *mut lua_State, ar: *mut lua_Debug) {
|
||||||
callback_error(state, |_| {
|
callback_error(state, |_| {
|
||||||
let debug = Debug {
|
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,
|
_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");
|
let hook_cb = mlua_expect!(lua.hook_callback(), "no hook callback set in hook_proc");
|
||||||
|
|
||||||
#[allow(clippy::match_wild_err_arm)]
|
#[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(())
|
Ok(())
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe fn ptr_to_str<'a>(input: *const c_char) -> Option<&'a [u8]> {
|
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.
|
//! 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.
|
//! Methods and operators to be used from Lua can be added using the [`UserDataMethods`] API.
|
||||||
|
//! Fields are supported using the [`UserDataFields`] API.
|
||||||
//!
|
//!
|
||||||
//! # Serde support
|
//! # Serde support
|
||||||
//!
|
//!
|
||||||
@@ -50,8 +51,8 @@
|
|||||||
//!
|
//!
|
||||||
//! [Lua programming language]: https://www.lua.org/
|
//! [Lua programming language]: https://www.lua.org/
|
||||||
//! [`Lua`]: struct.Lua.html
|
//! [`Lua`]: struct.Lua.html
|
||||||
//! [executing]: struct.Lua.html#method.exec
|
//! [executing]: struct.Chunk.html#method.exec
|
||||||
//! [evaluating]: struct.Lua.html#method.eval
|
//! [evaluating]: struct.Chunk.html#method.eval
|
||||||
//! [globals]: struct.Lua.html#method.globals
|
//! [globals]: struct.Lua.html#method.globals
|
||||||
//! [`ToLua`]: trait.ToLua.html
|
//! [`ToLua`]: trait.ToLua.html
|
||||||
//! [`FromLua`]: trait.FromLua.html
|
//! [`FromLua`]: trait.FromLua.html
|
||||||
@@ -59,6 +60,7 @@
|
|||||||
//! [`FromLuaMulti`]: trait.FromLuaMulti.html
|
//! [`FromLuaMulti`]: trait.FromLuaMulti.html
|
||||||
//! [`Function`]: struct.Function.html
|
//! [`Function`]: struct.Function.html
|
||||||
//! [`UserData`]: trait.UserData.html
|
//! [`UserData`]: trait.UserData.html
|
||||||
|
//! [`UserDataFields`]: trait.UserDataFields.html
|
||||||
//! [`UserDataMethods`]: trait.UserDataMethods.html
|
//! [`UserDataMethods`]: trait.UserDataMethods.html
|
||||||
//! [`LuaSerdeExt`]: serde/trait.LuaSerdeExt.html
|
//! [`LuaSerdeExt`]: serde/trait.LuaSerdeExt.html
|
||||||
//! [`Value`]: enum.Value.html
|
//! [`Value`]: enum.Value.html
|
||||||
@@ -70,7 +72,7 @@
|
|||||||
//! [`serde::Deserialize`]: https://docs.serde.rs/serde/de/trait.Deserialize.html
|
//! [`serde::Deserialize`]: https://docs.serde.rs/serde/de/trait.Deserialize.html
|
||||||
|
|
||||||
// mlua types in rustdoc of other crates get linked to here.
|
// mlua types in rustdoc of other crates get linked to here.
|
||||||
#![doc(html_root_url = "https://docs.rs/mlua/0.5.1")]
|
#![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*
|
// Deny warnings inside doc tests / examples. When this isn't present, rustdoc doesn't show *any*
|
||||||
// warnings at all.
|
// warnings at all.
|
||||||
#![doc(test(attr(deny(warnings))))]
|
#![doc(test(attr(deny(warnings))))]
|
||||||
@@ -96,12 +98,12 @@ mod userdata;
|
|||||||
mod util;
|
mod util;
|
||||||
mod value;
|
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::error::{Error, ExternalError, ExternalResult, Result};
|
||||||
pub use crate::function::Function;
|
pub use crate::function::Function;
|
||||||
pub use crate::hook::{Debug, DebugNames, DebugSource, DebugStack, HookTriggers};
|
pub use crate::hook::{Debug, DebugEvent, DebugNames, DebugSource, DebugStack, HookTriggers};
|
||||||
pub use crate::lua::{Chunk, ChunkMode, GCMode, Lua};
|
pub use crate::lua::{AsChunk, Chunk, ChunkMode, GCMode, Lua, LuaOptions};
|
||||||
pub use crate::multi::Variadic;
|
pub use crate::multi::Variadic;
|
||||||
pub use crate::scope::Scope;
|
pub use crate::scope::Scope;
|
||||||
pub use crate::stdlib::StdLib;
|
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::table::{Table, TableExt, TablePairs, TableSequence};
|
||||||
pub use crate::thread::{Thread, ThreadStatus};
|
pub use crate::thread::{Thread, ThreadStatus};
|
||||||
pub use crate::types::{Integer, LightUserData, Number, RegistryKey};
|
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};
|
pub use crate::value::{FromLua, FromLuaMulti, MultiValue, Nil, ToLua, ToLuaMulti, Value};
|
||||||
|
|
||||||
#[cfg(feature = "async")]
|
#[cfg(feature = "async")]
|
||||||
pub use crate::thread::AsyncThread;
|
pub use crate::thread::AsyncThread;
|
||||||
|
|
||||||
#[cfg(feature = "serialize")]
|
#[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;
|
pub mod prelude;
|
||||||
#[cfg(feature = "serialize")]
|
#[cfg(feature = "serialize")]
|
||||||
#[cfg_attr(docsrs, doc(cfg(feature = "serialize")))]
|
#[cfg_attr(docsrs, doc(cfg(feature = "serialize")))]
|
||||||
pub mod serde;
|
pub mod serde;
|
||||||
|
|
||||||
// Re-export #[mlua_derive::lua_module].
|
#[cfg(any(feature = "mlua_derive"))]
|
||||||
#[cfg(feature = "mlua_derive")]
|
|
||||||
#[allow(unused_imports)]
|
#[allow(unused_imports)]
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate mlua_derive;
|
extern crate mlua_derive;
|
||||||
#[cfg(feature = "mlua_derive")]
|
|
||||||
#[doc(hidden)]
|
/// Create a type that implements [`AsChunk`] and can capture Rust variables.
|
||||||
pub use mlua_derive::*;
|
///
|
||||||
|
/// 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;
|
||||||
|
|||||||
+1222
-485
File diff suppressed because it is too large
Load Diff
+19
-4
@@ -17,19 +17,19 @@ macro_rules! cstr {
|
|||||||
|
|
||||||
macro_rules! mlua_panic {
|
macro_rules! mlua_panic {
|
||||||
($msg:expr) => {
|
($msg:expr) => {
|
||||||
panic!(bug_msg!($msg));
|
panic!(bug_msg!($msg))
|
||||||
};
|
};
|
||||||
|
|
||||||
($msg:expr,) => {
|
($msg:expr,) => {
|
||||||
mlua_panic!($msg);
|
mlua_panic!($msg)
|
||||||
};
|
};
|
||||||
|
|
||||||
($msg:expr, $($arg:expr),+) => {
|
($msg:expr, $($arg:expr),+) => {
|
||||||
panic!(bug_msg!($msg), $($arg),+);
|
panic!(bug_msg!($msg), $($arg),+)
|
||||||
};
|
};
|
||||||
|
|
||||||
($msg:expr, $($arg:expr),+,) => {
|
($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");
|
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)
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|||||||
+3
-1
@@ -1,3 +1,5 @@
|
|||||||
|
#![allow(clippy::wrong_self_convention)]
|
||||||
|
|
||||||
use std::iter::FromIterator;
|
use std::iter::FromIterator;
|
||||||
use std::ops::{Deref, DerefMut};
|
use std::ops::{Deref, DerefMut};
|
||||||
use std::result::Result as StdResult;
|
use std::result::Result as StdResult;
|
||||||
@@ -34,7 +36,7 @@ impl<'lua, T: ToLua<'lua>> ToLuaMulti<'lua> for T {
|
|||||||
|
|
||||||
impl<'lua, T: FromLua<'lua>> FromLuaMulti<'lua> for T {
|
impl<'lua, T: FromLua<'lua>> FromLuaMulti<'lua> for T {
|
||||||
fn from_lua_multi(mut values: MultiValue<'lua>, lua: &'lua Lua) -> Result<Self> {
|
fn from_lua_multi(mut values: MultiValue<'lua>, lua: &'lua Lua) -> Result<Self> {
|
||||||
Ok(T::from_lua(values.pop_front().unwrap_or(Nil), lua)?)
|
T::from_lua(values.pop_front().unwrap_or(Nil), lua)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+9
-1
@@ -4,13 +4,21 @@ pub use crate::{
|
|||||||
AnyUserData as LuaAnyUserData, Chunk as LuaChunk, Error as LuaError,
|
AnyUserData as LuaAnyUserData, Chunk as LuaChunk, Error as LuaError,
|
||||||
ExternalError as LuaExternalError, ExternalResult as LuaExternalResult, FromLua, FromLuaMulti,
|
ExternalError as LuaExternalError, ExternalResult as LuaExternalResult, FromLua, FromLuaMulti,
|
||||||
Function as LuaFunction, GCMode as LuaGCMode, Integer as LuaInteger,
|
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,
|
MultiValue as LuaMultiValue, Nil as LuaNil, Number as LuaNumber, RegistryKey as LuaRegistryKey,
|
||||||
Result as LuaResult, String as LuaString, Table as LuaTable, TableExt as LuaTableExt,
|
Result as LuaResult, String as LuaString, Table as LuaTable, TableExt as LuaTableExt,
|
||||||
TablePairs as LuaTablePairs, TableSequence as LuaTableSequence, Thread as LuaThread,
|
TablePairs as LuaTablePairs, TableSequence as LuaTableSequence, Thread as LuaThread,
|
||||||
ThreadStatus as LuaThreadStatus, ToLua, ToLuaMulti, UserData as LuaUserData,
|
ThreadStatus as LuaThreadStatus, ToLua, ToLuaMulti, UserData as LuaUserData,
|
||||||
|
UserDataFields as LuaUserDataFields, UserDataMetatable as LuaUserDataMetatable,
|
||||||
UserDataMethods as LuaUserDataMethods, Value as LuaValue,
|
UserDataMethods as LuaUserDataMethods, Value as LuaValue,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(feature = "async")]
|
#[cfg(feature = "async")]
|
||||||
pub use crate::AsyncThread as LuaAsyncThread;
|
pub use crate::AsyncThread as LuaAsyncThread;
|
||||||
|
|
||||||
|
#[cfg(feature = "serialize")]
|
||||||
|
#[doc(inline)]
|
||||||
|
pub use crate::{
|
||||||
|
DeserializeOptions as LuaDeserializeOptions, LuaSerdeExt,
|
||||||
|
SerializeOptions as LuaSerializeOptions,
|
||||||
|
};
|
||||||
|
|||||||
+324
-130
@@ -1,8 +1,9 @@
|
|||||||
use std::any::Any;
|
use std::any::Any;
|
||||||
use std::cell::{Cell, Ref, RefCell, RefMut};
|
use std::cell::{Cell, RefCell};
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
use std::mem;
|
use std::mem;
|
||||||
use std::os::raw::c_void;
|
use std::os::raw::{c_int, c_void};
|
||||||
|
use std::ptr;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
#[cfg(feature = "serialize")]
|
#[cfg(feature = "serialize")]
|
||||||
@@ -12,20 +13,21 @@ use crate::error::{Error, Result};
|
|||||||
use crate::ffi;
|
use crate::ffi;
|
||||||
use crate::function::Function;
|
use crate::function::Function;
|
||||||
use crate::lua::Lua;
|
use crate::lua::Lua;
|
||||||
use crate::types::{Callback, LuaRef, MaybeSend, UserDataCell};
|
use crate::types::{Callback, CallbackUpvalue, LuaRef, MaybeSend};
|
||||||
use crate::userdata::{AnyUserData, MetaMethod, UserData, UserDataMethods, UserDataWrapped};
|
use crate::userdata::{
|
||||||
|
AnyUserData, MetaMethod, UserData, UserDataCell, UserDataFields, UserDataMethods,
|
||||||
|
};
|
||||||
use crate::util::{
|
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,
|
take_userdata, StackGuard,
|
||||||
};
|
};
|
||||||
use crate::value::{FromLuaMulti, MultiValue, ToLuaMulti, Value};
|
use crate::value::{FromLua, FromLuaMulti, MultiValue, ToLua, ToLuaMulti, Value};
|
||||||
|
|
||||||
#[cfg(feature = "async")]
|
#[cfg(feature = "async")]
|
||||||
use {
|
use {
|
||||||
crate::types::AsyncCallback,
|
crate::types::{AsyncCallback, AsyncCallbackUpvalue, AsyncPollUpvalue},
|
||||||
futures_core::future::Future,
|
futures_core::future::Future,
|
||||||
futures_util::future::{self, TryFutureExt},
|
futures_util::future::{self, TryFutureExt},
|
||||||
std::os::raw::c_char,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Constructed by the [`Lua::scope`] method, allows temporarily creating Lua userdata and
|
/// Constructed by the [`Lua::scope`] method, allows temporarily creating Lua userdata and
|
||||||
@@ -34,13 +36,14 @@ use {
|
|||||||
/// See [`Lua::scope`] for more details.
|
/// See [`Lua::scope`] for more details.
|
||||||
///
|
///
|
||||||
/// [`Lua::scope`]: struct.Lua.html#method.scope
|
/// [`Lua::scope`]: struct.Lua.html#method.scope
|
||||||
#[allow(clippy::type_complexity)]
|
|
||||||
pub struct Scope<'lua, 'scope> {
|
pub struct Scope<'lua, 'scope> {
|
||||||
lua: &'lua Lua,
|
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 ()>>,
|
_scope_invariant: PhantomData<Cell<&'scope ()>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type DestructorCallback<'lua> = Box<dyn Fn(LuaRef<'lua>) -> Vec<Box<dyn Any>> + 'lua>;
|
||||||
|
|
||||||
impl<'lua, 'scope> Scope<'lua, 'scope> {
|
impl<'lua, 'scope> Scope<'lua, 'scope> {
|
||||||
pub(crate) fn new(lua: &'lua Lua) -> Scope<'lua, 'scope> {
|
pub(crate) fn new(lua: &'lua Lua) -> Scope<'lua, 'scope> {
|
||||||
Scope {
|
Scope {
|
||||||
@@ -53,7 +56,7 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
|
|||||||
/// Wraps a Rust function or closure, creating a callable Lua function handle to it.
|
/// 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
|
/// 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::create_function`]: struct.Lua.html#method.create_function
|
||||||
/// [`Lua::scope`]: struct.Lua.html#method.scope
|
/// [`Lua::scope`]: struct.Lua.html#method.scope
|
||||||
@@ -65,7 +68,7 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
|
|||||||
{
|
{
|
||||||
// Safe, because 'scope must outlive 'callback (due to Self containing 'scope), however the
|
// 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
|
// 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
|
// the 'callback lifetime here can't be enlarged due to coming from a universal
|
||||||
// quantification in Lua::scope.
|
// quantification in Lua::scope.
|
||||||
//
|
//
|
||||||
@@ -82,7 +85,7 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
|
|||||||
/// Wraps a Rust mutable closure, creating a callable Lua function handle to it.
|
/// 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
|
/// 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::create_function_mut`]: struct.Lua.html#method.create_function_mut
|
||||||
/// [`Lua::scope`]: struct.Lua.html#method.scope
|
/// [`Lua::scope`]: struct.Lua.html#method.scope
|
||||||
@@ -107,7 +110,7 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
|
|||||||
/// Wraps a Rust async function or closure, creating a callable Lua function handle to it.
|
/// 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
|
/// 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"`
|
/// Requires `feature = "async"`
|
||||||
///
|
///
|
||||||
@@ -150,7 +153,7 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
|
|||||||
where
|
where
|
||||||
T: 'static + UserData,
|
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.
|
/// Create a Lua userdata object from a custom serializable userdata type.
|
||||||
@@ -170,26 +173,43 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
|
|||||||
where
|
where
|
||||||
T: 'static + UserData + Serialize,
|
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
|
where
|
||||||
T: 'static + UserData,
|
T: 'static + UserData,
|
||||||
{
|
{
|
||||||
// Safe even though T may not be Send, because the parent Lua cannot be sent to another
|
// 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).
|
// thread while the Scope is alive (or the returned AnyUserData handle even).
|
||||||
unsafe {
|
unsafe {
|
||||||
let u = self.lua.make_userdata(data)?;
|
let ud = self.lua.make_userdata(data)?;
|
||||||
self.destructors.borrow_mut().push((u.0.clone(), |u| {
|
|
||||||
let state = u.lua.state;
|
#[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);
|
assert_stack(state, 2);
|
||||||
u.lua.push_ref(&u);
|
|
||||||
// We know the destructor has not run yet because we hold a reference to the
|
ud.lua.push_ref(&ud);
|
||||||
// userdata.
|
|
||||||
|
// 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))]
|
vec![Box::new(take_userdata::<UserDataCell<T>>(state))]
|
||||||
}));
|
});
|
||||||
Ok(u)
|
self.destructors
|
||||||
|
.borrow_mut()
|
||||||
|
.push((ud.0.clone(), destructor));
|
||||||
|
|
||||||
|
Ok(ud)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -204,11 +224,11 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
|
|||||||
/// use [`Scope::create_userdata`] instead.
|
/// use [`Scope::create_userdata`] instead.
|
||||||
///
|
///
|
||||||
/// The main limitation that comes from using non-'static userdata is that the produced userdata
|
/// 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
|
/// 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
|
/// '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
|
/// 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
|
/// "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
|
/// 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.
|
/// creating the userdata metatable each time a new userdata is created.
|
||||||
///
|
///
|
||||||
@@ -220,55 +240,45 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
|
|||||||
where
|
where
|
||||||
T: 'scope + UserData,
|
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
|
// '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
|
// 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
|
// lifetime, so none of the static methods UserData types can add can possibly capture
|
||||||
// parameters.
|
// parameters.
|
||||||
fn wrap_method<'scope, 'lua, 'callback: 'scope, T: 'scope>(
|
fn wrap_method<'scope, 'lua, 'callback: 'scope, T: 'scope>(
|
||||||
scope: &Scope<'lua, 'scope>,
|
scope: &Scope<'lua, 'scope>,
|
||||||
data: Rc<UserDataCell<T>>,
|
data: Rc<RefCell<T>>,
|
||||||
|
data_ptr: *const c_void,
|
||||||
method: NonStaticMethod<'callback, T>,
|
method: NonStaticMethod<'callback, T>,
|
||||||
) -> Result<Function<'lua>> {
|
) -> Result<Function<'lua>> {
|
||||||
// On methods that actually receive the userdata, we fake a type check on the passed in
|
// 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
|
// 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
|
// 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
|
// 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
|
// called the method on the original value (since we otherwise completely ignore the
|
||||||
// first argument).
|
// first argument).
|
||||||
let check_data = data.clone();
|
|
||||||
let check_ud_type = move |lua: &'callback Lua, value| {
|
let check_ud_type = move |lua: &'callback Lua, value| {
|
||||||
if let Some(Value::UserData(ud)) = value {
|
if let Some(Value::UserData(ud)) = value {
|
||||||
unsafe {
|
unsafe {
|
||||||
assert_stack(lua.state, 1);
|
let _sg = StackGuard::new(lua.state);
|
||||||
lua.push_ref(&ud.0);
|
check_stack(lua.state, 2)?;
|
||||||
ffi::lua_getuservalue(lua.state, -1);
|
lua.push_userdata_ref(&ud.0)?;
|
||||||
#[cfg(any(feature = "lua52", feature = "lua51", feature = "luajit"))]
|
if get_userdata(lua.state, -1) as *const _ == data_ptr {
|
||||||
{
|
return Ok(());
|
||||||
ffi::lua_rawgeti(lua.state, -1, 1);
|
|
||||||
ffi::lua_remove(lua.state, -2);
|
|
||||||
}
|
}
|
||||||
return ffi::lua_touserdata(lua.state, -1)
|
|
||||||
== check_data.as_ptr() as *mut c_void;
|
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
Err(Error::UserDataTypeMismatch)
|
||||||
false
|
|
||||||
};
|
};
|
||||||
|
|
||||||
match method {
|
match method {
|
||||||
NonStaticMethod::Method(method) => {
|
NonStaticMethod::Method(method) => {
|
||||||
let f = Box::new(move |lua, mut args: MultiValue<'callback>| {
|
let f = Box::new(move |lua, mut args: MultiValue<'callback>| {
|
||||||
if !check_ud_type(lua, args.pop_front()) {
|
check_ud_type(lua, args.pop_front())?;
|
||||||
return Err(Error::UserDataTypeMismatch);
|
let data = data.try_borrow().map_err(|_| Error::UserDataBorrowError)?;
|
||||||
}
|
|
||||||
let data = data
|
|
||||||
.try_borrow()
|
|
||||||
.map(|cell| Ref::map(cell, AsRef::as_ref))
|
|
||||||
.map_err(|_| Error::UserDataBorrowError)?;
|
|
||||||
method(lua, &*data, args)
|
method(lua, &*data, args)
|
||||||
});
|
});
|
||||||
unsafe { scope.create_callback(f) }
|
unsafe { scope.create_callback(f) }
|
||||||
@@ -276,15 +286,12 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
|
|||||||
NonStaticMethod::MethodMut(method) => {
|
NonStaticMethod::MethodMut(method) => {
|
||||||
let method = RefCell::new(method);
|
let method = RefCell::new(method);
|
||||||
let f = Box::new(move |lua, mut args: MultiValue<'callback>| {
|
let f = Box::new(move |lua, mut args: MultiValue<'callback>| {
|
||||||
if !check_ud_type(lua, args.pop_front()) {
|
check_ud_type(lua, args.pop_front())?;
|
||||||
return Err(Error::UserDataTypeMismatch);
|
|
||||||
}
|
|
||||||
let mut method = method
|
let mut method = method
|
||||||
.try_borrow_mut()
|
.try_borrow_mut()
|
||||||
.map_err(|_| Error::RecursiveMutCallback)?;
|
.map_err(|_| Error::RecursiveMutCallback)?;
|
||||||
let mut data = data
|
let mut data = data
|
||||||
.try_borrow_mut()
|
.try_borrow_mut()
|
||||||
.map(|cell| RefMut::map(cell, AsMut::as_mut))
|
|
||||||
.map_err(|_| Error::UserDataBorrowMutError)?;
|
.map_err(|_| Error::UserDataBorrowMutError)?;
|
||||||
(&mut *method)(lua, &mut *data, args)
|
(&mut *method)(lua, &mut *data, args)
|
||||||
});
|
});
|
||||||
@@ -305,67 +312,136 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let mut ud_fields = NonStaticUserDataFields::default();
|
||||||
let mut ud_methods = NonStaticUserDataMethods::default();
|
let mut ud_methods = NonStaticUserDataMethods::default();
|
||||||
|
T::add_fields(&mut ud_fields);
|
||||||
T::add_methods(&mut ud_methods);
|
T::add_methods(&mut ud_methods);
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
let lua = self.lua;
|
let lua = self.lua;
|
||||||
let _sg = StackGuard::new(lua.state);
|
let _sg = StackGuard::new(lua.state);
|
||||||
assert_stack(lua.state, 6);
|
check_stack(lua.state, 13)?;
|
||||||
|
|
||||||
push_userdata(lua.state, ())?;
|
let data_ptr = protect_lua!(lua.state, 0, 1, |state| {
|
||||||
#[cfg(any(feature = "lua54", feature = "lua53"))]
|
ffi::lua_newuserdata(state, mem::size_of::<UserDataCell<Rc<RefCell<T>>>>())
|
||||||
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);
|
|
||||||
})?;
|
})?;
|
||||||
|
// 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 {
|
for (k, m) in ud_methods.meta_methods {
|
||||||
push_string(lua.state, k.name())?;
|
let data = data.clone();
|
||||||
lua.push_value(Value::Function(wrap_method(self, data.clone(), m)?))?;
|
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| {
|
let mut field_getters_index = None;
|
||||||
ffi::lua_rawset(state, -3);
|
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() {
|
let mut field_setters_index = None;
|
||||||
init_userdata_metatable::<()>(lua.state, -1, None)?;
|
let field_setters_nrec = ud_fields.field_setters.len();
|
||||||
} else {
|
if field_setters_nrec > 0 {
|
||||||
protect_lua_closure(lua.state, 0, 1, |state| {
|
push_table(lua.state, 0, field_setters_nrec as c_int)?;
|
||||||
ffi::lua_newtable(state);
|
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 {
|
for (k, m) in ud_methods.methods {
|
||||||
push_string(lua.state, &k)?;
|
let data = data.clone();
|
||||||
lua.push_value(Value::Function(wrap_method(self, data.clone(), m)?))?;
|
lua.push_value(Value::Function(wrap_method(self, data, data_ptr, m)?))?;
|
||||||
protect_lua_closure(lua.state, 3, 1, |state| {
|
rawset_field(lua.state, -2, &k)?;
|
||||||
ffi::lua_rawset(state, -3);
|
}
|
||||||
})?;
|
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))?;
|
let ud = Box::new(seal(take_userdata::<UserDataCell<Rc<RefCell<T>>>>(state)));
|
||||||
ffi::lua_pop(lua.state, 1);
|
vec![ud]
|
||||||
}
|
});
|
||||||
|
self.destructors
|
||||||
|
.borrow_mut()
|
||||||
|
.push((ud.0.clone(), destructor));
|
||||||
|
|
||||||
ffi::lua_setmetatable(lua.state, -2);
|
Ok(ud)
|
||||||
|
|
||||||
Ok(AnyUserData(lua.pop_ref()))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unsafe, because the callback can improperly capture any value with 'callback scope, such as
|
// 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
|
// 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
|
// 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.
|
// must NOT capture any parameters.
|
||||||
unsafe fn create_callback<'callback>(
|
unsafe fn create_callback<'callback>(
|
||||||
&self,
|
&self,
|
||||||
@@ -374,27 +450,26 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
|
|||||||
let f = mem::transmute::<Callback<'callback, 'scope>, Callback<'lua, 'static>>(f);
|
let f = mem::transmute::<Callback<'callback, 'scope>, Callback<'lua, 'static>>(f);
|
||||||
let f = self.lua.create_callback(f)?;
|
let f = self.lua.create_callback(f)?;
|
||||||
|
|
||||||
let mut destructors = self.destructors.borrow_mut();
|
let destructor: DestructorCallback = Box::new(|f| {
|
||||||
destructors.push((f.0.clone(), |f| {
|
|
||||||
let state = f.lua.state;
|
let state = f.lua.state;
|
||||||
|
let _sg = StackGuard::new(state);
|
||||||
assert_stack(state, 3);
|
assert_stack(state, 3);
|
||||||
|
|
||||||
f.lua.push_ref(&f);
|
f.lua.push_ref(&f);
|
||||||
|
|
||||||
// We know the destructor has not run yet because we hold a reference to the callback.
|
// We know the destructor has not run yet because we hold a reference to the callback.
|
||||||
|
|
||||||
ffi::lua_getupvalue(state, -1, 1);
|
ffi::lua_getupvalue(state, -1, 1);
|
||||||
let ud1 = take_userdata::<Callback>(state);
|
let ud = take_userdata::<CallbackUpvalue>(state);
|
||||||
ffi::lua_pushnil(state);
|
ffi::lua_pushnil(state);
|
||||||
ffi::lua_setupvalue(state, -2, 1);
|
ffi::lua_setupvalue(state, -2, 1);
|
||||||
|
|
||||||
ffi::lua_getupvalue(state, -1, 2);
|
vec![Box::new(ud)]
|
||||||
let ud2 = take_userdata::<Lua>(state);
|
});
|
||||||
ffi::lua_pushnil(state);
|
self.destructors
|
||||||
ffi::lua_setupvalue(state, -2, 2);
|
.borrow_mut()
|
||||||
|
.push((f.0.clone(), destructor));
|
||||||
|
|
||||||
ffi::lua_pop(state, 1);
|
|
||||||
vec![Box::new(ud1), Box::new(ud2)]
|
|
||||||
}));
|
|
||||||
Ok(f)
|
Ok(f)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -406,10 +481,14 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
|
|||||||
let f = mem::transmute::<AsyncCallback<'callback, 'scope>, AsyncCallback<'lua, 'static>>(f);
|
let f = mem::transmute::<AsyncCallback<'callback, 'scope>, AsyncCallback<'lua, 'static>>(f);
|
||||||
let f = self.lua.create_async_callback(f)?;
|
let f = self.lua.create_async_callback(f)?;
|
||||||
|
|
||||||
let mut destructors = self.destructors.borrow_mut();
|
// We need to pre-allocate strings to avoid failures in destructor.
|
||||||
destructors.push((f.0.clone(), |f| {
|
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;
|
let state = f.lua.state;
|
||||||
assert_stack(state, 4);
|
let _sg = StackGuard::new(state);
|
||||||
|
assert_stack(state, 5);
|
||||||
|
|
||||||
f.lua.push_ref(&f);
|
f.lua.push_ref(&f);
|
||||||
|
|
||||||
// We know the destructor has not run yet because we hold a reference to the callback.
|
// We know the destructor has not run yet because we hold a reference to the callback.
|
||||||
@@ -420,26 +499,34 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
|
|||||||
#[cfg(any(feature = "lua51", feature = "luajit"))]
|
#[cfg(any(feature = "lua51", feature = "luajit"))]
|
||||||
ffi::lua_getfenv(state, -1);
|
ffi::lua_getfenv(state, -1);
|
||||||
|
|
||||||
// Then, get the get_poll() closure using the corresponding key
|
// Second, get the `get_poll()` closure using the corresponding key
|
||||||
let key = "get_poll";
|
f.lua.push_ref(&get_poll_str.0);
|
||||||
ffi::lua_pushlstring(state, key.as_ptr() as *const c_char, key.len());
|
|
||||||
ffi::lua_rawget(state, -2);
|
ffi::lua_rawget(state, -2);
|
||||||
|
|
||||||
// Finally, destroy all upvalues
|
// Destroy all upvalues
|
||||||
ffi::lua_getupvalue(state, -1, 1);
|
ffi::lua_getupvalue(state, -1, 1);
|
||||||
let ud1 = take_userdata::<AsyncCallback>(state);
|
let upvalue1 = take_userdata::<AsyncCallbackUpvalue>(state);
|
||||||
ffi::lua_pushnil(state);
|
ffi::lua_pushnil(state);
|
||||||
ffi::lua_setupvalue(state, -2, 1);
|
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);
|
ffi::lua_pop(state, 1);
|
||||||
|
let mut data: Vec<Box<dyn Any>> = vec![Box::new(upvalue1)];
|
||||||
|
|
||||||
vec![Box::new(ud1), Box::new(ud2)]
|
// Finally, get polled future and destroy it
|
||||||
}));
|
f.lua.push_ref(&poll_str.0);
|
||||||
|
if ffi::lua_rawget(state, -2) == ffi::LUA_TFUNCTION {
|
||||||
|
ffi::lua_getupvalue(state, -1, 1);
|
||||||
|
let upvalue2 = take_userdata::<AsyncPollUpvalue>(state);
|
||||||
|
ffi::lua_pushnil(state);
|
||||||
|
ffi::lua_setupvalue(state, -2, 1);
|
||||||
|
data.push(Box::new(upvalue2));
|
||||||
|
}
|
||||||
|
|
||||||
|
data
|
||||||
|
});
|
||||||
|
self.destructors
|
||||||
|
.borrow_mut()
|
||||||
|
.push((f.0.clone(), destructor));
|
||||||
|
|
||||||
Ok(f)
|
Ok(f)
|
||||||
}
|
}
|
||||||
@@ -448,7 +535,7 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
|
|||||||
impl<'lua, 'scope> Drop for Scope<'lua, 'scope> {
|
impl<'lua, 'scope> Drop for Scope<'lua, 'scope> {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
// We separate the action of invalidating the userdata in Lua and actually dropping the
|
// 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.
|
// can be sure that all of the userdata in Lua is actually invalidated.
|
||||||
|
|
||||||
// All destructors are non-panicking, so this is fine
|
// All destructors are non-panicking, so this is fine
|
||||||
@@ -563,7 +650,6 @@ impl<'lua, T: UserData> UserDataMethods<'lua, T> for NonStaticUserDataMethods<'l
|
|||||||
#[cfg(feature = "async")]
|
#[cfg(feature = "async")]
|
||||||
fn add_async_function<S, A, R, F, FR>(&mut self, _name: &S, _function: F)
|
fn add_async_function<S, A, R, F, FR>(&mut self, _name: &S, _function: F)
|
||||||
where
|
where
|
||||||
T: Clone,
|
|
||||||
S: AsRef<[u8]> + ?Sized,
|
S: AsRef<[u8]> + ?Sized,
|
||||||
A: FromLuaMulti<'lua>,
|
A: FromLuaMulti<'lua>,
|
||||||
R: ToLuaMulti<'lua>,
|
R: ToLuaMulti<'lua>,
|
||||||
@@ -575,59 +661,167 @@ impl<'lua, T: UserData> UserDataMethods<'lua, T> for NonStaticUserDataMethods<'l
|
|||||||
mlua_panic!("asynchronous functions are not supported for non-static userdata")
|
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
|
where
|
||||||
|
S: Into<MetaMethod>,
|
||||||
A: FromLuaMulti<'lua>,
|
A: FromLuaMulti<'lua>,
|
||||||
R: ToLuaMulti<'lua>,
|
R: ToLuaMulti<'lua>,
|
||||||
M: 'static + MaybeSend + Fn(&'lua Lua, &T, A) -> Result<R>,
|
M: 'static + MaybeSend + Fn(&'lua Lua, &T, A) -> Result<R>,
|
||||||
{
|
{
|
||||||
self.meta_methods.push((
|
self.meta_methods.push((
|
||||||
meta,
|
meta.into(),
|
||||||
NonStaticMethod::Method(Box::new(move |lua, ud, args| {
|
NonStaticMethod::Method(Box::new(move |lua, ud, args| {
|
||||||
method(lua, ud, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
|
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
|
where
|
||||||
|
S: Into<MetaMethod>,
|
||||||
A: FromLuaMulti<'lua>,
|
A: FromLuaMulti<'lua>,
|
||||||
R: ToLuaMulti<'lua>,
|
R: ToLuaMulti<'lua>,
|
||||||
M: 'static + MaybeSend + FnMut(&'lua Lua, &mut T, A) -> Result<R>,
|
M: 'static + MaybeSend + FnMut(&'lua Lua, &mut T, A) -> Result<R>,
|
||||||
{
|
{
|
||||||
self.meta_methods.push((
|
self.meta_methods.push((
|
||||||
meta,
|
meta.into(),
|
||||||
NonStaticMethod::MethodMut(Box::new(move |lua, ud, args| {
|
NonStaticMethod::MethodMut(Box::new(move |lua, ud, args| {
|
||||||
method(lua, ud, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
|
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
|
where
|
||||||
|
S: Into<MetaMethod>,
|
||||||
A: FromLuaMulti<'lua>,
|
A: FromLuaMulti<'lua>,
|
||||||
R: ToLuaMulti<'lua>,
|
R: ToLuaMulti<'lua>,
|
||||||
F: 'static + MaybeSend + Fn(&'lua Lua, A) -> Result<R>,
|
F: 'static + MaybeSend + Fn(&'lua Lua, A) -> Result<R>,
|
||||||
{
|
{
|
||||||
self.meta_methods.push((
|
self.meta_methods.push((
|
||||||
meta,
|
meta.into(),
|
||||||
NonStaticMethod::Function(Box::new(move |lua, args| {
|
NonStaticMethod::Function(Box::new(move |lua, args| {
|
||||||
function(lua, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
|
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
|
where
|
||||||
|
S: Into<MetaMethod>,
|
||||||
A: FromLuaMulti<'lua>,
|
A: FromLuaMulti<'lua>,
|
||||||
R: ToLuaMulti<'lua>,
|
R: ToLuaMulti<'lua>,
|
||||||
F: 'static + MaybeSend + FnMut(&'lua Lua, A) -> Result<R>,
|
F: 'static + MaybeSend + FnMut(&'lua Lua, A) -> Result<R>,
|
||||||
{
|
{
|
||||||
self.meta_methods.push((
|
self.meta_methods.push((
|
||||||
meta,
|
meta.into(),
|
||||||
NonStaticMethod::FunctionMut(Box::new(move |lua, args| {
|
NonStaticMethod::FunctionMut(Box::new(move |lua, args| {
|
||||||
function(lua, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
|
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)
|
||||||
|
}),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
+263
-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 std::string::String as StdString;
|
||||||
|
|
||||||
use serde::de::{self, IntoDeserializer};
|
use serde::de::{self, IntoDeserializer};
|
||||||
|
|
||||||
use crate::error::{Error, Result};
|
use crate::error::{Error, Result};
|
||||||
use crate::table::{TablePairs, TableSequence};
|
use crate::table::{Table, TablePairs, TableSequence};
|
||||||
use crate::value::Value;
|
use crate::value::Value;
|
||||||
|
|
||||||
/// A struct for deserializing Lua values into Rust values.
|
/// 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> {
|
impl<'lua, 'de> serde::Deserializer<'de> for Deserializer<'lua> {
|
||||||
type Error = Error;
|
type Error = Error;
|
||||||
@@ -17,10 +105,12 @@ impl<'lua, 'de> serde::Deserializer<'de> for Deserializer<'lua> {
|
|||||||
where
|
where
|
||||||
V: de::Visitor<'de>,
|
V: de::Visitor<'de>,
|
||||||
{
|
{
|
||||||
match self.0 {
|
match self.value {
|
||||||
Value::Nil => visitor.visit_unit(),
|
Value::Nil => visitor.visit_unit(),
|
||||||
Value::Boolean(b) => visitor.visit_bool(b),
|
Value::Boolean(b) => visitor.visit_bool(b),
|
||||||
|
#[allow(clippy::useless_conversion)]
|
||||||
Value::Integer(i) => visitor.visit_i64(i.into()),
|
Value::Integer(i) => visitor.visit_i64(i.into()),
|
||||||
|
#[allow(clippy::useless_conversion)]
|
||||||
Value::Number(n) => visitor.visit_f64(n.into()),
|
Value::Number(n) => visitor.visit_f64(n.into()),
|
||||||
Value::String(s) => match s.to_str() {
|
Value::String(s) => match s.to_str() {
|
||||||
Ok(s) => visitor.visit_str(s),
|
Ok(s) => visitor.visit_str(s),
|
||||||
@@ -33,7 +123,16 @@ impl<'lua, 'de> serde::Deserializer<'de> for Deserializer<'lua> {
|
|||||||
| Value::Thread(_)
|
| Value::Thread(_)
|
||||||
| Value::UserData(_)
|
| Value::UserData(_)
|
||||||
| Value::LightUserData(_)
|
| 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()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -42,7 +141,7 @@ impl<'lua, 'de> serde::Deserializer<'de> for Deserializer<'lua> {
|
|||||||
where
|
where
|
||||||
V: de::Visitor<'de>,
|
V: de::Visitor<'de>,
|
||||||
{
|
{
|
||||||
match self.0 {
|
match self.value {
|
||||||
Value::Nil => visitor.visit_none(),
|
Value::Nil => visitor.visit_none(),
|
||||||
Value::LightUserData(ud) if ud.0.is_null() => visitor.visit_none(),
|
Value::LightUserData(ud) if ud.0.is_null() => visitor.visit_none(),
|
||||||
_ => visitor.visit_some(self),
|
_ => visitor.visit_some(self),
|
||||||
@@ -59,9 +158,11 @@ impl<'lua, 'de> serde::Deserializer<'de> for Deserializer<'lua> {
|
|||||||
where
|
where
|
||||||
V: de::Visitor<'de>,
|
V: de::Visitor<'de>,
|
||||||
{
|
{
|
||||||
let (variant, value) = match self.0 {
|
let (variant, value, _guard) = match self.value {
|
||||||
Value::Table(value) => {
|
Value::Table(table) => {
|
||||||
let mut iter = value.pairs::<StdString, Value>();
|
let _guard = RecursionGuard::new(&table, &self.visited);
|
||||||
|
|
||||||
|
let mut iter = table.pairs::<StdString, Value>();
|
||||||
let (variant, value) = match iter.next() {
|
let (variant, value) = match iter.next() {
|
||||||
Some(v) => v?,
|
Some(v) => v?,
|
||||||
None => {
|
None => {
|
||||||
@@ -78,13 +179,22 @@ impl<'lua, 'de> serde::Deserializer<'de> for Deserializer<'lua> {
|
|||||||
&"map with a single key",
|
&"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")),
|
_ => 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]
|
#[inline]
|
||||||
@@ -92,12 +202,18 @@ impl<'lua, 'de> serde::Deserializer<'de> for Deserializer<'lua> {
|
|||||||
where
|
where
|
||||||
V: de::Visitor<'de>,
|
V: de::Visitor<'de>,
|
||||||
{
|
{
|
||||||
match self.0 {
|
match self.value {
|
||||||
Value::Table(t) => {
|
Value::Table(t) => {
|
||||||
|
let _guard = RecursionGuard::new(&t, &self.visited);
|
||||||
|
|
||||||
let len = t.raw_len() as usize;
|
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)?;
|
let seq = visitor.visit_seq(&mut deserializer)?;
|
||||||
if deserializer.0.count() == 0 {
|
if deserializer.seq.count() == 0 {
|
||||||
Ok(seq)
|
Ok(seq)
|
||||||
} else {
|
} else {
|
||||||
Err(de::Error::invalid_length(
|
Err(de::Error::invalid_length(
|
||||||
@@ -106,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",
|
||||||
|
)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -136,9 +255,17 @@ impl<'lua, 'de> serde::Deserializer<'de> for Deserializer<'lua> {
|
|||||||
where
|
where
|
||||||
V: de::Visitor<'de>,
|
V: de::Visitor<'de>,
|
||||||
{
|
{
|
||||||
match self.0 {
|
match self.value {
|
||||||
Value::Table(t) => {
|
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 map = visitor.visit_map(&mut deserializer)?;
|
||||||
let count = deserializer.pairs.count();
|
let count = deserializer.pairs.count();
|
||||||
if count == 0 {
|
if count == 0 {
|
||||||
@@ -150,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",
|
||||||
|
)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -174,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> {
|
impl<'lua, 'de> de::SeqAccess<'de> for SeqDeserializer<'lua> {
|
||||||
type Error = Error;
|
type Error = Error;
|
||||||
@@ -183,14 +317,24 @@ impl<'lua, 'de> de::SeqAccess<'de> for SeqDeserializer<'lua> {
|
|||||||
where
|
where
|
||||||
T: de::DeserializeSeed<'de>,
|
T: de::DeserializeSeed<'de>,
|
||||||
{
|
{
|
||||||
match self.0.next() {
|
loop {
|
||||||
Some(value) => seed.deserialize(Deserializer(value?)).map(Some),
|
match self.seq.next() {
|
||||||
None => Ok(None),
|
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> {
|
fn size_hint(&self) -> Option<usize> {
|
||||||
match self.0.size_hint() {
|
match self.seq.size_hint() {
|
||||||
(lower, Some(upper)) if lower == upper => Some(upper),
|
(lower, Some(upper)) if lower == upper => Some(upper),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
@@ -200,19 +344,11 @@ impl<'lua, 'de> de::SeqAccess<'de> for SeqDeserializer<'lua> {
|
|||||||
struct MapDeserializer<'lua> {
|
struct MapDeserializer<'lua> {
|
||||||
pairs: TablePairs<'lua, Value<'lua>, Value<'lua>>,
|
pairs: TablePairs<'lua, Value<'lua>, Value<'lua>>,
|
||||||
value: Option<Value<'lua>>,
|
value: Option<Value<'lua>>,
|
||||||
|
options: Options,
|
||||||
|
visited: Rc<RefCell<HashSet<*const c_void>>>,
|
||||||
processed: usize,
|
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> {
|
impl<'lua, 'de> de::MapAccess<'de> for MapDeserializer<'lua> {
|
||||||
type Error = Error;
|
type Error = Error;
|
||||||
|
|
||||||
@@ -220,15 +356,23 @@ impl<'lua, 'de> de::MapAccess<'de> for MapDeserializer<'lua> {
|
|||||||
where
|
where
|
||||||
T: de::DeserializeSeed<'de>,
|
T: de::DeserializeSeed<'de>,
|
||||||
{
|
{
|
||||||
match self.pairs.next() {
|
loop {
|
||||||
Some(item) => {
|
match self.pairs.next() {
|
||||||
let (key, value) = item?;
|
Some(item) => {
|
||||||
self.processed += 1;
|
let (key, value) = item?;
|
||||||
self.value = Some(value);
|
if check_value_if_skip(&key, self.options, &self.visited)?
|
||||||
let key_de = Deserializer(key);
|
|| check_value_if_skip(&value, self.options, &self.visited)?
|
||||||
seed.deserialize(key_de).map(Some)
|
{
|
||||||
|
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),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -237,7 +381,10 @@ impl<'lua, 'de> de::MapAccess<'de> for MapDeserializer<'lua> {
|
|||||||
T: de::DeserializeSeed<'de>,
|
T: de::DeserializeSeed<'de>,
|
||||||
{
|
{
|
||||||
match self.value.take() {
|
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")),
|
None => Err(de::Error::custom("value is missing")),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -253,6 +400,8 @@ impl<'lua, 'de> de::MapAccess<'de> for MapDeserializer<'lua> {
|
|||||||
struct EnumDeserializer<'lua> {
|
struct EnumDeserializer<'lua> {
|
||||||
variant: StdString,
|
variant: StdString,
|
||||||
value: Option<Value<'lua>>,
|
value: Option<Value<'lua>>,
|
||||||
|
options: Options,
|
||||||
|
visited: Rc<RefCell<HashSet<*const c_void>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'lua, 'de> de::EnumAccess<'de> for EnumDeserializer<'lua> {
|
impl<'lua, 'de> de::EnumAccess<'de> for EnumDeserializer<'lua> {
|
||||||
@@ -264,13 +413,19 @@ impl<'lua, 'de> de::EnumAccess<'de> for EnumDeserializer<'lua> {
|
|||||||
T: de::DeserializeSeed<'de>,
|
T: de::DeserializeSeed<'de>,
|
||||||
{
|
{
|
||||||
let variant = self.variant.into_deserializer();
|
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))
|
seed.deserialize(variant).map(|v| (v, variant_access))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct VariantDeserializer<'lua> {
|
struct VariantDeserializer<'lua> {
|
||||||
value: Option<Value<'lua>>,
|
value: Option<Value<'lua>>,
|
||||||
|
options: Options,
|
||||||
|
visited: Rc<RefCell<HashSet<*const c_void>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'lua, 'de> de::VariantAccess<'de> for VariantDeserializer<'lua> {
|
impl<'lua, 'de> de::VariantAccess<'de> for VariantDeserializer<'lua> {
|
||||||
@@ -291,7 +446,9 @@ impl<'lua, 'de> de::VariantAccess<'de> for VariantDeserializer<'lua> {
|
|||||||
T: de::DeserializeSeed<'de>,
|
T: de::DeserializeSeed<'de>,
|
||||||
{
|
{
|
||||||
match self.value {
|
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(
|
None => Err(de::Error::invalid_type(
|
||||||
de::Unexpected::UnitVariant,
|
de::Unexpected::UnitVariant,
|
||||||
&"newtype variant",
|
&"newtype variant",
|
||||||
@@ -304,7 +461,10 @@ impl<'lua, 'de> de::VariantAccess<'de> for VariantDeserializer<'lua> {
|
|||||||
V: de::Visitor<'de>,
|
V: de::Visitor<'de>,
|
||||||
{
|
{
|
||||||
match self.value {
|
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(
|
None => Err(de::Error::invalid_type(
|
||||||
de::Unexpected::UnitVariant,
|
de::Unexpected::UnitVariant,
|
||||||
&"tuple variant",
|
&"tuple variant",
|
||||||
@@ -317,7 +477,10 @@ impl<'lua, 'de> de::VariantAccess<'de> for VariantDeserializer<'lua> {
|
|||||||
V: de::Visitor<'de>,
|
V: de::Visitor<'de>,
|
||||||
{
|
{
|
||||||
match self.value {
|
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(
|
None => Err(de::Error::invalid_type(
|
||||||
de::Unexpected::UnitVariant,
|
de::Unexpected::UnitVariant,
|
||||||
&"struct variant",
|
&"struct variant",
|
||||||
@@ -325,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.
|
//! (De)Serialization support using serde.
|
||||||
|
|
||||||
use std::os::raw::{c_int, c_void};
|
use std::os::raw::c_void;
|
||||||
use std::ptr;
|
use std::ptr;
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
@@ -9,9 +9,11 @@ use crate::error::Result;
|
|||||||
use crate::ffi;
|
use crate::ffi;
|
||||||
use crate::lua::Lua;
|
use crate::lua::Lua;
|
||||||
use crate::table::Table;
|
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;
|
use crate::value::Value;
|
||||||
|
|
||||||
|
/// Trait for serializing/deserializing Lua values using Serde.
|
||||||
pub trait LuaSerdeExt<'lua> {
|
pub trait LuaSerdeExt<'lua> {
|
||||||
/// A special value (lightuserdata) to encode/decode optional (none) values.
|
/// A special value (lightuserdata) to encode/decode optional (none) values.
|
||||||
///
|
///
|
||||||
@@ -25,7 +27,7 @@ pub trait LuaSerdeExt<'lua> {
|
|||||||
///
|
///
|
||||||
/// fn main() -> Result<()> {
|
/// fn main() -> Result<()> {
|
||||||
/// let lua = Lua::new();
|
/// 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 val = lua.load(r#"{a = null}"#).eval()?;
|
||||||
/// let map: HashMap<String, Option<String>> = lua.from_value(val)?;
|
/// let map: HashMap<String, Option<String>> = lua.from_value(val)?;
|
||||||
@@ -34,7 +36,7 @@ pub trait LuaSerdeExt<'lua> {
|
|||||||
/// Ok(())
|
/// 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).
|
/// A metatable attachable to a Lua table to systematically encode it as Array (instead of Map).
|
||||||
/// As result, encoded Array will contain only sequence part of the table, with the same length
|
/// As 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<()> {
|
/// fn main() -> Result<()> {
|
||||||
/// let lua = Lua::new();
|
/// 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)
|
/// // Encode as an empty array (no sequence part in the lua table)
|
||||||
/// let val = lua.load("setmetatable({a = 5}, array_mt)").eval()?;
|
/// let val = lua.load("setmetatable({a = 5}, array_mt)").eval()?;
|
||||||
@@ -65,7 +67,7 @@ pub trait LuaSerdeExt<'lua> {
|
|||||||
/// Ok(())
|
/// Ok(())
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
fn array_metatable(&'lua self) -> Result<Table<'lua>>;
|
fn array_metatable(&'lua self) -> Table<'lua>;
|
||||||
|
|
||||||
/// Converts `T` into a `Value` instance.
|
/// 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>>;
|
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.
|
/// Deserializes a `Value` into any serde deserializable object.
|
||||||
///
|
///
|
||||||
/// Requires `feature = "serialize"`
|
/// Requires `feature = "serialize"`
|
||||||
@@ -129,34 +158,56 @@ pub trait LuaSerdeExt<'lua> {
|
|||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
fn from_value<T: Deserialize<'lua>>(&'lua self, value: Value<'lua>) -> Result<T>;
|
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 {
|
impl<'lua> LuaSerdeExt<'lua> for Lua {
|
||||||
fn null(&'lua self) -> Result<Value<'lua>> {
|
fn null(&'lua self) -> Value<'lua> {
|
||||||
unsafe {
|
Value::LightUserData(LightUserData(ptr::null_mut()))
|
||||||
let _sg = StackGuard::new(self.state);
|
|
||||||
assert_stack(self.state, 3);
|
|
||||||
|
|
||||||
unsafe extern "C" fn push_null(state: *mut ffi::lua_State) -> c_int {
|
|
||||||
ffi::lua_pushlightuserdata(state, ptr::null_mut());
|
|
||||||
1
|
|
||||||
}
|
|
||||||
protect_lua(self.state, 0, push_null)?;
|
|
||||||
Ok(self.pop_value())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn array_metatable(&'lua self) -> Result<Table<'lua>> {
|
fn array_metatable(&'lua self) -> Table<'lua> {
|
||||||
unsafe {
|
unsafe {
|
||||||
let _sg = StackGuard::new(self.state);
|
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(self.state);
|
||||||
push_array_metatable(state);
|
|
||||||
1
|
Table(self.pop_ref())
|
||||||
}
|
|
||||||
protect_lua(self.state, 0, get_array_mt)?;
|
|
||||||
Ok(Table(self.pop_ref()))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -164,37 +215,57 @@ impl<'lua> LuaSerdeExt<'lua> for Lua {
|
|||||||
where
|
where
|
||||||
T: Serialize + ?Sized,
|
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>
|
fn from_value<T>(&'lua self, value: Value<'lua>) -> Result<T>
|
||||||
where
|
where
|
||||||
T: Deserialize<'lua>,
|
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) {
|
// Uses 2 stack spaces and calls checkstack.
|
||||||
ffi::lua_pushlightuserdata(
|
pub(crate) unsafe fn init_metatables(state: *mut ffi::lua_State) -> Result<()> {
|
||||||
state,
|
check_stack(state, 2)?;
|
||||||
&ARRAY_METATABLE_REGISTRY_KEY as *const u8 as *mut c_void,
|
protect_lua!(state, 0, 0, fn(state) {
|
||||||
);
|
ffi::lua_createtable(state, 0, 1);
|
||||||
ffi::lua_newtable(state);
|
|
||||||
|
|
||||||
ffi::lua_pushstring(state, cstr!("__metatable"));
|
ffi::lua_pushstring(state, cstr!("__metatable"));
|
||||||
ffi::lua_pushboolean(state, 0);
|
ffi::lua_pushboolean(state, 0);
|
||||||
ffi::lua_rawset(state, -3);
|
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) {
|
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;
|
let array_metatable_key = &ARRAY_METATABLE_REGISTRY_KEY as *const u8 as *mut c_void;
|
||||||
ffi::lua_rawgetp(state, ffi::LUA_REGISTRYINDEX, key);
|
ffi::lua_rawgetp(state, ffi::LUA_REGISTRYINDEX, array_metatable_key);
|
||||||
}
|
}
|
||||||
|
|
||||||
static ARRAY_METATABLE_REGISTRY_KEY: u8 = 0;
|
static ARRAY_METATABLE_REGISTRY_KEY: u8 = 0;
|
||||||
|
|
||||||
pub mod de;
|
pub mod de;
|
||||||
pub mod ser;
|
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::string::String;
|
||||||
use crate::table::Table;
|
use crate::table::Table;
|
||||||
use crate::types::Integer;
|
use crate::types::Integer;
|
||||||
use crate::util::{assert_stack, protect_lua, StackGuard};
|
use crate::util::{check_stack, StackGuard};
|
||||||
use crate::value::{ToLua, Value};
|
use crate::value::{ToLua, Value};
|
||||||
|
|
||||||
/// A struct for serializing Rust values into Lua values.
|
/// 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 {
|
macro_rules! lua_serialize_number {
|
||||||
($name:ident, $t:ty) => {
|
($name:ident, $t:ty) => {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn $name(self, value: $t) -> Result<Value<'lua>> {
|
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]
|
#[inline]
|
||||||
fn serialize_str(self, value: &str) -> Result<Value<'lua>> {
|
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]
|
#[inline]
|
||||||
fn serialize_bytes(self, value: &[u8]) -> Result<Value<'lua>> {
|
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]
|
#[inline]
|
||||||
fn serialize_none(self) -> Result<Value<'lua>> {
|
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]
|
#[inline]
|
||||||
fn serialize_some<T>(self, value: &T) -> Result<Value<'lua>>
|
fn serialize_some<T>(self, value: &T) -> Result<Value<'lua>>
|
||||||
where
|
where
|
||||||
T: ?Sized + Serialize,
|
T: Serialize + ?Sized,
|
||||||
{
|
{
|
||||||
value.serialize(self)
|
value.serialize(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn serialize_unit(self) -> Result<Value<'lua>> {
|
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]
|
#[inline]
|
||||||
fn serialize_unit_struct(self, _name: &'static str) -> Result<Value<'lua>> {
|
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]
|
#[inline]
|
||||||
@@ -106,7 +206,7 @@ impl<'lua> ser::Serializer for Serializer<'lua> {
|
|||||||
#[inline]
|
#[inline]
|
||||||
fn serialize_newtype_struct<T>(self, _name: &'static str, value: &T) -> Result<Value<'lua>>
|
fn serialize_newtype_struct<T>(self, _name: &'static str, value: &T) -> Result<Value<'lua>>
|
||||||
where
|
where
|
||||||
T: ?Sized + Serialize,
|
T: Serialize + ?Sized,
|
||||||
{
|
{
|
||||||
value.serialize(self)
|
value.serialize(self)
|
||||||
}
|
}
|
||||||
@@ -120,11 +220,11 @@ impl<'lua> ser::Serializer for Serializer<'lua> {
|
|||||||
value: &T,
|
value: &T,
|
||||||
) -> Result<Value<'lua>>
|
) -> Result<Value<'lua>>
|
||||||
where
|
where
|
||||||
T: ?Sized + Serialize,
|
T: Serialize + ?Sized,
|
||||||
{
|
{
|
||||||
let table = self.0.create_table()?;
|
let table = self.lua.create_table()?;
|
||||||
let variant = self.0.create_string(variant)?;
|
let variant = self.lua.create_string(variant)?;
|
||||||
let value = self.0.to_value(value)?;
|
let value = self.lua.to_value_with(value, self.options)?;
|
||||||
table.raw_set(variant, value)?;
|
table.raw_set(variant, value)?;
|
||||||
Ok(Value::Table(table))
|
Ok(Value::Table(table))
|
||||||
}
|
}
|
||||||
@@ -132,9 +232,12 @@ impl<'lua> ser::Serializer for Serializer<'lua> {
|
|||||||
#[inline]
|
#[inline]
|
||||||
fn serialize_seq(self, len: Option<usize>) -> Result<Self::SerializeSeq> {
|
fn serialize_seq(self, len: Option<usize>) -> Result<Self::SerializeSeq> {
|
||||||
let len = len.unwrap_or(0) as c_int;
|
let len = len.unwrap_or(0) as c_int;
|
||||||
let table = self.0.create_table_with_capacity(len, 0)?;
|
let table = self.lua.create_table_with_capacity(len, 0)?;
|
||||||
table.set_metatable(Some(self.0.array_metatable()?));
|
if self.options.set_array_metatable {
|
||||||
Ok(SerializeVec { table })
|
table.set_metatable(Some(self.lua.array_metatable()));
|
||||||
|
}
|
||||||
|
let options = self.options;
|
||||||
|
Ok(SerializeVec { table, options })
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
@@ -159,9 +262,11 @@ impl<'lua> ser::Serializer for Serializer<'lua> {
|
|||||||
variant: &'static str,
|
variant: &'static str,
|
||||||
_len: usize,
|
_len: usize,
|
||||||
) -> Result<Self::SerializeTupleVariant> {
|
) -> Result<Self::SerializeTupleVariant> {
|
||||||
let name = self.0.create_string(variant)?;
|
Ok(SerializeTupleVariant {
|
||||||
let table = self.0.create_table()?;
|
name: self.lua.create_string(variant)?,
|
||||||
Ok(SerializeTupleVariant { name, table })
|
table: self.lua.create_table()?,
|
||||||
|
options: self.options,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
@@ -169,7 +274,8 @@ impl<'lua> ser::Serializer for Serializer<'lua> {
|
|||||||
let len = len.unwrap_or(0) as c_int;
|
let len = len.unwrap_or(0) as c_int;
|
||||||
Ok(SerializeMap {
|
Ok(SerializeMap {
|
||||||
key: None,
|
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,
|
variant: &'static str,
|
||||||
len: usize,
|
len: usize,
|
||||||
) -> Result<Self::SerializeStructVariant> {
|
) -> Result<Self::SerializeStructVariant> {
|
||||||
let name = self.0.create_string(variant)?;
|
Ok(SerializeStructVariant {
|
||||||
let table = self.0.create_table_with_capacity(0, len as c_int)?;
|
name: self.lua.create_string(variant)?,
|
||||||
Ok(SerializeStructVariant { name, table })
|
table: self.lua.create_table_with_capacity(0, len as c_int)?,
|
||||||
|
options: self.options,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[doc(hidden)]
|
||||||
pub struct SerializeVec<'lua> {
|
pub struct SerializeVec<'lua> {
|
||||||
table: Table<'lua>,
|
table: Table<'lua>,
|
||||||
|
options: Options,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'lua> ser::SerializeSeq for SerializeVec<'lua> {
|
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<()>
|
fn serialize_element<T>(&mut self, value: &T) -> Result<()>
|
||||||
where
|
where
|
||||||
T: ?Sized + Serialize,
|
T: Serialize + ?Sized,
|
||||||
{
|
{
|
||||||
let lua = self.table.0.lua;
|
let lua = self.table.0.lua;
|
||||||
let value = lua.to_value(value)?;
|
let value = lua.to_value_with(value, self.options)?;
|
||||||
unsafe {
|
unsafe {
|
||||||
let _sg = StackGuard::new(lua.state);
|
let _sg = StackGuard::new(lua.state);
|
||||||
assert_stack(lua.state, 4);
|
check_stack(lua.state, 4)?;
|
||||||
|
|
||||||
lua.push_ref(&self.table.0);
|
lua.push_ref(&self.table.0);
|
||||||
lua.push_value(value)?;
|
lua.push_value(value)?;
|
||||||
|
protect_lua!(lua.state, 2, 0, fn(state) {
|
||||||
unsafe extern "C" fn push_to_table(state: *mut ffi::lua_State) -> c_int {
|
|
||||||
let len = ffi::lua_rawlen(state, -2) as Integer;
|
let len = ffi::lua_rawlen(state, -2) as Integer;
|
||||||
ffi::lua_rawseti(state, -2, len + 1);
|
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<()>
|
fn serialize_element<T>(&mut self, value: &T) -> Result<()>
|
||||||
where
|
where
|
||||||
T: ?Sized + Serialize,
|
T: Serialize + ?Sized,
|
||||||
{
|
{
|
||||||
ser::SerializeSeq::serialize_element(self, value)
|
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<()>
|
fn serialize_field<T>(&mut self, value: &T) -> Result<()>
|
||||||
where
|
where
|
||||||
T: ?Sized + Serialize,
|
T: Serialize + ?Sized,
|
||||||
{
|
{
|
||||||
ser::SerializeSeq::serialize_element(self, value)
|
ser::SerializeSeq::serialize_element(self, value)
|
||||||
}
|
}
|
||||||
@@ -260,9 +366,11 @@ impl<'lua> ser::SerializeTupleStruct for SerializeVec<'lua> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[doc(hidden)]
|
||||||
pub struct SerializeTupleVariant<'lua> {
|
pub struct SerializeTupleVariant<'lua> {
|
||||||
name: String<'lua>,
|
name: String<'lua>,
|
||||||
table: Table<'lua>,
|
table: Table<'lua>,
|
||||||
|
options: Options,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'lua> ser::SerializeTupleVariant for SerializeTupleVariant<'lua> {
|
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<()>
|
fn serialize_field<T>(&mut self, value: &T) -> Result<()>
|
||||||
where
|
where
|
||||||
T: ?Sized + Serialize,
|
T: Serialize + ?Sized,
|
||||||
{
|
{
|
||||||
let lua = self.table.0.lua;
|
let lua = self.table.0.lua;
|
||||||
let idx = self.table.raw_len() + 1;
|
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>> {
|
fn end(self) -> Result<Value<'lua>> {
|
||||||
@@ -286,9 +395,11 @@ impl<'lua> ser::SerializeTupleVariant for SerializeTupleVariant<'lua> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[doc(hidden)]
|
||||||
pub struct SerializeMap<'lua> {
|
pub struct SerializeMap<'lua> {
|
||||||
table: Table<'lua>,
|
table: Table<'lua>,
|
||||||
key: Option<Value<'lua>>,
|
key: Option<Value<'lua>>,
|
||||||
|
options: Options,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'lua> ser::SerializeMap for SerializeMap<'lua> {
|
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<()>
|
fn serialize_key<T>(&mut self, key: &T) -> Result<()>
|
||||||
where
|
where
|
||||||
T: ?Sized + Serialize,
|
T: Serialize + ?Sized,
|
||||||
{
|
{
|
||||||
let lua = self.table.0.lua;
|
let lua = self.table.0.lua;
|
||||||
self.key = Some(lua.to_value(key)?);
|
self.key = Some(lua.to_value_with(key, self.options)?);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn serialize_value<T>(&mut self, value: &T) -> Result<()>
|
fn serialize_value<T>(&mut self, value: &T) -> Result<()>
|
||||||
where
|
where
|
||||||
T: ?Sized + Serialize,
|
T: Serialize + ?Sized,
|
||||||
{
|
{
|
||||||
let lua = self.table.0.lua;
|
let lua = self.table.0.lua;
|
||||||
let key = mlua_expect!(
|
let key = mlua_expect!(
|
||||||
self.key.take(),
|
self.key.take(),
|
||||||
"serialize_value called before serialize_key"
|
"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)
|
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<()>
|
fn serialize_field<T>(&mut self, key: &'static str, value: &T) -> Result<()>
|
||||||
where
|
where
|
||||||
T: ?Sized + Serialize,
|
T: Serialize + ?Sized,
|
||||||
{
|
{
|
||||||
ser::SerializeMap::serialize_key(self, key)?;
|
ser::SerializeMap::serialize_key(self, key)?;
|
||||||
ser::SerializeMap::serialize_value(self, value)
|
ser::SerializeMap::serialize_value(self, value)
|
||||||
@@ -339,9 +450,11 @@ impl<'lua> ser::SerializeStruct for SerializeMap<'lua> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[doc(hidden)]
|
||||||
pub struct SerializeStructVariant<'lua> {
|
pub struct SerializeStructVariant<'lua> {
|
||||||
name: String<'lua>,
|
name: String<'lua>,
|
||||||
table: Table<'lua>,
|
table: Table<'lua>,
|
||||||
|
options: Options,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'lua> ser::SerializeStructVariant for SerializeStructVariant<'lua> {
|
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<()>
|
fn serialize_field<T>(&mut self, key: &'static str, value: &T) -> Result<()>
|
||||||
where
|
where
|
||||||
T: ?Sized + Serialize,
|
T: Serialize + ?Sized,
|
||||||
{
|
{
|
||||||
let lua = self.table.0.lua;
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+3
-3
@@ -39,17 +39,17 @@ impl StdLib {
|
|||||||
#[cfg(any(feature = "luajit", doc))]
|
#[cfg(any(feature = "luajit", doc))]
|
||||||
pub const JIT: StdLib = StdLib(1 << 9);
|
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"`
|
/// Requires `feature = "luajit"`
|
||||||
#[cfg(any(feature = "luajit", doc))]
|
#[cfg(any(feature = "luajit", doc))]
|
||||||
pub const FFI: StdLib = StdLib(1 << 30);
|
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);
|
pub const DEBUG: StdLib = StdLib(1 << 31);
|
||||||
|
|
||||||
/// No libraries
|
/// No libraries
|
||||||
pub const NONE: StdLib = StdLib(0);
|
pub const NONE: StdLib = StdLib(0);
|
||||||
/// (unsafe) All standard libraries
|
/// (**unsafe**) All standard libraries
|
||||||
pub const ALL: StdLib = StdLib(u32::MAX);
|
pub const ALL: StdLib = StdLib(u32::MAX);
|
||||||
/// The safe subset of the standard libraries
|
/// The safe subset of the standard libraries
|
||||||
pub const ALL_SAFE: StdLib = StdLib((1 << 30) - 1);
|
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};
|
use std::{slice, str};
|
||||||
|
|
||||||
#[cfg(feature = "serialize")]
|
#[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.
|
/// Get the bytes that make up this string.
|
||||||
///
|
///
|
||||||
/// The returned slice will not contain the terminating nul byte, but will contain any nul
|
/// The returned slice will not contain the terminating nul byte, but will contain any nul
|
||||||
|
|||||||
+82
-104
@@ -1,5 +1,4 @@
|
|||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
use std::os::raw::c_int;
|
|
||||||
|
|
||||||
#[cfg(feature = "serialize")]
|
#[cfg(feature = "serialize")]
|
||||||
use {
|
use {
|
||||||
@@ -11,7 +10,7 @@ use crate::error::{Error, Result};
|
|||||||
use crate::ffi;
|
use crate::ffi;
|
||||||
use crate::function::Function;
|
use crate::function::Function;
|
||||||
use crate::types::{Integer, LuaRef};
|
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};
|
use crate::value::{FromLua, FromLuaMulti, Nil, ToLua, ToLuaMulti, Value};
|
||||||
|
|
||||||
#[cfg(feature = "async")]
|
#[cfg(feature = "async")]
|
||||||
@@ -60,19 +59,15 @@ impl<'lua> Table<'lua> {
|
|||||||
let lua = self.0.lua;
|
let lua = self.0.lua;
|
||||||
let key = key.to_lua(lua)?;
|
let key = key.to_lua(lua)?;
|
||||||
let value = value.to_lua(lua)?;
|
let value = value.to_lua(lua)?;
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
let _sg = StackGuard::new(lua.state);
|
let _sg = StackGuard::new(lua.state);
|
||||||
assert_stack(lua.state, 6);
|
check_stack(lua.state, 5)?;
|
||||||
|
|
||||||
lua.push_ref(&self.0);
|
lua.push_ref(&self.0);
|
||||||
lua.push_value(key)?;
|
lua.push_value(key)?;
|
||||||
lua.push_value(value)?;
|
lua.push_value(value)?;
|
||||||
|
protect_lua!(lua.state, 3, 0, fn(state) ffi::lua_settable(state, -3))
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -103,18 +98,15 @@ impl<'lua> Table<'lua> {
|
|||||||
pub fn get<K: ToLua<'lua>, V: FromLua<'lua>>(&self, key: K) -> Result<V> {
|
pub fn get<K: ToLua<'lua>, V: FromLua<'lua>>(&self, key: K) -> Result<V> {
|
||||||
let lua = self.0.lua;
|
let lua = self.0.lua;
|
||||||
let key = key.to_lua(lua)?;
|
let key = key.to_lua(lua)?;
|
||||||
|
|
||||||
let value = unsafe {
|
let value = unsafe {
|
||||||
let _sg = StackGuard::new(lua.state);
|
let _sg = StackGuard::new(lua.state);
|
||||||
assert_stack(lua.state, 5);
|
check_stack(lua.state, 4)?;
|
||||||
|
|
||||||
lua.push_ref(&self.0);
|
lua.push_ref(&self.0);
|
||||||
lua.push_value(key)?;
|
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()
|
lua.pop_value()
|
||||||
};
|
};
|
||||||
V::from_lua(value, lua)
|
V::from_lua(value, lua)
|
||||||
@@ -127,19 +119,12 @@ impl<'lua> Table<'lua> {
|
|||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
let _sg = StackGuard::new(lua.state);
|
let _sg = StackGuard::new(lua.state);
|
||||||
assert_stack(lua.state, 5);
|
check_stack(lua.state, 4)?;
|
||||||
|
|
||||||
lua.push_ref(&self.0);
|
lua.push_ref(&self.0);
|
||||||
lua.push_value(key)?;
|
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 {
|
Ok(ffi::lua_isnil(lua.state, -1) == 0)
|
||||||
ffi::lua_gettable(state, -2);
|
|
||||||
1
|
|
||||||
}
|
|
||||||
protect_lua(lua.state, 2, get_table)?;
|
|
||||||
|
|
||||||
let has = ffi::lua_isnil(lua.state, -1) == 0;
|
|
||||||
Ok(has)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -207,19 +192,12 @@ impl<'lua> Table<'lua> {
|
|||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
let _sg = StackGuard::new(lua.state);
|
let _sg = StackGuard::new(lua.state);
|
||||||
assert_stack(lua.state, 6);
|
check_stack(lua.state, 5)?;
|
||||||
|
|
||||||
lua.push_ref(&self.0);
|
lua.push_ref(&self.0);
|
||||||
lua.push_value(key)?;
|
lua.push_value(key)?;
|
||||||
lua.push_value(value)?;
|
lua.push_value(value)?;
|
||||||
|
protect_lua!(lua.state, 3, 0, fn(state) ffi::lua_rawset(state, -3))
|
||||||
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(())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -227,13 +205,15 @@ impl<'lua> Table<'lua> {
|
|||||||
pub fn raw_get<K: ToLua<'lua>, V: FromLua<'lua>>(&self, key: K) -> Result<V> {
|
pub fn raw_get<K: ToLua<'lua>, V: FromLua<'lua>>(&self, key: K) -> Result<V> {
|
||||||
let lua = self.0.lua;
|
let lua = self.0.lua;
|
||||||
let key = key.to_lua(lua)?;
|
let key = key.to_lua(lua)?;
|
||||||
|
|
||||||
let value = unsafe {
|
let value = unsafe {
|
||||||
let _sg = StackGuard::new(lua.state);
|
let _sg = StackGuard::new(lua.state);
|
||||||
assert_stack(lua.state, 3);
|
check_stack(lua.state, 3)?;
|
||||||
|
|
||||||
lua.push_ref(&self.0);
|
lua.push_ref(&self.0);
|
||||||
lua.push_value(key)?;
|
lua.push_value(key)?;
|
||||||
ffi::lua_rawget(lua.state, -2);
|
ffi::lua_rawget(lua.state, -2);
|
||||||
|
|
||||||
lua.pop_value()
|
lua.pop_value()
|
||||||
};
|
};
|
||||||
V::from_lua(value, lua)
|
V::from_lua(value, lua)
|
||||||
@@ -251,18 +231,17 @@ impl<'lua> Table<'lua> {
|
|||||||
let value = value.to_lua(lua)?;
|
let value = value.to_lua(lua)?;
|
||||||
unsafe {
|
unsafe {
|
||||||
let _sg = StackGuard::new(lua.state);
|
let _sg = StackGuard::new(lua.state);
|
||||||
assert_stack(lua.state, 6);
|
check_stack(lua.state, 5)?;
|
||||||
|
|
||||||
lua.push_ref(&self.0);
|
lua.push_ref(&self.0);
|
||||||
lua.push_value(value)?;
|
lua.push_value(value)?;
|
||||||
|
protect_lua!(lua.state, 2, 0, |state| {
|
||||||
protect_lua_closure(lua.state, 2, 0, |state| {
|
for i in (idx..=size).rev() {
|
||||||
for i in (idx..size + 1).rev() {
|
|
||||||
// table[i+1] = table[i]
|
// table[i+1] = table[i]
|
||||||
ffi::lua_rawgeti(state, -2, i);
|
ffi::lua_rawgeti(state, -2, i);
|
||||||
ffi::lua_rawseti(state, -3, i + 1);
|
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.
|
/// Removes a key from the table.
|
||||||
///
|
///
|
||||||
/// If `key` is an integer, mlua shifts down the elements from `table[key+1]`,
|
/// 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.
|
/// 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<()> {
|
pub fn raw_remove<K: ToLua<'lua>>(&self, key: K) -> Result<()> {
|
||||||
let lua = self.0.lua;
|
let lua = self.0.lua;
|
||||||
let key = key.to_lua(lua)?;
|
let key = key.to_lua(lua)?;
|
||||||
@@ -285,11 +264,10 @@ impl<'lua> Table<'lua> {
|
|||||||
}
|
}
|
||||||
unsafe {
|
unsafe {
|
||||||
let _sg = StackGuard::new(lua.state);
|
let _sg = StackGuard::new(lua.state);
|
||||||
assert_stack(lua.state, 6);
|
check_stack(lua.state, 4)?;
|
||||||
|
|
||||||
lua.push_ref(&self.0);
|
lua.push_ref(&self.0);
|
||||||
|
protect_lua!(lua.state, 1, 0, |state| {
|
||||||
protect_lua_closure(lua.state, 1, 0, |state| {
|
|
||||||
for i in idx..size {
|
for i in idx..size {
|
||||||
ffi::lua_rawgeti(state, -1, i + 1);
|
ffi::lua_rawgeti(state, -1, i + 1);
|
||||||
ffi::lua_rawseti(state, -2, i);
|
ffi::lua_rawseti(state, -2, i);
|
||||||
@@ -312,9 +290,10 @@ impl<'lua> Table<'lua> {
|
|||||||
let lua = self.0.lua;
|
let lua = self.0.lua;
|
||||||
unsafe {
|
unsafe {
|
||||||
let _sg = StackGuard::new(lua.state);
|
let _sg = StackGuard::new(lua.state);
|
||||||
assert_stack(lua.state, 4);
|
check_stack(lua.state, 4)?;
|
||||||
|
|
||||||
lua.push_ref(&self.0);
|
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 {
|
unsafe {
|
||||||
let _sg = StackGuard::new(lua.state);
|
let _sg = StackGuard::new(lua.state);
|
||||||
assert_stack(lua.state, 1);
|
assert_stack(lua.state, 1);
|
||||||
|
|
||||||
lua.push_ref(&self.0);
|
lua.push_ref(&self.0);
|
||||||
let len = ffi::lua_rawlen(lua.state, -1);
|
ffi::lua_rawlen(lua.state, -1) as Integer
|
||||||
len as Integer
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -337,13 +316,13 @@ impl<'lua> Table<'lua> {
|
|||||||
let lua = self.0.lua;
|
let lua = self.0.lua;
|
||||||
unsafe {
|
unsafe {
|
||||||
let _sg = StackGuard::new(lua.state);
|
let _sg = StackGuard::new(lua.state);
|
||||||
assert_stack(lua.state, 1);
|
assert_stack(lua.state, 2);
|
||||||
|
|
||||||
lua.push_ref(&self.0);
|
lua.push_ref(&self.0);
|
||||||
if ffi::lua_getmetatable(lua.state, -1) == 0 {
|
if ffi::lua_getmetatable(lua.state, -1) == 0 {
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
let table = Table(lua.pop_ref());
|
Some(Table(lua.pop_ref()))
|
||||||
Some(table)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -356,7 +335,8 @@ impl<'lua> Table<'lua> {
|
|||||||
let lua = self.0.lua;
|
let lua = self.0.lua;
|
||||||
unsafe {
|
unsafe {
|
||||||
let _sg = StackGuard::new(lua.state);
|
let _sg = StackGuard::new(lua.state);
|
||||||
assert_stack(lua.state, 1);
|
assert_stack(lua.state, 2);
|
||||||
|
|
||||||
lua.push_ref(&self.0);
|
lua.push_ref(&self.0);
|
||||||
if let Some(metatable) = metatable {
|
if let Some(metatable) = metatable {
|
||||||
lua.push_ref(&metatable.0);
|
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> {
|
pub fn pairs<K: FromLua<'lua>, V: FromLua<'lua>>(self) -> TablePairs<'lua, K, V> {
|
||||||
TablePairs {
|
TablePairs {
|
||||||
table: self.0,
|
table: self.0,
|
||||||
next_key: Some(Nil),
|
key: Some(Nil),
|
||||||
_phantom: PhantomData,
|
_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.
|
/// 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
|
/// 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`
|
/// `__index` metamethod according to the usual rules. However, the deprecated `__ipairs`
|
||||||
/// metatable will not be called.
|
/// metatable will not be called.
|
||||||
///
|
///
|
||||||
@@ -474,9 +454,12 @@ impl<'lua> Table<'lua> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "serialize")]
|
#[cfg(any(feature = "async", feature = "serialize"))]
|
||||||
pub(crate) fn raw_sequence_values_by_len<V: FromLua<'lua>>(self) -> TableSequence<'lua, V> {
|
pub(crate) fn raw_sequence_values_by_len<V: FromLua<'lua>>(
|
||||||
let len = self.raw_len();
|
self,
|
||||||
|
len: Option<Integer>,
|
||||||
|
) -> TableSequence<'lua, V> {
|
||||||
|
let len = len.unwrap_or_else(|| self.raw_len());
|
||||||
TableSequence {
|
TableSequence {
|
||||||
table: self.0,
|
table: self.0,
|
||||||
index: Some(1),
|
index: Some(1),
|
||||||
@@ -492,6 +475,7 @@ impl<'lua> Table<'lua> {
|
|||||||
unsafe {
|
unsafe {
|
||||||
let _sg = StackGuard::new(lua.state);
|
let _sg = StackGuard::new(lua.state);
|
||||||
assert_stack(lua.state, 3);
|
assert_stack(lua.state, 3);
|
||||||
|
|
||||||
lua.push_ref(&self.0);
|
lua.push_ref(&self.0);
|
||||||
if ffi::lua_getmetatable(lua.state, -1) == 0 {
|
if ffi::lua_getmetatable(lua.state, -1) == 0 {
|
||||||
return false;
|
return false;
|
||||||
@@ -641,7 +625,7 @@ impl<'lua> Serialize for Table<'lua> {
|
|||||||
let len = self.raw_len() as usize;
|
let len = self.raw_len() as usize;
|
||||||
if len > 0 || self.is_array() {
|
if len > 0 || self.is_array() {
|
||||||
let mut seq = serializer.serialize_seq(Some(len))?;
|
let mut seq = serializer.serialize_seq(Some(len))?;
|
||||||
for v in self.clone().raw_sequence_values_by_len::<Value>() {
|
for v in self.clone().raw_sequence_values_by_len::<Value>(None) {
|
||||||
let v = v.map_err(serde::ser::Error::custom)?;
|
let v = v.map_err(serde::ser::Error::custom)?;
|
||||||
seq.serialize_element(&v)?;
|
seq.serialize_element(&v)?;
|
||||||
}
|
}
|
||||||
@@ -664,7 +648,7 @@ impl<'lua> Serialize for Table<'lua> {
|
|||||||
/// [`Table::pairs`]: struct.Table.html#method.pairs
|
/// [`Table::pairs`]: struct.Table.html#method.pairs
|
||||||
pub struct TablePairs<'lua, K, V> {
|
pub struct TablePairs<'lua, K, V> {
|
||||||
table: LuaRef<'lua>,
|
table: LuaRef<'lua>,
|
||||||
next_key: Option<Value<'lua>>,
|
key: Option<Value<'lua>>,
|
||||||
_phantom: PhantomData<(K, V)>,
|
_phantom: PhantomData<(K, V)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -676,41 +660,37 @@ where
|
|||||||
type Item = Result<(K, V)>;
|
type Item = Result<(K, V)>;
|
||||||
|
|
||||||
fn next(&mut self) -> Option<Self::Item> {
|
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 lua = self.table.lua;
|
||||||
|
|
||||||
let res = (|| {
|
let res = (|| unsafe {
|
||||||
let res = unsafe {
|
let _sg = StackGuard::new(lua.state);
|
||||||
let _sg = StackGuard::new(lua.state);
|
check_stack(lua.state, 5)?;
|
||||||
assert_stack(lua.state, 6);
|
|
||||||
|
|
||||||
lua.push_ref(&self.table);
|
lua.push_ref(&self.table);
|
||||||
lua.push_value(next_key)?;
|
lua.push_value(prev_key)?;
|
||||||
|
|
||||||
let next = protect_lua_closure(lua.state, 2, ffi::LUA_MULTRET, |state| {
|
let next = protect_lua!(lua.state, 2, ffi::LUA_MULTRET, |state| {
|
||||||
ffi::lua_next(state, -2) != 0
|
ffi::lua_next(state, -2)
|
||||||
})?;
|
})?;
|
||||||
if next {
|
if next != 0 {
|
||||||
ffi::lua_pushvalue(lua.state, -2);
|
let value = lua.pop_value();
|
||||||
let key = lua.pop_value();
|
let key = lua.pop_value();
|
||||||
let value = lua.pop_value();
|
Ok(Some((
|
||||||
self.next_key = Some(lua.pop_value());
|
key.clone(),
|
||||||
|
K::from_lua(key, lua)?,
|
||||||
Some((key, value))
|
V::from_lua(value, lua)?,
|
||||||
} else {
|
)))
|
||||||
None
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(if let Some((key, value)) = res {
|
|
||||||
Some((K::from_lua(key, lua)?, V::from_lua(value, lua)?))
|
|
||||||
} else {
|
} else {
|
||||||
None
|
Ok(None)
|
||||||
})
|
}
|
||||||
})();
|
})();
|
||||||
|
|
||||||
match res {
|
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,
|
Ok(None) => None,
|
||||||
Err(e) => Some(Err(e)),
|
Err(e) => Some(Err(e)),
|
||||||
}
|
}
|
||||||
@@ -743,31 +723,29 @@ where
|
|||||||
if let Some(index) = self.index.take() {
|
if let Some(index) = self.index.take() {
|
||||||
let lua = self.table.lua;
|
let lua = self.table.lua;
|
||||||
|
|
||||||
let res = unsafe {
|
let res = (|| unsafe {
|
||||||
let _sg = StackGuard::new(lua.state);
|
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);
|
lua.push_ref(&self.table);
|
||||||
let lua_geti = if self.raw {
|
let res = if self.raw {
|
||||||
ffi::lua_rawgeti
|
ffi::lua_rawgeti(lua.state, -1, index)
|
||||||
} else {
|
} 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)) {
|
match res {
|
||||||
Ok(ffi::LUA_TNIL) if index > self.len.unwrap_or(0) => None,
|
ffi::LUA_TNIL if index > self.len.unwrap_or(0) => Ok(None),
|
||||||
Ok(_) => {
|
_ => Ok(Some((index, lua.pop_value()))),
|
||||||
let value = lua.pop_value();
|
|
||||||
self.index = Some(index + 1);
|
|
||||||
Some(Ok(value))
|
|
||||||
}
|
|
||||||
Err(err) => Some(Err(err)),
|
|
||||||
}
|
}
|
||||||
};
|
})();
|
||||||
|
|
||||||
match res {
|
match res {
|
||||||
Some(Ok(r)) => Some(V::from_lua(r, lua)),
|
Ok(Some((index, r))) => {
|
||||||
Some(Err(err)) => Some(Err(err)),
|
self.index = Some(index + 1);
|
||||||
None => None,
|
Some(V::from_lua(r, lua))
|
||||||
|
}
|
||||||
|
Ok(None) => None,
|
||||||
|
Err(err) => Some(Err(err)),
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
|
|||||||
+74
-52
@@ -1,25 +1,27 @@
|
|||||||
|
use std::cmp;
|
||||||
use std::os::raw::c_int;
|
use std::os::raw::c_int;
|
||||||
|
|
||||||
use crate::error::{Error, Result};
|
use crate::error::{Error, Result};
|
||||||
use crate::ffi;
|
use crate::ffi;
|
||||||
use crate::types::LuaRef;
|
use crate::types::LuaRef;
|
||||||
use crate::util::{
|
use crate::util::{assert_stack, check_stack, error_traceback, pop_error, StackGuard};
|
||||||
assert_stack, check_stack, error_traceback, pop_error, protect_lua_closure, StackGuard,
|
|
||||||
};
|
|
||||||
use crate::value::{FromLuaMulti, MultiValue, ToLuaMulti};
|
use crate::value::{FromLuaMulti, MultiValue, ToLuaMulti};
|
||||||
|
|
||||||
|
#[cfg(any(feature = "lua54", all(feature = "luajit", feature = "vendored"), doc))]
|
||||||
|
use crate::function::Function;
|
||||||
|
|
||||||
#[cfg(feature = "async")]
|
#[cfg(feature = "async")]
|
||||||
use {
|
use {
|
||||||
crate::{
|
crate::{
|
||||||
error::ExternalError,
|
lua::{ASYNC_POLL_PENDING, WAKER_REGISTRY_KEY},
|
||||||
lua::{AsyncPollPending, Lua, WAKER_REGISTRY_KEY},
|
util::get_gc_userdata,
|
||||||
util::{get_gc_userdata, push_gc_userdata},
|
|
||||||
value::Value,
|
value::Value,
|
||||||
},
|
},
|
||||||
futures_core::{future::Future, stream::Stream},
|
futures_core::{future::Future, stream::Stream},
|
||||||
std::{
|
std::{
|
||||||
cell::RefCell,
|
cell::RefCell,
|
||||||
marker::PhantomData,
|
marker::PhantomData,
|
||||||
|
mem,
|
||||||
os::raw::c_void,
|
os::raw::c_void,
|
||||||
pin::Pin,
|
pin::Pin,
|
||||||
task::{Context, Poll, Waker},
|
task::{Context, Poll, Waker},
|
||||||
@@ -109,24 +111,21 @@ impl<'lua> Thread<'lua> {
|
|||||||
{
|
{
|
||||||
let lua = self.0.lua;
|
let lua = self.0.lua;
|
||||||
let args = args.to_lua_multi(lua)?;
|
let args = args.to_lua_multi(lua)?;
|
||||||
|
let nargs = args.len() as c_int;
|
||||||
let results = unsafe {
|
let results = unsafe {
|
||||||
let _sg = StackGuard::new(lua.state);
|
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);
|
lua.push_ref(&self.0);
|
||||||
let thread_state = ffi::lua_tothread(lua.state, -1);
|
let thread_state = ffi::lua_tothread(lua.state, -1);
|
||||||
|
ffi::lua_pop(lua.state, 1);
|
||||||
|
|
||||||
let status = ffi::lua_status(thread_state);
|
let status = ffi::lua_status(thread_state);
|
||||||
if status != ffi::LUA_YIELD && ffi::lua_gettop(thread_state) == 0 {
|
if status != ffi::LUA_YIELD && ffi::lua_gettop(thread_state) == 0 {
|
||||||
return Err(Error::CoroutineInactive);
|
return Err(Error::CoroutineInactive);
|
||||||
}
|
}
|
||||||
|
|
||||||
ffi::lua_pop(lua.state, 1);
|
check_stack(thread_state, nargs)?;
|
||||||
|
|
||||||
let nargs = args.len() as c_int;
|
|
||||||
check_stack(lua.state, nargs)?;
|
|
||||||
check_stack(thread_state, nargs + 1)?;
|
|
||||||
|
|
||||||
for arg in args {
|
for arg in args {
|
||||||
lua.push_value(arg)?;
|
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);
|
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 {
|
if ret != ffi::LUA_OK && ret != ffi::LUA_YIELD {
|
||||||
protect_lua_closure(lua.state, 0, 0, |_| {
|
protect_lua!(lua.state, 0, 0, |_| error_traceback(thread_state))?;
|
||||||
error_traceback(thread_state);
|
|
||||||
0
|
|
||||||
})?;
|
|
||||||
return Err(pop_error(thread_state, ret));
|
return Err(pop_error(thread_state, ret));
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut results = MultiValue::new();
|
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);
|
ffi::lua_xmove(thread_state, lua.state, nresults);
|
||||||
|
|
||||||
assert_stack(lua.state, 2);
|
|
||||||
for _ in 0..nresults {
|
for _ in 0..nresults {
|
||||||
results.push_front(lua.pop_value());
|
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.
|
/// Converts Thread to an AsyncThread which implements Future and Stream traits.
|
||||||
///
|
///
|
||||||
/// `args` are passed as arguments to the thread function for first call.
|
/// `args` are passed as arguments to the thread function for first call.
|
||||||
@@ -225,7 +258,7 @@ impl<'lua> Thread<'lua> {
|
|||||||
A: ToLuaMulti<'lua>,
|
A: ToLuaMulti<'lua>,
|
||||||
R: FromLuaMulti<'lua>,
|
R: FromLuaMulti<'lua>,
|
||||||
{
|
{
|
||||||
let args = args.to_lua_multi(&self.0.lua);
|
let args = args.to_lua_multi(self.0.lua);
|
||||||
AsyncThread {
|
AsyncThread {
|
||||||
thread: self,
|
thread: self,
|
||||||
args0: RefCell::new(Some(args)),
|
args0: RefCell::new(Some(args)),
|
||||||
@@ -262,7 +295,7 @@ where
|
|||||||
self.thread.resume(())?
|
self.thread.resume(())?
|
||||||
};
|
};
|
||||||
|
|
||||||
if is_poll_pending(lua, &ret) {
|
if is_poll_pending(&ret) {
|
||||||
return Poll::Pending;
|
return Poll::Pending;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -283,7 +316,7 @@ where
|
|||||||
|
|
||||||
match self.thread.status() {
|
match self.thread.status() {
|
||||||
ThreadStatus::Resumable => {}
|
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());
|
let _wg = WakerGuard::new(lua.state, cx.waker().clone());
|
||||||
@@ -293,7 +326,7 @@ where
|
|||||||
self.thread.resume(())?
|
self.thread.resume(())?
|
||||||
};
|
};
|
||||||
|
|
||||||
if is_poll_pending(lua, &ret) {
|
if is_poll_pending(&ret) {
|
||||||
return Poll::Pending;
|
return Poll::Pending;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -308,44 +341,32 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "async")]
|
#[cfg(feature = "async")]
|
||||||
fn is_poll_pending(lua: &Lua, val: &MultiValue) -> bool {
|
#[inline(always)]
|
||||||
if val.len() != 1 {
|
fn is_poll_pending(val: &MultiValue) -> bool {
|
||||||
return false;
|
match val.iter().enumerate().last() {
|
||||||
}
|
Some((1, Value::LightUserData(ud))) => {
|
||||||
|
ud.0 == &ASYNC_POLL_PENDING as *const u8 as *mut c_void
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
_ => false,
|
||||||
}
|
}
|
||||||
|
|
||||||
false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "async")]
|
#[cfg(feature = "async")]
|
||||||
struct WakerGuard(*mut ffi::lua_State);
|
struct WakerGuard(*mut ffi::lua_State, Option<Waker>);
|
||||||
|
|
||||||
#[cfg(feature = "async")]
|
#[cfg(feature = "async")]
|
||||||
impl WakerGuard {
|
impl WakerGuard {
|
||||||
pub fn new(state: *mut ffi::lua_State, waker: Waker) -> Result<WakerGuard> {
|
pub fn new(state: *mut ffi::lua_State, waker: Waker) -> Result<WakerGuard> {
|
||||||
unsafe {
|
unsafe {
|
||||||
let _sg = StackGuard::new(state);
|
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);
|
let waker_key = &WAKER_REGISTRY_KEY as *const u8 as *const c_void;
|
||||||
push_gc_userdata(state, waker)?;
|
ffi::lua_rawgetp(state, ffi::LUA_REGISTRYINDEX, waker_key);
|
||||||
ffi::lua_rawset(state, ffi::LUA_REGISTRYINDEX);
|
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")]
|
#[cfg(feature = "async")]
|
||||||
impl Drop for WakerGuard {
|
impl Drop for WakerGuard {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
|
let state = self.0;
|
||||||
unsafe {
|
unsafe {
|
||||||
let state = self.0;
|
|
||||||
let _sg = StackGuard::new(state);
|
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);
|
let waker_key = &WAKER_REGISTRY_KEY as *const u8 as *const c_void;
|
||||||
ffi::lua_pushnil(state);
|
ffi::lua_rawgetp(state, ffi::LUA_REGISTRYINDEX, waker_key);
|
||||||
ffi::lua_rawset(state, ffi::LUA_REGISTRYINDEX);
|
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::cell::RefCell;
|
||||||
|
use std::hash::{Hash, Hasher};
|
||||||
use std::os::raw::{c_int, c_void};
|
use std::os::raw::{c_int, c_void};
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
use std::{fmt, mem, ptr};
|
use std::{fmt, mem, ptr};
|
||||||
@@ -10,7 +11,6 @@ use crate::error::Result;
|
|||||||
use crate::ffi;
|
use crate::ffi;
|
||||||
use crate::hook::Debug;
|
use crate::hook::Debug;
|
||||||
use crate::lua::Lua;
|
use crate::lua::Lua;
|
||||||
use crate::userdata::UserDataWrapped;
|
|
||||||
use crate::util::{assert_stack, StackGuard};
|
use crate::util::{assert_stack, StackGuard};
|
||||||
use crate::value::MultiValue;
|
use crate::value::MultiValue;
|
||||||
|
|
||||||
@@ -26,13 +26,32 @@ pub struct LightUserData(pub *mut c_void);
|
|||||||
pub(crate) type Callback<'lua, 'a> =
|
pub(crate) type Callback<'lua, 'a> =
|
||||||
Box<dyn Fn(&'lua Lua, MultiValue<'lua>) -> Result<MultiValue<'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")]
|
#[cfg(feature = "async")]
|
||||||
pub(crate) type AsyncCallback<'lua, 'a> =
|
pub(crate) type AsyncCallback<'lua, 'a> =
|
||||||
Box<dyn Fn(&'lua Lua, MultiValue<'lua>) -> LocalBoxFuture<'lua, Result<MultiValue<'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")]
|
#[cfg(feature = "send")]
|
||||||
pub trait MaybeSend: Send {}
|
pub trait MaybeSend: Send {}
|
||||||
@@ -44,9 +63,11 @@ pub trait MaybeSend {}
|
|||||||
#[cfg(not(feature = "send"))]
|
#[cfg(not(feature = "send"))]
|
||||||
impl<T> MaybeSend for T {}
|
impl<T> MaybeSend for T {}
|
||||||
|
|
||||||
|
pub(crate) struct DestructedUserdataMT;
|
||||||
|
|
||||||
/// An auto generated key into the Lua registry.
|
/// 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`],
|
/// 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`].
|
/// 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 {
|
impl Drop for RegistryKey {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
let mut unref_list = mlua_expect!(self.unref_list.lock(), "unref list poisoned");
|
let mut unref_list = mlua_expect!(self.unref_list.lock(), "unref list poisoned");
|
||||||
@@ -120,8 +155,8 @@ impl<'lua> PartialEq for LuaRef<'lua> {
|
|||||||
unsafe {
|
unsafe {
|
||||||
let _sg = StackGuard::new(lua.state);
|
let _sg = StackGuard::new(lua.state);
|
||||||
assert_stack(lua.state, 2);
|
assert_stack(lua.state, 2);
|
||||||
lua.push_ref(&self);
|
lua.push_ref(self);
|
||||||
lua.push_ref(&other);
|
lua.push_ref(other);
|
||||||
ffi::lua_rawequal(lua.state, -1, -2) == 1
|
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")]
|
#[cfg(feature = "async")]
|
||||||
use std::future::Future;
|
use std::future::Future;
|
||||||
@@ -6,6 +11,7 @@ use std::future::Future;
|
|||||||
#[cfg(feature = "serialize")]
|
#[cfg(feature = "serialize")]
|
||||||
use {
|
use {
|
||||||
serde::ser::{self, Serialize, Serializer},
|
serde::ser::{self, Serialize, Serializer},
|
||||||
|
std::os::raw::c_void,
|
||||||
std::result::Result as StdResult,
|
std::result::Result as StdResult,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -13,10 +19,16 @@ use crate::error::{Error, Result};
|
|||||||
use crate::ffi;
|
use crate::ffi;
|
||||||
use crate::function::Function;
|
use crate::function::Function;
|
||||||
use crate::lua::Lua;
|
use crate::lua::Lua;
|
||||||
use crate::table::Table;
|
use crate::table::{Table, TablePairs};
|
||||||
use crate::types::{LuaRef, MaybeSend, UserDataCell};
|
use crate::types::{Callback, LuaRef, MaybeSend};
|
||||||
use crate::util::{assert_stack, get_destructed_userdata_metatable, get_userdata, StackGuard};
|
use crate::util::{check_stack, get_userdata, StackGuard};
|
||||||
use crate::value::{FromLua, FromLuaMulti, ToLua, ToLuaMulti, Value};
|
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.
|
/// 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`.
|
/// generally no need to do so: [`UserData`] implementors can instead just implement `Drop`.
|
||||||
///
|
///
|
||||||
/// [`UserData`]: trait.UserData.html
|
/// [`UserData`]: trait.UserData.html
|
||||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum MetaMethod {
|
pub enum MetaMethod {
|
||||||
/// The `+` operator.
|
/// The `+` operator.
|
||||||
Add,
|
Add,
|
||||||
@@ -93,6 +105,15 @@ pub enum MetaMethod {
|
|||||||
/// Requires `feature = "lua54/lua53/lua52"`
|
/// Requires `feature = "lua54/lua53/lua52"`
|
||||||
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52", doc))]
|
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52", doc))]
|
||||||
Pairs,
|
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.
|
/// The `__close` metamethod.
|
||||||
///
|
///
|
||||||
/// Executed when a variable, that marked as to-be-closed, goes out of scope.
|
/// 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
|
/// [lua_doc]: https://www.lua.org/manual/5.4/manual.html#3.3.8
|
||||||
#[cfg(any(feature = "lua54", doc))]
|
#[cfg(any(feature = "lua54", doc))]
|
||||||
Close,
|
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 {
|
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 {
|
match self {
|
||||||
MetaMethod::Add => b"__add",
|
MetaMethod::Add => "__add",
|
||||||
MetaMethod::Sub => b"__sub",
|
MetaMethod::Sub => "__sub",
|
||||||
MetaMethod::Mul => b"__mul",
|
MetaMethod::Mul => "__mul",
|
||||||
MetaMethod::Div => b"__div",
|
MetaMethod::Div => "__div",
|
||||||
MetaMethod::Mod => b"__mod",
|
MetaMethod::Mod => "__mod",
|
||||||
MetaMethod::Pow => b"__pow",
|
MetaMethod::Pow => "__pow",
|
||||||
MetaMethod::Unm => b"__unm",
|
MetaMethod::Unm => "__unm",
|
||||||
|
|
||||||
#[cfg(any(feature = "lua54", feature = "lua53"))]
|
#[cfg(any(feature = "lua54", feature = "lua53"))]
|
||||||
MetaMethod::IDiv => b"__idiv",
|
MetaMethod::IDiv => "__idiv",
|
||||||
#[cfg(any(feature = "lua54", feature = "lua53"))]
|
#[cfg(any(feature = "lua54", feature = "lua53"))]
|
||||||
MetaMethod::BAnd => b"__band",
|
MetaMethod::BAnd => "__band",
|
||||||
#[cfg(any(feature = "lua54", feature = "lua53"))]
|
#[cfg(any(feature = "lua54", feature = "lua53"))]
|
||||||
MetaMethod::BOr => b"__bor",
|
MetaMethod::BOr => "__bor",
|
||||||
#[cfg(any(feature = "lua54", feature = "lua53"))]
|
#[cfg(any(feature = "lua54", feature = "lua53"))]
|
||||||
MetaMethod::BXor => b"__bxor",
|
MetaMethod::BXor => "__bxor",
|
||||||
#[cfg(any(feature = "lua54", feature = "lua53"))]
|
#[cfg(any(feature = "lua54", feature = "lua53"))]
|
||||||
MetaMethod::BNot => b"__bnot",
|
MetaMethod::BNot => "__bnot",
|
||||||
#[cfg(any(feature = "lua54", feature = "lua53"))]
|
#[cfg(any(feature = "lua54", feature = "lua53"))]
|
||||||
MetaMethod::Shl => b"__shl",
|
MetaMethod::Shl => "__shl",
|
||||||
#[cfg(any(feature = "lua54", feature = "lua53"))]
|
#[cfg(any(feature = "lua54", feature = "lua53"))]
|
||||||
MetaMethod::Shr => b"__shr",
|
MetaMethod::Shr => "__shr",
|
||||||
|
|
||||||
MetaMethod::Concat => b"__concat",
|
MetaMethod::Concat => "__concat",
|
||||||
MetaMethod::Len => b"__len",
|
MetaMethod::Len => "__len",
|
||||||
MetaMethod::Eq => b"__eq",
|
MetaMethod::Eq => "__eq",
|
||||||
MetaMethod::Lt => b"__lt",
|
MetaMethod::Lt => "__lt",
|
||||||
MetaMethod::Le => b"__le",
|
MetaMethod::Le => "__le",
|
||||||
MetaMethod::Index => b"__index",
|
MetaMethod::Index => "__index",
|
||||||
MetaMethod::NewIndex => b"__newindex",
|
MetaMethod::NewIndex => "__newindex",
|
||||||
MetaMethod::Call => b"__call",
|
MetaMethod::Call => "__call",
|
||||||
MetaMethod::ToString => b"__tostring",
|
MetaMethod::ToString => "__tostring",
|
||||||
|
|
||||||
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
|
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
|
||||||
MetaMethod::Pairs => b"__pairs",
|
MetaMethod::Pairs => "__pairs",
|
||||||
|
#[cfg(feature = "lua52")]
|
||||||
|
MetaMethod::IPairs => "__ipairs",
|
||||||
|
|
||||||
#[cfg(feature = "lua54")]
|
#[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.
|
/// Method registry for [`UserData`] implementors.
|
||||||
///
|
///
|
||||||
/// [`UserData`]: trait.UserData.html
|
/// [`UserData`]: trait.UserData.html
|
||||||
pub trait UserDataMethods<'lua, T: UserData> {
|
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
|
/// 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.
|
/// 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.
|
/// 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)
|
fn add_method<S, A, R, M>(&mut self, name: &S, method: M)
|
||||||
where
|
where
|
||||||
S: ?Sized + AsRef<[u8]>,
|
S: AsRef<[u8]> + ?Sized,
|
||||||
A: FromLuaMulti<'lua>,
|
A: FromLuaMulti<'lua>,
|
||||||
R: ToLuaMulti<'lua>,
|
R: ToLuaMulti<'lua>,
|
||||||
M: 'static + MaybeSend + Fn(&'lua Lua, &T, A) -> Result<R>;
|
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
|
/// [`add_method`]: #method.add_method
|
||||||
fn add_method_mut<S, A, R, M>(&mut self, name: &S, method: M)
|
fn add_method_mut<S, A, R, M>(&mut self, name: &S, method: M)
|
||||||
where
|
where
|
||||||
S: ?Sized + AsRef<[u8]>,
|
S: AsRef<[u8]> + ?Sized,
|
||||||
A: FromLuaMulti<'lua>,
|
A: FromLuaMulti<'lua>,
|
||||||
R: ToLuaMulti<'lua>,
|
R: ToLuaMulti<'lua>,
|
||||||
M: 'static + MaybeSend + FnMut(&'lua Lua, &mut T, A) -> Result<R>;
|
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)
|
fn add_async_method<S, A, R, M, MR>(&mut self, name: &S, method: M)
|
||||||
where
|
where
|
||||||
T: Clone,
|
T: Clone,
|
||||||
S: ?Sized + AsRef<[u8]>,
|
S: AsRef<[u8]> + ?Sized,
|
||||||
A: FromLuaMulti<'lua>,
|
A: FromLuaMulti<'lua>,
|
||||||
R: ToLuaMulti<'lua>,
|
R: ToLuaMulti<'lua>,
|
||||||
M: 'static + MaybeSend + Fn(&'lua Lua, T, A) -> MR,
|
M: 'static + MaybeSend + Fn(&'lua Lua, T, A) -> MR,
|
||||||
MR: 'lua + Future<Output = Result<R>>;
|
MR: 'lua + Future<Output = Result<R>>;
|
||||||
|
|
||||||
/// Add a regular method as a function which accepts generic arguments, the first argument will
|
/// 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(arg1, arg2)`, or it is passed in as the first argument:
|
||||||
/// `my_userdata.my_method(my_userdata, arg1, arg2)`.
|
/// `my_userdata.my_method(my_userdata, arg1, arg2)`.
|
||||||
///
|
///
|
||||||
/// Prefer to use [`add_method`] or [`add_method_mut`] as they are easier to use.
|
/// 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`]: #method.add_method
|
||||||
/// [`add_method_mut`]: #method.add_method_mut
|
/// [`add_method_mut`]: #method.add_method_mut
|
||||||
fn add_function<S, A, R, F>(&mut self, name: &S, function: F)
|
fn add_function<S, A, R, F>(&mut self, name: &S, function: F)
|
||||||
where
|
where
|
||||||
S: ?Sized + AsRef<[u8]>,
|
S: AsRef<[u8]> + ?Sized,
|
||||||
A: FromLuaMulti<'lua>,
|
A: FromLuaMulti<'lua>,
|
||||||
R: ToLuaMulti<'lua>,
|
R: ToLuaMulti<'lua>,
|
||||||
F: 'static + MaybeSend + Fn(&'lua Lua, A) -> Result<R>;
|
F: 'static + MaybeSend + Fn(&'lua Lua, A) -> Result<R>;
|
||||||
@@ -224,7 +343,7 @@ pub trait UserDataMethods<'lua, T: UserData> {
|
|||||||
/// [`add_function`]: #method.add_function
|
/// [`add_function`]: #method.add_function
|
||||||
fn add_function_mut<S, A, R, F>(&mut self, name: &S, function: F)
|
fn add_function_mut<S, A, R, F>(&mut self, name: &S, function: F)
|
||||||
where
|
where
|
||||||
S: ?Sized + AsRef<[u8]>,
|
S: AsRef<[u8]> + ?Sized,
|
||||||
A: FromLuaMulti<'lua>,
|
A: FromLuaMulti<'lua>,
|
||||||
R: ToLuaMulti<'lua>,
|
R: ToLuaMulti<'lua>,
|
||||||
F: 'static + MaybeSend + FnMut(&'lua Lua, A) -> Result<R>;
|
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")))]
|
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
|
||||||
fn add_async_function<S, A, R, F, FR>(&mut self, name: &S, function: F)
|
fn add_async_function<S, A, R, F, FR>(&mut self, name: &S, function: F)
|
||||||
where
|
where
|
||||||
T: Clone,
|
S: AsRef<[u8]> + ?Sized,
|
||||||
S: ?Sized + AsRef<[u8]>,
|
|
||||||
A: FromLuaMulti<'lua>,
|
A: FromLuaMulti<'lua>,
|
||||||
R: ToLuaMulti<'lua>,
|
R: ToLuaMulti<'lua>,
|
||||||
F: 'static + MaybeSend + Fn(&'lua Lua, A) -> FR,
|
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`].
|
/// side has a metatable. To prevent this, use [`add_meta_function`].
|
||||||
///
|
///
|
||||||
/// [`add_meta_function`]: #method.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
|
where
|
||||||
|
S: Into<MetaMethod>,
|
||||||
A: FromLuaMulti<'lua>,
|
A: FromLuaMulti<'lua>,
|
||||||
R: ToLuaMulti<'lua>,
|
R: ToLuaMulti<'lua>,
|
||||||
M: 'static + MaybeSend + Fn(&'lua Lua, &T, A) -> Result<R>;
|
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`].
|
/// side has a metatable. To prevent this, use [`add_meta_function`].
|
||||||
///
|
///
|
||||||
/// [`add_meta_function`]: #method.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
|
where
|
||||||
|
S: Into<MetaMethod>,
|
||||||
A: FromLuaMulti<'lua>,
|
A: FromLuaMulti<'lua>,
|
||||||
R: ToLuaMulti<'lua>,
|
R: ToLuaMulti<'lua>,
|
||||||
M: 'static + MaybeSend + FnMut(&'lua Lua, &mut T, A) -> Result<R>;
|
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
|
/// 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
|
/// the binary operator has a metatable, so the first argument here is not necessarily a
|
||||||
/// userdata of type `T`.
|
/// 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
|
where
|
||||||
|
S: Into<MetaMethod>,
|
||||||
A: FromLuaMulti<'lua>,
|
A: FromLuaMulti<'lua>,
|
||||||
R: ToLuaMulti<'lua>,
|
R: ToLuaMulti<'lua>,
|
||||||
F: 'static + MaybeSend + Fn(&'lua Lua, A) -> Result<R>;
|
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.
|
/// This is a version of [`add_meta_function`] that accepts a FnMut argument.
|
||||||
///
|
///
|
||||||
/// [`add_meta_function`]: #method.add_meta_function
|
/// [`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
|
where
|
||||||
|
S: Into<MetaMethod>,
|
||||||
A: FromLuaMulti<'lua>,
|
A: FromLuaMulti<'lua>,
|
||||||
R: ToLuaMulti<'lua>,
|
R: ToLuaMulti<'lua>,
|
||||||
F: 'static + MaybeSend + FnMut(&'lua Lua, A) -> Result<R>;
|
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.
|
/// Trait for custom userdata types.
|
||||||
///
|
///
|
||||||
/// By implementing this trait, a struct becomes eligible for use inside Lua code. Implementations
|
/// By implementing this trait, a struct becomes eligible for use inside Lua code.
|
||||||
/// of [`ToLua`] and [`FromLua`] are automatically provided.
|
/// Implementation of [`ToLua`] is automatically provided, [`FromLua`] is implemented
|
||||||
|
/// only for `T: UserData + Clone`.
|
||||||
|
///
|
||||||
///
|
///
|
||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
@@ -322,21 +541,21 @@ pub trait UserDataMethods<'lua, T: UserData> {
|
|||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
/// Custom methods and operators can be provided by implementing `add_methods` (refer to
|
/// Custom fields, methods and operators can be provided by implementing `add_fields` or `add_methods`
|
||||||
/// [`UserDataMethods`] for more information):
|
/// (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<()> {
|
/// # fn main() -> Result<()> {
|
||||||
/// # let lua = Lua::new();
|
/// # let lua = Lua::new();
|
||||||
/// struct MyUserData(i32);
|
/// struct MyUserData(i32);
|
||||||
///
|
///
|
||||||
/// impl UserData for MyUserData {
|
/// impl UserData for MyUserData {
|
||||||
/// fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
|
/// fn add_fields<'lua, F: UserDataFields<'lua, Self>>(fields: &mut F) {
|
||||||
/// methods.add_method("get", |_, this, _: ()| {
|
/// fields.add_field_method_get("val", |_, this| Ok(this.0));
|
||||||
/// Ok(this.0)
|
/// }
|
||||||
/// });
|
|
||||||
///
|
///
|
||||||
|
/// fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
|
||||||
/// methods.add_method_mut("add", |_, this, value: i32| {
|
/// methods.add_method_mut("add", |_, this, value: i32| {
|
||||||
/// this.0 += value;
|
/// this.0 += value;
|
||||||
/// Ok(())
|
/// Ok(())
|
||||||
@@ -351,9 +570,9 @@ pub trait UserDataMethods<'lua, T: UserData> {
|
|||||||
/// lua.globals().set("myobject", MyUserData(123))?;
|
/// lua.globals().set("myobject", MyUserData(123))?;
|
||||||
///
|
///
|
||||||
/// lua.load(r#"
|
/// lua.load(r#"
|
||||||
/// assert(myobject:get() == 123)
|
/// assert(myobject.val == 123)
|
||||||
/// myobject:add(7)
|
/// myobject:add(7)
|
||||||
/// assert(myobject:get() == 130)
|
/// assert(myobject.val == 130)
|
||||||
/// assert(myobject + 10 == 140)
|
/// assert(myobject + 10 == 140)
|
||||||
/// "#).exec()?;
|
/// "#).exec()?;
|
||||||
/// # Ok(())
|
/// # Ok(())
|
||||||
@@ -362,66 +581,111 @@ pub trait UserDataMethods<'lua, T: UserData> {
|
|||||||
///
|
///
|
||||||
/// [`ToLua`]: trait.ToLua.html
|
/// [`ToLua`]: trait.ToLua.html
|
||||||
/// [`FromLua`]: trait.FromLua.html
|
/// [`FromLua`]: trait.FromLua.html
|
||||||
|
/// [`UserDataFields`]: trait.UserDataFields.html
|
||||||
/// [`UserDataMethods`]: trait.UserDataMethods.html
|
/// [`UserDataMethods`]: trait.UserDataMethods.html
|
||||||
pub trait UserData: Sized {
|
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.
|
/// Adds custom methods and operators specific to this userdata.
|
||||||
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(_methods: &mut M) {}
|
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(_methods: &mut M) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) struct UserDataWrapped<T> {
|
// Wraps UserData in a way to always implement `serde::Serialize` trait.
|
||||||
pub(crate) data: *mut T,
|
pub(crate) struct UserDataCell<T>(RefCell<UserDataWrapped<T>>);
|
||||||
#[cfg(feature = "serialize")]
|
|
||||||
ser: *mut dyn erased_serde::Serialize,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> Drop for UserDataWrapped<T> {
|
impl<T> UserDataCell<T> {
|
||||||
fn drop(&mut self) {
|
#[inline]
|
||||||
unsafe {
|
|
||||||
drop(Box::from_raw(self.data));
|
|
||||||
#[cfg(feature = "serialize")]
|
|
||||||
if self.data as *mut () != self.ser as *mut () {
|
|
||||||
drop(Box::from_raw(self.ser));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> UserDataWrapped<T> {
|
|
||||||
pub(crate) fn new(data: T) -> Self {
|
pub(crate) fn new(data: T) -> Self {
|
||||||
UserDataWrapped {
|
UserDataCell(RefCell::new(UserDataWrapped::new(data)))
|
||||||
data: Box::into_raw(Box::new(data)),
|
|
||||||
#[cfg(feature = "serialize")]
|
|
||||||
ser: Box::into_raw(Box::new(UserDataSerializeError)),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "serialize")]
|
#[cfg(feature = "serialize")]
|
||||||
|
#[inline]
|
||||||
pub(crate) fn new_ser(data: T) -> Self
|
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
|
where
|
||||||
T: 'static + Serialize,
|
T: 'static + Serialize,
|
||||||
{
|
{
|
||||||
let data_raw = Box::into_raw(Box::new(data));
|
let data_raw = Box::into_raw(Box::new(data));
|
||||||
UserDataWrapped {
|
UserDataWrapped::Serializable(data_raw, data_raw)
|
||||||
data: data_raw,
|
|
||||||
ser: data_raw,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> AsRef<T> for UserDataWrapped<T> {
|
|
||||||
fn as_ref(&self) -> &T {
|
|
||||||
unsafe { &*self.data }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> AsMut<T> for UserDataWrapped<T> {
|
|
||||||
fn as_mut(&mut self) -> &mut T {
|
|
||||||
unsafe { &mut *self.data }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "serialize")]
|
#[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")]
|
#[cfg(feature = "serialize")]
|
||||||
impl Serialize for UserDataSerializeError {
|
impl Serialize for UserDataSerializeError {
|
||||||
@@ -468,26 +732,20 @@ impl<'lua> AnyUserData<'lua> {
|
|||||||
///
|
///
|
||||||
/// Returns a `UserDataBorrowError` if the userdata is already mutably borrowed. Returns a
|
/// Returns a `UserDataBorrowError` if the userdata is already mutably borrowed. Returns a
|
||||||
/// `UserDataTypeMismatch` if the userdata is not of type `T`.
|
/// `UserDataTypeMismatch` if the userdata is not of type `T`.
|
||||||
|
#[inline]
|
||||||
pub fn borrow<T: 'static + UserData>(&self) -> Result<Ref<T>> {
|
pub fn borrow<T: 'static + UserData>(&self) -> Result<Ref<T>> {
|
||||||
self.inspect(|cell| {
|
self.inspect(|cell| cell.try_borrow())
|
||||||
let cell_ref = cell.try_borrow().map_err(|_| Error::UserDataBorrowError)?;
|
|
||||||
Ok(Ref::map(cell_ref, |x| unsafe { &*x.data }))
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Borrow this userdata mutably if it is of type `T`.
|
/// Borrow this userdata mutably if it is of type `T`.
|
||||||
///
|
///
|
||||||
/// # Errors
|
/// # Errors
|
||||||
///
|
///
|
||||||
/// Returns a `UserDataBorrowMutError` if the userdata is already borrowed. Returns a
|
/// Returns a `UserDataBorrowMutError` if the userdata cannot be mutably borrowed.
|
||||||
/// `UserDataTypeMismatch` if the userdata is not of type `T`.
|
/// Returns a `UserDataTypeMismatch` if the userdata is not of type `T`.
|
||||||
|
#[inline]
|
||||||
pub fn borrow_mut<T: 'static + UserData>(&self) -> Result<RefMut<T>> {
|
pub fn borrow_mut<T: 'static + UserData>(&self) -> Result<RefMut<T>> {
|
||||||
self.inspect(|cell| {
|
self.inspect(|cell| cell.try_borrow_mut())
|
||||||
let cell_ref = cell
|
|
||||||
.try_borrow_mut()
|
|
||||||
.map_err(|_| Error::UserDataBorrowMutError)?;
|
|
||||||
Ok(RefMut::map(cell_ref, |x| unsafe { &mut *x.data }))
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets an associated value to this `AnyUserData`.
|
/// Sets an associated value to this `AnyUserData`.
|
||||||
@@ -500,19 +758,21 @@ impl<'lua> AnyUserData<'lua> {
|
|||||||
let lua = self.0.lua;
|
let lua = self.0.lua;
|
||||||
#[cfg(any(feature = "lua52", feature = "lua51", feature = "luajit"))]
|
#[cfg(any(feature = "lua52", feature = "lua51", feature = "luajit"))]
|
||||||
let v = {
|
let v = {
|
||||||
// Lua 5.2/5.1 allows to store only a table. Then we will wrap the value.
|
// Lua <= 5.2 allows to store only a table. Then we will wrap the value.
|
||||||
let t = lua.create_table()?;
|
let t = lua.create_table_with_capacity(1, 0)?;
|
||||||
t.raw_set(1, v)?;
|
t.raw_set(1, v)?;
|
||||||
crate::Value::Table(t)
|
Value::Table(t)
|
||||||
};
|
};
|
||||||
#[cfg(any(feature = "lua54", feature = "lua53"))]
|
#[cfg(any(feature = "lua54", feature = "lua53"))]
|
||||||
let v = v.to_lua(lua)?;
|
let v = v.to_lua(lua)?;
|
||||||
unsafe {
|
unsafe {
|
||||||
let _sg = StackGuard::new(lua.state);
|
let _sg = StackGuard::new(lua.state);
|
||||||
assert_stack(lua.state, 2);
|
check_stack(lua.state, 3)?;
|
||||||
lua.push_ref(&self.0);
|
|
||||||
|
lua.push_userdata_ref(&self.0)?;
|
||||||
lua.push_value(v)?;
|
lua.push_value(v)?;
|
||||||
ffi::lua_setuservalue(lua.state, -2);
|
ffi::lua_setuservalue(lua.state, -2);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -526,57 +786,54 @@ impl<'lua> AnyUserData<'lua> {
|
|||||||
let lua = self.0.lua;
|
let lua = self.0.lua;
|
||||||
let res = unsafe {
|
let res = unsafe {
|
||||||
let _sg = StackGuard::new(lua.state);
|
let _sg = StackGuard::new(lua.state);
|
||||||
assert_stack(lua.state, 3);
|
check_stack(lua.state, 3)?;
|
||||||
lua.push_ref(&self.0);
|
|
||||||
|
lua.push_userdata_ref(&self.0)?;
|
||||||
ffi::lua_getuservalue(lua.state, -1);
|
ffi::lua_getuservalue(lua.state, -1);
|
||||||
lua.pop_value()
|
lua.pop_value()
|
||||||
};
|
};
|
||||||
#[cfg(any(feature = "lua52", feature = "lua51", feature = "luajit"))]
|
#[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"))]
|
#[cfg(any(feature = "lua54", feature = "lua53"))]
|
||||||
V::from_lua(res, lua)
|
V::from_lua(res, lua)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Checks for a metamethod in this `AnyUserData`
|
/// Returns a metatable of this `UserData`.
|
||||||
pub fn has_metamethod(&self, method: MetaMethod) -> Result<bool> {
|
///
|
||||||
match self.get_metatable() {
|
/// Returned [`UserDataMetatable`] object wraps the original metatable and
|
||||||
Ok(mt) => {
|
/// provides safe access to its methods.
|
||||||
let name = self.0.lua.create_string(method.name())?;
|
///
|
||||||
if let Value::Nil = mt.raw_get(name)? {
|
/// For `T: UserData + 'static` returned metatable is shared among all instances of type `T`.
|
||||||
Ok(false)
|
///
|
||||||
} else {
|
/// [`UserDataMetatable`]: struct.UserDataMetatable.html
|
||||||
Ok(true)
|
pub fn get_metatable(&self) -> Result<UserDataMetatable<'lua>> {
|
||||||
}
|
self.get_raw_metatable().map(UserDataMetatable)
|
||||||
}
|
|
||||||
Err(Error::UserDataTypeMismatch) => Ok(false),
|
|
||||||
Err(e) => Err(e),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_metatable(&self) -> Result<Table<'lua>> {
|
fn get_raw_metatable(&self) -> Result<Table<'lua>> {
|
||||||
unsafe {
|
unsafe {
|
||||||
let lua = self.0.lua;
|
let lua = self.0.lua;
|
||||||
let _sg = StackGuard::new(lua.state);
|
let _sg = StackGuard::new(lua.state);
|
||||||
assert_stack(lua.state, 3);
|
check_stack(lua.state, 3)?;
|
||||||
|
|
||||||
lua.push_ref(&self.0);
|
|
||||||
|
|
||||||
if ffi::lua_getmetatable(lua.state, -1) == 0 {
|
|
||||||
return Err(Error::UserDataTypeMismatch);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
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()))
|
Ok(Table(lua.pop_ref()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn equals<T: AsRef<Self>>(&self, other: T) -> Result<bool> {
|
pub(crate) fn equals<T: AsRef<Self>>(&self, other: T) -> Result<bool> {
|
||||||
let other = other.as_ref();
|
let other = other.as_ref();
|
||||||
|
// Uses lua_rawequal() under the hood
|
||||||
if self == other {
|
if self == other {
|
||||||
return Ok(true);
|
return Ok(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mt = self.get_metatable()?;
|
let mt = self.get_raw_metatable()?;
|
||||||
if mt != other.get_metatable()? {
|
if mt != other.get_raw_metatable()? {
|
||||||
return Ok(false);
|
return Ok(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -594,34 +851,17 @@ impl<'lua> AnyUserData<'lua> {
|
|||||||
T: 'static + UserData,
|
T: 'static + UserData,
|
||||||
F: FnOnce(&'a UserDataCell<T>) -> Result<R>,
|
F: FnOnce(&'a UserDataCell<T>) -> Result<R>,
|
||||||
{
|
{
|
||||||
|
let lua = self.0.lua;
|
||||||
unsafe {
|
unsafe {
|
||||||
let lua = self.0.lua;
|
|
||||||
let _sg = StackGuard::new(lua.state);
|
let _sg = StackGuard::new(lua.state);
|
||||||
assert_stack(lua.state, 3);
|
check_stack(lua.state, 2)?;
|
||||||
|
|
||||||
lua.push_ref(&self.0);
|
let type_id = lua.push_userdata_ref(&self.0)?;
|
||||||
|
match type_id {
|
||||||
if ffi::lua_getmetatable(lua.state, -1) == 0 {
|
Some(type_id) if type_id == TypeId::of::<T>() => {
|
||||||
Err(Error::UserDataTypeMismatch)
|
func(&*get_userdata::<UserDataCell<T>>(lua.state, -1))
|
||||||
} 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))
|
|
||||||
}
|
}
|
||||||
|
_ => 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")]
|
#[cfg(feature = "serialize")]
|
||||||
impl<'lua> Serialize for AnyUserData<'lua> {
|
impl<'lua> Serialize for AnyUserData<'lua> {
|
||||||
fn serialize<S>(&self, serializer: S) -> StdResult<S::Ok, S::Error>
|
fn serialize<S>(&self, serializer: S) -> StdResult<S::Ok, S::Error>
|
||||||
where
|
where
|
||||||
S: Serializer,
|
S: Serializer,
|
||||||
{
|
{
|
||||||
let f = || unsafe {
|
unsafe {
|
||||||
let lua = self.0.lua;
|
let lua = self.0.lua;
|
||||||
let _sg = StackGuard::new(lua.state);
|
let _sg = StackGuard::new(lua.state);
|
||||||
assert_stack(lua.state, 2);
|
check_stack(lua.state, 3).map_err(ser::Error::custom)?;
|
||||||
|
|
||||||
lua.push_userdata_ref(&self.0)?;
|
lua.push_userdata_ref(&self.0).map_err(ser::Error::custom)?;
|
||||||
let ud = &*get_userdata::<UserDataCell<()>>(lua.state, -1);
|
let ud = &*get_userdata::<UserDataCell<c_void>>(lua.state, -1);
|
||||||
(*ud.try_borrow().map_err(|_| Error::UserDataBorrowError)?.ser)
|
let data =
|
||||||
.serialize(serializer)
|
ud.0.try_borrow()
|
||||||
.map_err(|err| Error::SerializeError(err.to_string()))
|
.map_err(|_| ser::Error::custom(Error::UserDataBorrowError))?;
|
||||||
};
|
match *data {
|
||||||
f().map_err(ser::Error::custom)
|
UserDataWrapped::Default(_) => UserDataSerializeError.serialize(serializer),
|
||||||
|
UserDataWrapped::Serializable(_, ser) => (&*ser).serialize(serializer),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+524
-378
File diff suppressed because it is too large
Load Diff
+21
-8
@@ -8,7 +8,6 @@ use {
|
|||||||
};
|
};
|
||||||
|
|
||||||
use crate::error::{Error, Result};
|
use crate::error::{Error, Result};
|
||||||
use crate::ffi;
|
|
||||||
use crate::function::Function;
|
use crate::function::Function;
|
||||||
use crate::lua::Lua;
|
use crate::lua::Lua;
|
||||||
use crate::string::String;
|
use crate::string::String;
|
||||||
@@ -17,10 +16,9 @@ use crate::thread::Thread;
|
|||||||
use crate::types::{Integer, LightUserData, Number};
|
use crate::types::{Integer, LightUserData, Number};
|
||||||
use crate::userdata::AnyUserData;
|
use crate::userdata::AnyUserData;
|
||||||
|
|
||||||
/// A dynamically typed Lua value. The `String`, `Table`, `Function`, `Thread`, and `UserData`
|
/// 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
|
/// 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
|
/// types between separate `Lua` instances, and doing so will result in a panic.
|
||||||
/// parameter in a Rust callback, and doing so will result in a panic.
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum Value<'lua> {
|
pub enum Value<'lua> {
|
||||||
/// The Lua value `nil`.
|
/// 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`.
|
/// Reference to a userdata object that holds a custom type which implements `UserData`.
|
||||||
/// Special builtin userdata types will be represented as other `Value` variants.
|
/// Special builtin userdata types will be represented as other `Value` variants.
|
||||||
UserData(AnyUserData<'lua>),
|
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),
|
Error(Error),
|
||||||
}
|
}
|
||||||
pub use self::Value::Nil;
|
pub use self::Value::Nil;
|
||||||
@@ -96,8 +94,8 @@ impl<'lua> PartialEq for Value<'lua> {
|
|||||||
(Value::Boolean(a), Value::Boolean(b)) => a == b,
|
(Value::Boolean(a), Value::Boolean(b)) => a == b,
|
||||||
(Value::LightUserData(a), Value::LightUserData(b)) => a == b,
|
(Value::LightUserData(a), Value::LightUserData(b)) => a == b,
|
||||||
(Value::Integer(a), Value::Integer(b)) => *a == *b,
|
(Value::Integer(a), Value::Integer(b)) => *a == *b,
|
||||||
(Value::Integer(a), Value::Number(b)) => *a as ffi::lua_Number == *b,
|
(Value::Integer(a), Value::Number(b)) => *a as Number == *b,
|
||||||
(Value::Number(a), Value::Integer(b)) => *a == *b as ffi::lua_Number,
|
(Value::Number(a), Value::Integer(b)) => *a == *b as Number,
|
||||||
(Value::Number(a), Value::Number(b)) => *a == *b,
|
(Value::Number(a), Value::Number(b)) => *a == *b,
|
||||||
(Value::String(a), Value::String(b)) => a == b,
|
(Value::String(a), Value::String(b)) => a == b,
|
||||||
(Value::Table(a), Value::Table(b)) => a == b,
|
(Value::Table(a), Value::Table(b)) => a == b,
|
||||||
@@ -125,7 +123,9 @@ impl<'lua> Serialize for Value<'lua> {
|
|||||||
match self {
|
match self {
|
||||||
Value::Nil => serializer.serialize_unit(),
|
Value::Nil => serializer.serialize_unit(),
|
||||||
Value::Boolean(b) => serializer.serialize_bool(*b),
|
Value::Boolean(b) => serializer.serialize_bool(*b),
|
||||||
|
#[allow(clippy::useless_conversion)]
|
||||||
Value::Integer(i) => serializer.serialize_i64((*i).into()),
|
Value::Integer(i) => serializer.serialize_i64((*i).into()),
|
||||||
|
#[allow(clippy::useless_conversion)]
|
||||||
Value::Number(n) => serializer.serialize_f64((*n).into()),
|
Value::Number(n) => serializer.serialize_f64((*n).into()),
|
||||||
Value::String(s) => s.serialize(serializer),
|
Value::String(s) => s.serialize(serializer),
|
||||||
Value::Table(t) => t.serialize(serializer),
|
Value::Table(t) => t.serialize(serializer),
|
||||||
@@ -157,18 +157,21 @@ pub struct MultiValue<'lua>(Vec<Value<'lua>>);
|
|||||||
|
|
||||||
impl<'lua> MultiValue<'lua> {
|
impl<'lua> MultiValue<'lua> {
|
||||||
/// Creates an empty `MultiValue` containing no values.
|
/// Creates an empty `MultiValue` containing no values.
|
||||||
|
#[inline]
|
||||||
pub fn new() -> MultiValue<'lua> {
|
pub fn new() -> MultiValue<'lua> {
|
||||||
MultiValue(Vec::new())
|
MultiValue(Vec::new())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'lua> Default for MultiValue<'lua> {
|
impl<'lua> Default for MultiValue<'lua> {
|
||||||
|
#[inline]
|
||||||
fn default() -> MultiValue<'lua> {
|
fn default() -> MultiValue<'lua> {
|
||||||
MultiValue::new()
|
MultiValue::new()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'lua> FromIterator<Value<'lua>> for MultiValue<'lua> {
|
impl<'lua> FromIterator<Value<'lua>> for MultiValue<'lua> {
|
||||||
|
#[inline]
|
||||||
fn from_iter<I: IntoIterator<Item = Value<'lua>>>(iter: I) -> Self {
|
fn from_iter<I: IntoIterator<Item = Value<'lua>>>(iter: I) -> Self {
|
||||||
MultiValue::from_vec(Vec::from_iter(iter))
|
MultiValue::from_vec(Vec::from_iter(iter))
|
||||||
}
|
}
|
||||||
@@ -178,6 +181,7 @@ impl<'lua> IntoIterator for MultiValue<'lua> {
|
|||||||
type Item = Value<'lua>;
|
type Item = Value<'lua>;
|
||||||
type IntoIter = iter::Rev<vec::IntoIter<Value<'lua>>>;
|
type IntoIter = iter::Rev<vec::IntoIter<Value<'lua>>>;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
fn into_iter(self) -> Self::IntoIter {
|
fn into_iter(self) -> Self::IntoIter {
|
||||||
self.0.into_iter().rev()
|
self.0.into_iter().rev()
|
||||||
}
|
}
|
||||||
@@ -187,43 +191,52 @@ impl<'a, 'lua> IntoIterator for &'a MultiValue<'lua> {
|
|||||||
type Item = &'a Value<'lua>;
|
type Item = &'a Value<'lua>;
|
||||||
type IntoIter = iter::Rev<slice::Iter<'a, Value<'lua>>>;
|
type IntoIter = iter::Rev<slice::Iter<'a, Value<'lua>>>;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
fn into_iter(self) -> Self::IntoIter {
|
fn into_iter(self) -> Self::IntoIter {
|
||||||
(&self.0).iter().rev()
|
(&self.0).iter().rev()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'lua> MultiValue<'lua> {
|
impl<'lua> MultiValue<'lua> {
|
||||||
|
#[inline]
|
||||||
pub fn from_vec(mut v: Vec<Value<'lua>>) -> MultiValue<'lua> {
|
pub fn from_vec(mut v: Vec<Value<'lua>>) -> MultiValue<'lua> {
|
||||||
v.reverse();
|
v.reverse();
|
||||||
MultiValue(v)
|
MultiValue(v)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
pub fn into_vec(self) -> Vec<Value<'lua>> {
|
pub fn into_vec(self) -> Vec<Value<'lua>> {
|
||||||
let mut v = self.0;
|
let mut v = self.0;
|
||||||
v.reverse();
|
v.reverse();
|
||||||
v
|
v
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
pub(crate) fn reserve(&mut self, size: usize) {
|
pub(crate) fn reserve(&mut self, size: usize) {
|
||||||
self.0.reserve(size);
|
self.0.reserve(size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
pub(crate) fn push_front(&mut self, value: Value<'lua>) {
|
pub(crate) fn push_front(&mut self, value: Value<'lua>) {
|
||||||
self.0.push(value);
|
self.0.push(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
pub(crate) fn pop_front(&mut self) -> Option<Value<'lua>> {
|
pub(crate) fn pop_front(&mut self) -> Option<Value<'lua>> {
|
||||||
self.0.pop()
|
self.0.pop()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
pub fn len(&self) -> usize {
|
pub fn len(&self) -> usize {
|
||||||
self.0.len()
|
self.0.len()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
pub fn is_empty(&self) -> bool {
|
pub fn is_empty(&self) -> bool {
|
||||||
self.0.len() == 0
|
self.0.len() == 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
pub fn iter(&self) -> iter::Rev<slice::Iter<Value<'lua>>> {
|
pub fn iter(&self) -> iter::Rev<slice::Iter<Value<'lua>>> {
|
||||||
self.0.iter().rev()
|
self.0.iter().rev()
|
||||||
}
|
}
|
||||||
|
|||||||
+36
-12
@@ -1,15 +1,4 @@
|
|||||||
#![cfg(feature = "async")]
|
#![cfg(feature = "async")]
|
||||||
#![cfg_attr(
|
|
||||||
all(feature = "luajit", target_os = "macos", target_arch = "x86_64"),
|
|
||||||
feature(link_args)
|
|
||||||
)]
|
|
||||||
|
|
||||||
#[cfg_attr(
|
|
||||||
all(feature = "luajit", target_os = "macos", target_arch = "x86_64"),
|
|
||||||
link_args = "-pagezero_size 10000 -image_base 100000000",
|
|
||||||
allow(unused_attributes)
|
|
||||||
)]
|
|
||||||
extern "system" {}
|
|
||||||
|
|
||||||
use std::cell::Cell;
|
use std::cell::Cell;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
@@ -22,7 +11,9 @@ use std::time::Duration;
|
|||||||
use futures_timer::Delay;
|
use futures_timer::Delay;
|
||||||
use futures_util::stream::TryStreamExt;
|
use futures_util::stream::TryStreamExt;
|
||||||
|
|
||||||
use mlua::{Error, Function, Lua, Result, Table, TableExt, UserData, UserDataMethods};
|
use mlua::{
|
||||||
|
Error, Function, Lua, Result, Table, TableExt, Thread, UserData, UserDataMethods, Value,
|
||||||
|
};
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn test_async_function() -> Result<()> {
|
async fn test_async_function() -> Result<()> {
|
||||||
@@ -134,6 +125,24 @@ async fn test_async_handle_yield() -> Result<()> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_async_multi_return_nil() -> Result<()> {
|
||||||
|
let lua = Lua::new();
|
||||||
|
lua.globals().set(
|
||||||
|
"func",
|
||||||
|
lua.create_async_function(|_, _: ()| async { Ok((Option::<String>::None, "error")) })?,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
lua.load(
|
||||||
|
r#"
|
||||||
|
local ok, err = func()
|
||||||
|
assert(err == "error")
|
||||||
|
"#,
|
||||||
|
)
|
||||||
|
.exec_async()
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn test_async_return_async_closure() -> Result<()> {
|
async fn test_async_return_async_closure() -> Result<()> {
|
||||||
let lua = Lua::new();
|
let lua = Lua::new();
|
||||||
@@ -332,11 +341,18 @@ async fn test_async_scope() -> Result<()> {
|
|||||||
let _ = f.call_async::<u64, ()>(10).await?;
|
let _ = f.call_async::<u64, ()>(10).await?;
|
||||||
assert_eq!(Rc::strong_count(rc), 1);
|
assert_eq!(Rc::strong_count(rc), 1);
|
||||||
|
|
||||||
|
// Create future in partialy polled state (Poll::Pending)
|
||||||
|
let g = lua.create_thread(f)?;
|
||||||
|
g.resume::<u64, ()>(10)?;
|
||||||
|
lua.globals().set("g", g)?;
|
||||||
|
assert_eq!(Rc::strong_count(rc), 2);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
});
|
});
|
||||||
|
|
||||||
assert_eq!(Rc::strong_count(rc), 1);
|
assert_eq!(Rc::strong_count(rc), 1);
|
||||||
let _ = fut.await?;
|
let _ = fut.await?;
|
||||||
|
assert_eq!(Rc::strong_count(rc), 1);
|
||||||
|
|
||||||
match lua
|
match lua
|
||||||
.globals()
|
.globals()
|
||||||
@@ -351,6 +367,14 @@ async fn test_async_scope() -> Result<()> {
|
|||||||
r => panic!("improper return for destructed function: {:?}", r),
|
r => panic!("improper return for destructed function: {:?}", r),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
match lua.globals().get::<_, Thread>("g")?.resume::<_, Value>(()) {
|
||||||
|
Err(Error::CallbackError { ref cause, .. }) => match cause.as_ref() {
|
||||||
|
Error::CallbackDestructed => {}
|
||||||
|
e => panic!("expected `CallbackDestructed` error cause, got {:?}", e),
|
||||||
|
},
|
||||||
|
r => panic!("improper return for destructed function: {:?}", r),
|
||||||
|
};
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
-13
@@ -1,20 +1,8 @@
|
|||||||
#![cfg_attr(
|
|
||||||
all(feature = "luajit", target_os = "macos", target_arch = "x86_64"),
|
|
||||||
feature(link_args)
|
|
||||||
)]
|
|
||||||
|
|
||||||
#[cfg_attr(
|
|
||||||
all(feature = "luajit", target_os = "macos", target_arch = "x86_64"),
|
|
||||||
link_args = "-pagezero_size 10000 -image_base 100000000",
|
|
||||||
allow(unused_attributes)
|
|
||||||
)]
|
|
||||||
extern "system" {}
|
|
||||||
|
|
||||||
use bstr::{BStr, BString};
|
use bstr::{BStr, BString};
|
||||||
use mlua::{Lua, Result};
|
use mlua::{Lua, Result};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn byte_string_round_trip() -> Result<()> {
|
fn test_byte_string_round_trip() -> Result<()> {
|
||||||
let lua = Lua::new();
|
let lua = Lua::new();
|
||||||
|
|
||||||
lua.load(
|
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> {
|
9 | let _ = lua.create_function(|_, ()| -> Result<i32> {
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^ may outlive borrowed value `test`
|
| ^^^^^^^^^^^^^^^^^^^^^^ may outlive borrowed value `test`
|
||||||
10 | Ok(test.0)
|
10 | Ok(test.0)
|
||||||
| ---- `test` is borrowed here
|
| ------ `test` is borrowed here
|
||||||
|
|
|
|
||||||
note: function requires argument type to outlive `'static`
|
note: function requires argument type to outlive `'static`
|
||||||
--> $DIR/function_borrow.rs:9:13
|
--> $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 it appears within the type `Lua`
|
||||||
= note: required because of the requirements on the impl of `UnwindSafe` for `&Lua`
|
= note: required because of the requirements on the impl of `UnwindSafe` for `&Lua`
|
||||||
= note: required because it appears within the type `[closure@$DIR/tests/compile/lua_norefunwindsafe.rs:7:18: 7:48]`
|
= note: required 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 `mlua::types::LuaRef<'_>`
|
||||||
= note: required because it appears within the type `LuaTable<'_>`
|
= note: required because it appears within the type `LuaTable<'_>`
|
||||||
= note: required because it appears within the type `[closure@$DIR/tests/compile/ref_nounwindsafe.rs:8:18: 8:54]`
|
= note: required 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(|_, ()| {
|
14 | .create_function_mut(|_, ()| {
|
||||||
| ^^^^^^^ may outlive borrowed value `test`
|
| ^^^^^^^ may outlive borrowed value `test`
|
||||||
15 | test.field = 42;
|
15 | test.field = 42;
|
||||||
| ---- `test` is borrowed here
|
| ---------- `test` is borrowed here
|
||||||
|
|
|
|
||||||
note: function requires argument type to outlive `'1`
|
note: function requires argument type to outlive `'1`
|
||||||
--> $DIR/scope_invariance.rs:13:13
|
--> $DIR/scope_invariance.rs:13:13
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ use mlua::{Lua, UserData};
|
|||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
struct MyUserData<'a>(&'a mut i32);
|
struct MyUserData<'a>(&'a mut i32);
|
||||||
impl<'a> UserData for MyUserData<'a> {};
|
impl<'a> UserData for MyUserData<'a> {}
|
||||||
|
|
||||||
let mut i = 1;
|
let mut i = 1;
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ use mlua::{Lua, UserData};
|
|||||||
fn main() {
|
fn main() {
|
||||||
// Should not allow userdata borrow to outlive lifetime of AnyUserData handle
|
// Should not allow userdata borrow to outlive lifetime of AnyUserData handle
|
||||||
struct MyUserData<'a>(&'a i32);
|
struct MyUserData<'a>(&'a i32);
|
||||||
impl<'a> UserData for MyUserData<'a> {};
|
impl<'a> UserData for MyUserData<'a> {}
|
||||||
|
|
||||||
let igood = 1;
|
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
-13
@@ -1,15 +1,3 @@
|
|||||||
#![cfg_attr(
|
|
||||||
all(feature = "luajit", target_os = "macos", target_arch = "x86_64"),
|
|
||||||
feature(link_args)
|
|
||||||
)]
|
|
||||||
|
|
||||||
#[cfg_attr(
|
|
||||||
all(feature = "luajit", target_os = "macos", target_arch = "x86_64"),
|
|
||||||
link_args = "-pagezero_size 10000 -image_base 100000000",
|
|
||||||
allow(unused_attributes)
|
|
||||||
)]
|
|
||||||
extern "system" {}
|
|
||||||
|
|
||||||
use mlua::{Function, Lua, Result, String};
|
use mlua::{Function, Lua, Result, String};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -88,6 +76,23 @@ fn test_rust_function() -> Result<()> {
|
|||||||
Ok(())
|
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]
|
#[test]
|
||||||
fn test_dump() -> Result<()> {
|
fn test_dump() -> Result<()> {
|
||||||
let lua = unsafe { Lua::unsafe_new() };
|
let lua = unsafe { Lua::unsafe_new() };
|
||||||
@@ -95,7 +100,7 @@ fn test_dump() -> Result<()> {
|
|||||||
let concat_lua = lua
|
let concat_lua = lua
|
||||||
.load(r#"function(arg1, arg2) return arg1 .. arg2 end"#)
|
.load(r#"function(arg1, arg2) return arg1 .. arg2 end"#)
|
||||||
.eval::<Function>()?;
|
.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");
|
assert_eq!(concat.call::<_, String>(("foo", "bar"))?, "foobar");
|
||||||
|
|
||||||
|
|||||||
+54
-82
@@ -1,38 +1,34 @@
|
|||||||
#![cfg_attr(
|
|
||||||
all(feature = "luajit", target_os = "macos", target_arch = "x86_64"),
|
|
||||||
feature(link_args)
|
|
||||||
)]
|
|
||||||
|
|
||||||
#[cfg_attr(
|
|
||||||
all(feature = "luajit", target_os = "macos", target_arch = "x86_64"),
|
|
||||||
link_args = "-pagezero_size 10000 -image_base 100000000",
|
|
||||||
allow(unused_attributes)
|
|
||||||
)]
|
|
||||||
extern "system" {}
|
|
||||||
|
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
use std::str;
|
use std::str;
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
use mlua::{Error, HookTriggers, Lua, Result, Value};
|
use mlua::{DebugEvent, Error, HookTriggers, Lua, Result, Value};
|
||||||
|
|
||||||
#[test]
|
#[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 output = Arc::new(Mutex::new(Vec::new()));
|
||||||
let hook_output = output.clone();
|
let hook_output = output.clone();
|
||||||
|
|
||||||
let lua = Lua::new();
|
let lua = Lua::new();
|
||||||
lua.set_hook(
|
lua.set_hook(HookTriggers::every_line(), move |_lua, debug| {
|
||||||
HookTriggers {
|
assert_eq!(debug.event(), DebugEvent::Line);
|
||||||
every_line: true,
|
hook_output.lock().unwrap().push(debug.curr_line());
|
||||||
..Default::default()
|
Ok(())
|
||||||
},
|
})?;
|
||||||
move |_lua, debug| {
|
|
||||||
hook_output.lock().unwrap().push(debug.curr_line());
|
|
||||||
Ok(())
|
|
||||||
},
|
|
||||||
)?;
|
|
||||||
lua.load(
|
lua.load(
|
||||||
r#"
|
r#"
|
||||||
local x = 2 + 3
|
local x = 2 + 3
|
||||||
@@ -55,25 +51,20 @@ fn line_counts() -> Result<()> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn function_calls() -> Result<()> {
|
fn test_function_calls() -> Result<()> {
|
||||||
let output = Arc::new(Mutex::new(Vec::new()));
|
let output = Arc::new(Mutex::new(Vec::new()));
|
||||||
let hook_output = output.clone();
|
let hook_output = output.clone();
|
||||||
|
|
||||||
let lua = Lua::new();
|
let lua = Lua::new();
|
||||||
lua.set_hook(
|
lua.set_hook(HookTriggers::on_calls(), move |_lua, debug| {
|
||||||
HookTriggers {
|
assert_eq!(debug.event(), DebugEvent::Call);
|
||||||
on_calls: true,
|
let names = debug.names();
|
||||||
..Default::default()
|
let source = debug.source();
|
||||||
},
|
let name = names.name.map(|s| str::from_utf8(s).unwrap().to_owned());
|
||||||
move |_lua, debug| {
|
let what = source.what.map(|s| str::from_utf8(s).unwrap().to_owned());
|
||||||
let names = debug.names();
|
hook_output.lock().unwrap().push((name, what));
|
||||||
let source = debug.source();
|
Ok(())
|
||||||
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(
|
lua.load(
|
||||||
r#"
|
r#"
|
||||||
@@ -107,19 +98,14 @@ fn function_calls() -> Result<()> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn error_within_hook() -> Result<()> {
|
fn test_error_within_hook() -> Result<()> {
|
||||||
let lua = Lua::new();
|
let lua = Lua::new();
|
||||||
lua.set_hook(
|
|
||||||
HookTriggers {
|
lua.set_hook(HookTriggers::every_line(), |_lua, _debug| {
|
||||||
every_line: true,
|
Err(Error::RuntimeError(
|
||||||
..Default::default()
|
"Something happened in there!".to_string(),
|
||||||
},
|
))
|
||||||
|_lua, _debug| {
|
})?;
|
||||||
Err(Error::RuntimeError(
|
|
||||||
"Something happened in there!".to_string(),
|
|
||||||
))
|
|
||||||
},
|
|
||||||
)?;
|
|
||||||
|
|
||||||
let err = lua
|
let err = lua
|
||||||
.load("x = 1")
|
.load("x = 1")
|
||||||
@@ -138,7 +124,7 @@ fn error_within_hook() -> Result<()> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn limit_execution_instructions() -> Result<()> {
|
fn test_limit_execution_instructions() -> Result<()> {
|
||||||
let lua = Lua::new();
|
let lua = Lua::new();
|
||||||
let mut max_instructions = 10000;
|
let mut max_instructions = 10000;
|
||||||
|
|
||||||
@@ -147,11 +133,9 @@ fn limit_execution_instructions() -> Result<()> {
|
|||||||
lua.load("jit.off()").exec()?;
|
lua.load("jit.off()").exec()?;
|
||||||
|
|
||||||
lua.set_hook(
|
lua.set_hook(
|
||||||
HookTriggers {
|
HookTriggers::every_nth_instruction(30),
|
||||||
every_nth_instruction: Some(30),
|
move |_lua, debug| {
|
||||||
..Default::default()
|
assert_eq!(debug.event(), DebugEvent::Count);
|
||||||
},
|
|
||||||
move |_lua, _debug| {
|
|
||||||
max_instructions -= 30;
|
max_instructions -= 30;
|
||||||
if max_instructions < 0 {
|
if max_instructions < 0 {
|
||||||
Err(Error::RuntimeError("time's up".to_string()))
|
Err(Error::RuntimeError("time's up".to_string()))
|
||||||
@@ -177,20 +161,14 @@ fn limit_execution_instructions() -> Result<()> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn hook_removal() -> Result<()> {
|
fn test_hook_removal() -> Result<()> {
|
||||||
let lua = Lua::new();
|
let lua = Lua::new();
|
||||||
|
|
||||||
lua.set_hook(
|
lua.set_hook(HookTriggers::every_nth_instruction(1), |_lua, _debug| {
|
||||||
HookTriggers {
|
Err(Error::RuntimeError(
|
||||||
every_nth_instruction: Some(1),
|
"this hook should've been removed by this time".to_string(),
|
||||||
..Default::default()
|
))
|
||||||
},
|
})?;
|
||||||
|_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());
|
assert!(lua.load("local x = 1").exec().is_err());
|
||||||
lua.remove_hook();
|
lua.remove_hook();
|
||||||
@@ -200,7 +178,7 @@ fn hook_removal() -> Result<()> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn hook_swap_within_hook() -> Result<()> {
|
fn test_hook_swap_within_hook() -> Result<()> {
|
||||||
thread_local! {
|
thread_local! {
|
||||||
static TL_LUA: RefCell<Option<Lua>> = RefCell::new(None);
|
static TL_LUA: RefCell<Option<Lua>> = RefCell::new(None);
|
||||||
}
|
}
|
||||||
@@ -210,19 +188,14 @@ fn hook_swap_within_hook() -> Result<()> {
|
|||||||
});
|
});
|
||||||
|
|
||||||
TL_LUA.with(|tl| {
|
TL_LUA.with(|tl| {
|
||||||
tl.borrow().as_ref().unwrap().set_hook(
|
tl.borrow()
|
||||||
HookTriggers {
|
.as_ref()
|
||||||
every_line: true,
|
.unwrap()
|
||||||
..Default::default()
|
.set_hook(HookTriggers::every_line(), move |lua, _debug| {
|
||||||
},
|
|
||||||
move |lua, _debug| {
|
|
||||||
lua.globals().set("ok", 1i64)?;
|
lua.globals().set("ok", 1i64)?;
|
||||||
TL_LUA.with(|tl| {
|
TL_LUA.with(|tl| {
|
||||||
tl.borrow().as_ref().unwrap().set_hook(
|
tl.borrow().as_ref().unwrap().set_hook(
|
||||||
HookTriggers {
|
HookTriggers::every_line(),
|
||||||
every_line: true,
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
move |lua, _debug| {
|
move |lua, _debug| {
|
||||||
lua.load(
|
lua.load(
|
||||||
r#"
|
r#"
|
||||||
@@ -240,8 +213,7 @@ fn hook_swap_within_hook() -> Result<()> {
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
},
|
})
|
||||||
)
|
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
TL_LUA.with(|tl| {
|
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(())
|
||||||
|
}
|
||||||
+6
-12
@@ -1,15 +1,3 @@
|
|||||||
#![cfg_attr(
|
|
||||||
all(feature = "luajit", target_os = "macos", target_arch = "x86_64"),
|
|
||||||
feature(link_args)
|
|
||||||
)]
|
|
||||||
|
|
||||||
#[cfg_attr(
|
|
||||||
all(feature = "luajit", target_os = "macos", target_arch = "x86_64"),
|
|
||||||
link_args = "-pagezero_size 10000 -image_base 100000000",
|
|
||||||
allow(unused_attributes)
|
|
||||||
)]
|
|
||||||
extern "system" {}
|
|
||||||
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use mlua::{Lua, Result, UserData};
|
use mlua::{Lua, Result, UserData};
|
||||||
@@ -50,6 +38,9 @@ fn test_gc_control() -> Result<()> {
|
|||||||
let lua = Lua::new();
|
let lua = Lua::new();
|
||||||
let globals = lua.globals();
|
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"))]
|
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
|
||||||
{
|
{
|
||||||
assert!(lua.gc_is_running());
|
assert!(lua.gc_is_running());
|
||||||
@@ -71,6 +62,9 @@ fn test_gc_control() -> Result<()> {
|
|||||||
lua.gc_collect()?;
|
lua.gc_collect()?;
|
||||||
assert_eq!(Arc::strong_count(&rc), 1);
|
assert_eq!(Arc::strong_count(&rc), 1);
|
||||||
|
|
||||||
|
#[cfg(feature = "lua54")]
|
||||||
|
assert_eq!(lua.gc_inc(0, 0, 0), mlua::GCMode::Generational);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -15,6 +15,32 @@ fn test_module() -> Result<()> {
|
|||||||
.exec()
|
.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(
|
#[cfg(any(
|
||||||
feature = "lua54",
|
feature = "lua54",
|
||||||
feature = "lua53",
|
feature = "lua53",
|
||||||
|
|||||||
+167
-49
@@ -1,22 +1,14 @@
|
|||||||
#![cfg_attr(
|
|
||||||
all(feature = "luajit", target_os = "macos", target_arch = "x86_64"),
|
|
||||||
feature(link_args)
|
|
||||||
)]
|
|
||||||
|
|
||||||
#[cfg_attr(
|
|
||||||
all(feature = "luajit", target_os = "macos", target_arch = "x86_64"),
|
|
||||||
link_args = "-pagezero_size 10000 -image_base 100000000",
|
|
||||||
allow(unused_attributes)
|
|
||||||
)]
|
|
||||||
extern "system" {}
|
|
||||||
|
|
||||||
use std::cell::Cell;
|
use std::cell::Cell;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
use mlua::{Error, Function, Lua, MetaMethod, Result, String, UserData, UserDataMethods};
|
use mlua::{
|
||||||
|
AnyUserData, Error, Function, Lua, MetaMethod, Result, String, UserData, UserDataFields,
|
||||||
|
UserDataMethods,
|
||||||
|
};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn scope_func() -> Result<()> {
|
fn test_scope_func() -> Result<()> {
|
||||||
let lua = Lua::new();
|
let lua = Lua::new();
|
||||||
|
|
||||||
let rc = Rc::new(Cell::new(0));
|
let rc = Rc::new(Cell::new(0));
|
||||||
@@ -35,7 +27,10 @@ fn scope_func() -> Result<()> {
|
|||||||
assert_eq!(Rc::strong_count(&rc), 1);
|
assert_eq!(Rc::strong_count(&rc), 1);
|
||||||
|
|
||||||
match lua.globals().get::<_, Function>("bad")?.call::<_, ()>(()) {
|
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),
|
r => panic!("improper return for destructed function: {:?}", r),
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -43,36 +38,7 @@ fn scope_func() -> Result<()> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn scope_drop() -> Result<()> {
|
fn test_scope_capture() -> Result<()> {
|
||||||
let lua = Lua::new();
|
|
||||||
|
|
||||||
struct MyUserdata(Rc<()>);
|
|
||||||
impl UserData for MyUserdata {
|
|
||||||
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
|
|
||||||
methods.add_method("method", |_, _, ()| Ok(()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let rc = Rc::new(());
|
|
||||||
|
|
||||||
lua.scope(|scope| {
|
|
||||||
lua.globals()
|
|
||||||
.set("test", scope.create_userdata(MyUserdata(rc.clone()))?)?;
|
|
||||||
assert_eq!(Rc::strong_count(&rc), 2);
|
|
||||||
Ok(())
|
|
||||||
})?;
|
|
||||||
assert_eq!(Rc::strong_count(&rc), 1);
|
|
||||||
|
|
||||||
match lua.load("test:method()").exec() {
|
|
||||||
Err(Error::CallbackError { .. }) => {}
|
|
||||||
r => panic!("improper return for destructed userdata: {:?}", r),
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn scope_capture() -> Result<()> {
|
|
||||||
let lua = Lua::new();
|
let lua = Lua::new();
|
||||||
|
|
||||||
let mut i = 0;
|
let mut i = 0;
|
||||||
@@ -90,7 +56,7 @@ fn scope_capture() -> Result<()> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn outer_lua_access() -> Result<()> {
|
fn test_scope_outer_lua_access() -> Result<()> {
|
||||||
let lua = Lua::new();
|
let lua = Lua::new();
|
||||||
|
|
||||||
let table = lua.create_table()?;
|
let table = lua.create_table()?;
|
||||||
@@ -105,7 +71,42 @@ fn outer_lua_access() -> Result<()> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[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>);
|
struct MyUserData<'a>(&'a Cell<i64>);
|
||||||
|
|
||||||
impl<'a> UserData for MyUserData<'a> {
|
impl<'a> UserData for MyUserData<'a> {
|
||||||
@@ -146,7 +147,7 @@ fn scope_userdata_methods() -> Result<()> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn scope_userdata_functions() -> Result<()> {
|
fn test_scope_userdata_functions() -> Result<()> {
|
||||||
struct MyUserData<'a>(&'a i64);
|
struct MyUserData<'a>(&'a i64);
|
||||||
|
|
||||||
impl<'a> UserData for MyUserData<'a> {
|
impl<'a> UserData for MyUserData<'a> {
|
||||||
@@ -188,7 +189,7 @@ fn scope_userdata_functions() -> Result<()> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn scope_userdata_mismatch() -> Result<()> {
|
fn test_scope_userdata_mismatch() -> Result<()> {
|
||||||
struct MyUserData<'a>(&'a Cell<i64>);
|
struct MyUserData<'a>(&'a Cell<i64>);
|
||||||
|
|
||||||
impl<'a> UserData for MyUserData<'a> {
|
impl<'a> UserData for MyUserData<'a> {
|
||||||
@@ -238,3 +239,120 @@ fn scope_userdata_mismatch() -> Result<()> {
|
|||||||
|
|
||||||
Ok(())
|
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(())
|
||||||
|
}
|
||||||
|
|||||||
+181
-22
@@ -1,17 +1,11 @@
|
|||||||
#![cfg(feature = "serialize")]
|
#![cfg(feature = "serialize")]
|
||||||
#![cfg_attr(
|
|
||||||
all(feature = "luajit", target_os = "macos", target_arch = "x86_64"),
|
|
||||||
feature(link_args)
|
|
||||||
)]
|
|
||||||
|
|
||||||
#[cfg_attr(
|
use std::collections::HashMap;
|
||||||
all(feature = "luajit", target_os = "macos", target_arch = "x86_64"),
|
|
||||||
link_args = "-pagezero_size 10000 -image_base 100000000",
|
|
||||||
allow(unused_attributes)
|
|
||||||
)]
|
|
||||||
extern "system" {}
|
|
||||||
|
|
||||||
use mlua::{Error, Lua, LuaSerdeExt, Result as LuaResult, UserData, Value};
|
use mlua::{
|
||||||
|
DeserializeOptions, Error, Lua, LuaSerdeExt, Result as LuaResult, SerializeOptions, UserData,
|
||||||
|
Value,
|
||||||
|
};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -19,17 +13,17 @@ fn test_serialize() -> Result<(), Box<dyn std::error::Error>> {
|
|||||||
#[derive(Serialize)]
|
#[derive(Serialize)]
|
||||||
struct MyUserData(i64, String);
|
struct MyUserData(i64, String);
|
||||||
|
|
||||||
impl UserData for MyUserData {};
|
impl UserData for MyUserData {}
|
||||||
|
|
||||||
let lua = Lua::new();
|
let lua = Lua::new();
|
||||||
let globals = lua.globals();
|
let globals = lua.globals();
|
||||||
|
|
||||||
let ud = lua.create_ser_userdata(MyUserData(123, "test userdata".into()))?;
|
let ud = lua.create_ser_userdata(MyUserData(123, "test userdata".into()))?;
|
||||||
globals.set("ud", ud)?;
|
globals.set("ud", ud)?;
|
||||||
globals.set("null", lua.null()?)?;
|
globals.set("null", lua.null())?;
|
||||||
|
|
||||||
let empty_array = lua.create_table()?;
|
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)?;
|
globals.set("empty_array", empty_array)?;
|
||||||
|
|
||||||
let val = lua
|
let val = lua
|
||||||
@@ -81,7 +75,7 @@ fn test_serialize_in_scope() -> LuaResult<()> {
|
|||||||
#[derive(Serialize, Clone)]
|
#[derive(Serialize, Clone)]
|
||||||
struct MyUserData(i64, String);
|
struct MyUserData(i64, String);
|
||||||
|
|
||||||
impl UserData for MyUserData {};
|
impl UserData for MyUserData {}
|
||||||
|
|
||||||
let lua = Lua::new();
|
let lua = Lua::new();
|
||||||
lua.scope(|scope| {
|
lua.scope(|scope| {
|
||||||
@@ -104,6 +98,19 @@ fn test_serialize_in_scope() -> LuaResult<()> {
|
|||||||
Err(e) => panic!("expected destructed error, got {}", e),
|
Err(e) => panic!("expected destructed error, got {}", e),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct MyUserDataRef<'a>(&'a ());
|
||||||
|
|
||||||
|
impl<'a> UserData for MyUserDataRef<'a> {}
|
||||||
|
|
||||||
|
lua.scope(|scope| {
|
||||||
|
let ud = scope.create_nonstatic_userdata(MyUserDataRef(&()))?;
|
||||||
|
match serde_json::to_value(&ud) {
|
||||||
|
Ok(v) => panic!("expected serialization error, got {}", v),
|
||||||
|
Err(serde_json::Error { .. }) => {}
|
||||||
|
};
|
||||||
|
Ok(())
|
||||||
|
})?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -112,7 +119,7 @@ fn test_serialize_failure() -> Result<(), Box<dyn std::error::Error>> {
|
|||||||
#[derive(Serialize)]
|
#[derive(Serialize)]
|
||||||
struct MyUserData(i64);
|
struct MyUserData(i64);
|
||||||
|
|
||||||
impl UserData for MyUserData {};
|
impl UserData for MyUserData {}
|
||||||
|
|
||||||
let lua = Lua::new();
|
let lua = Lua::new();
|
||||||
|
|
||||||
@@ -141,14 +148,14 @@ fn test_serialize_failure() -> Result<(), Box<dyn std::error::Error>> {
|
|||||||
fn test_to_value_struct() -> LuaResult<()> {
|
fn test_to_value_struct() -> LuaResult<()> {
|
||||||
let lua = Lua::new();
|
let lua = Lua::new();
|
||||||
let globals = lua.globals();
|
let globals = lua.globals();
|
||||||
globals.set("null", lua.null()?)?;
|
globals.set("null", lua.null())?;
|
||||||
|
|
||||||
#[derive(Serialize)]
|
#[derive(Serialize)]
|
||||||
struct Test {
|
struct Test {
|
||||||
name: String,
|
name: String,
|
||||||
key: i64,
|
key: i64,
|
||||||
data: Option<bool>,
|
data: Option<bool>,
|
||||||
};
|
}
|
||||||
|
|
||||||
let test = Test {
|
let test = Test {
|
||||||
name: "alex".to_string(),
|
name: "alex".to_string(),
|
||||||
@@ -171,14 +178,14 @@ fn test_to_value_struct() -> LuaResult<()> {
|
|||||||
fn test_to_value_enum() -> LuaResult<()> {
|
fn test_to_value_enum() -> LuaResult<()> {
|
||||||
let lua = Lua::new();
|
let lua = Lua::new();
|
||||||
let globals = lua.globals();
|
let globals = lua.globals();
|
||||||
globals.set("null", lua.null()?)?;
|
globals.set("null", lua.null())?;
|
||||||
|
|
||||||
#[derive(Serialize)]
|
#[derive(Serialize)]
|
||||||
struct Test {
|
struct Test {
|
||||||
name: String,
|
name: String,
|
||||||
key: i64,
|
key: i64,
|
||||||
data: Option<bool>,
|
data: Option<bool>,
|
||||||
};
|
}
|
||||||
|
|
||||||
let test = Test {
|
let test = Test {
|
||||||
name: "alex".to_string(),
|
name: "alex".to_string(),
|
||||||
@@ -228,6 +235,106 @@ fn test_to_value_enum() -> LuaResult<()> {
|
|||||||
Ok(())
|
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]
|
#[test]
|
||||||
fn test_from_value_struct() -> Result<(), Box<dyn std::error::Error>> {
|
fn test_from_value_struct() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
let lua = Lua::new();
|
let lua = Lua::new();
|
||||||
@@ -236,7 +343,7 @@ fn test_from_value_struct() -> Result<(), Box<dyn std::error::Error>> {
|
|||||||
struct Test {
|
struct Test {
|
||||||
int: u32,
|
int: u32,
|
||||||
seq: Vec<String>,
|
seq: Vec<String>,
|
||||||
map: std::collections::HashMap<i32, i32>,
|
map: HashMap<i32, i32>,
|
||||||
empty: Vec<()>,
|
empty: Vec<()>,
|
||||||
tuple: (u8, u8, u8),
|
tuple: (u8, u8, u8),
|
||||||
}
|
}
|
||||||
@@ -303,7 +410,7 @@ fn test_from_value_enum() -> Result<(), Box<dyn std::error::Error>> {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_from_value_enum_untagged() -> Result<(), Box<dyn std::error::Error>> {
|
fn test_from_value_enum_untagged() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
let lua = Lua::new();
|
let lua = Lua::new();
|
||||||
lua.globals().set("null", lua.null()?)?;
|
lua.globals().set("null", lua.null())?;
|
||||||
|
|
||||||
#[derive(Deserialize, PartialEq, Debug)]
|
#[derive(Deserialize, PartialEq, Debug)]
|
||||||
#[serde(untagged)]
|
#[serde(untagged)]
|
||||||
@@ -339,3 +446,55 @@ fn test_from_value_enum_untagged() -> Result<(), Box<dyn std::error::Error>> {
|
|||||||
|
|
||||||
Ok(())
|
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
-15
@@ -1,21 +1,9 @@
|
|||||||
#![cfg_attr(
|
|
||||||
all(feature = "luajit", target_os = "macos", target_arch = "x86_64"),
|
|
||||||
feature(link_args)
|
|
||||||
)]
|
|
||||||
|
|
||||||
#[cfg_attr(
|
|
||||||
all(feature = "luajit", target_os = "macos", target_arch = "x86_64"),
|
|
||||||
link_args = "-pagezero_size 10000 -image_base 100000000",
|
|
||||||
allow(unused_attributes)
|
|
||||||
)]
|
|
||||||
extern "system" {}
|
|
||||||
|
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
|
|
||||||
use mlua::{Lua, Result, String};
|
use mlua::{Lua, Result, String};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn compare() {
|
fn test_string_compare() {
|
||||||
fn with_str<F: FnOnce(String)>(s: &str, f: F) {
|
fn with_str<F: FnOnce(String)>(s: &str, f: F) {
|
||||||
f(Lua::new().create_string(s).unwrap());
|
f(Lua::new().create_string(s).unwrap());
|
||||||
}
|
}
|
||||||
@@ -33,7 +21,7 @@ fn compare() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn string_views() -> Result<()> {
|
fn test_string_views() -> Result<()> {
|
||||||
let lua = Lua::new();
|
let lua = Lua::new();
|
||||||
|
|
||||||
lua.load(
|
lua.load(
|
||||||
@@ -51,6 +39,10 @@ fn string_views() -> Result<()> {
|
|||||||
let empty: String = globals.get("empty")?;
|
let empty: String = globals.get("empty")?;
|
||||||
|
|
||||||
assert_eq!(ok.to_str()?, "null bytes are valid utf-8, wh\0 knew?");
|
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!(
|
assert_eq!(
|
||||||
ok.as_bytes(),
|
ok.as_bytes(),
|
||||||
&b"null bytes are valid utf-8, wh\0 knew?"[..]
|
&b"null bytes are valid utf-8, wh\0 knew?"[..]
|
||||||
@@ -67,7 +59,7 @@ fn string_views() -> Result<()> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn raw_string() -> Result<()> {
|
fn test_raw_string() -> Result<()> {
|
||||||
let lua = Lua::new();
|
let lua = Lua::new();
|
||||||
|
|
||||||
let rs = lua.create_string(&[0, 1, 2, 3, 0, 1, 2, 3])?;
|
let rs = lua.create_string(&[0, 1, 2, 3, 0, 1, 2, 3])?;
|
||||||
|
|||||||
@@ -1,15 +1,3 @@
|
|||||||
#![cfg_attr(
|
|
||||||
all(feature = "luajit", target_os = "macos", target_arch = "x86_64"),
|
|
||||||
feature(link_args)
|
|
||||||
)]
|
|
||||||
|
|
||||||
#[cfg_attr(
|
|
||||||
all(feature = "luajit", target_os = "macos", target_arch = "x86_64"),
|
|
||||||
link_args = "-pagezero_size 10000 -image_base 100000000",
|
|
||||||
allow(unused_attributes)
|
|
||||||
)]
|
|
||||||
extern "system" {}
|
|
||||||
|
|
||||||
use mlua::{Lua, Nil, Result, Table, TableExt, Value};
|
use mlua::{Lua, Nil, Result, Table, TableExt, Value};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
+231
-82
@@ -1,23 +1,14 @@
|
|||||||
#![cfg_attr(
|
use std::collections::HashMap;
|
||||||
all(feature = "luajit", target_os = "macos", target_arch = "x86_64"),
|
|
||||||
feature(link_args)
|
|
||||||
)]
|
|
||||||
|
|
||||||
#[cfg_attr(
|
|
||||||
all(feature = "luajit", target_os = "macos", target_arch = "x86_64"),
|
|
||||||
link_args = "-pagezero_size 10000 -image_base 100000000",
|
|
||||||
allow(unused_attributes)
|
|
||||||
)]
|
|
||||||
extern "system" {}
|
|
||||||
|
|
||||||
use std::iter::FromIterator;
|
use std::iter::FromIterator;
|
||||||
use std::panic::catch_unwind;
|
use std::panic::{catch_unwind, AssertUnwindSafe};
|
||||||
|
use std::string::String as StdString;
|
||||||
|
use std::sync::atomic::{AtomicU32, Ordering};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::{error, f32, f64, fmt};
|
use std::{error, f32, f64, fmt};
|
||||||
|
|
||||||
use mlua::{
|
use mlua::{
|
||||||
ChunkMode, Error, ExternalError, Function, Lua, Nil, Result, StdLib, String, Table, UserData,
|
ChunkMode, Error, ExternalError, Function, Lua, LuaOptions, Nil, Result, StdLib, String, Table,
|
||||||
Value, Variadic,
|
UserData, Value, Variadic,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -35,7 +26,7 @@ fn test_safety() -> Result<()> {
|
|||||||
assert!(lua.load(r#"require "debug""#).exec().is_ok());
|
assert!(lua.load(r#"require "debug""#).exec().is_ok());
|
||||||
drop(lua);
|
drop(lua);
|
||||||
|
|
||||||
match Lua::new_with(StdLib::DEBUG) {
|
match Lua::new_with(StdLib::DEBUG, LuaOptions::default()) {
|
||||||
Err(Error::SafetyError(_)) => {}
|
Err(Error::SafetyError(_)) => {}
|
||||||
Err(e) => panic!("expected SafetyError, got {:?}", e),
|
Err(e) => panic!("expected SafetyError, got {:?}", e),
|
||||||
Ok(_) => panic!("expected SafetyError, got new Lua state"),
|
Ok(_) => panic!("expected SafetyError, got new Lua state"),
|
||||||
@@ -64,7 +55,7 @@ fn test_safety() -> Result<()> {
|
|||||||
Ok(_) => panic!("expected SafetyError, got no error"),
|
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() {
|
match lua.load(&bytecode).exec() {
|
||||||
Err(Error::SafetyError(msg)) => {
|
Err(Error::SafetyError(msg)) => {
|
||||||
assert!(msg.contains("binary chunks are disabled in safe mode"))
|
assert!(msg.contains("binary chunks are disabled in safe mode"))
|
||||||
@@ -75,7 +66,7 @@ fn test_safety() -> Result<()> {
|
|||||||
drop(lua);
|
drop(lua);
|
||||||
|
|
||||||
// Test safety rules after dynamically loading `package` library
|
// 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());
|
assert!(lua.globals().get::<_, Option<Value>>("require")?.is_none());
|
||||||
lua.load_from_std_lib(StdLib::PACKAGE)?;
|
lua.load_from_std_lib(StdLib::PACKAGE)?;
|
||||||
match lua.load(r#"package.loadlib()"#).exec() {
|
match lua.load(r#"package.loadlib()"#).exec() {
|
||||||
@@ -93,6 +84,7 @@ fn test_safety() -> Result<()> {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_load() -> Result<()> {
|
fn test_load() -> Result<()> {
|
||||||
let lua = Lua::new();
|
let lua = Lua::new();
|
||||||
|
|
||||||
let func = lua.load("return 1+2").into_function()?;
|
let func = lua.load("return 1+2").into_function()?;
|
||||||
let result: i32 = func.call(())?;
|
let result: i32 = func.call(())?;
|
||||||
assert_eq!(result, 3);
|
assert_eq!(result, 3);
|
||||||
@@ -174,7 +166,7 @@ fn test_load_mode() -> Result<()> {
|
|||||||
Err(e) => panic!("expected SyntaxError, got {:?}", e),
|
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).eval::<i32>()?, 2);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
lua.load(&bytecode)
|
lua.load(&bytecode)
|
||||||
@@ -233,6 +225,7 @@ fn test_coercion() -> Result<()> {
|
|||||||
int = 123
|
int = 123
|
||||||
str = "123"
|
str = "123"
|
||||||
num = 123.0
|
num = 123.0
|
||||||
|
func = function() end
|
||||||
"#,
|
"#,
|
||||||
)
|
)
|
||||||
.exec()?;
|
.exec()?;
|
||||||
@@ -241,6 +234,7 @@ fn test_coercion() -> Result<()> {
|
|||||||
assert_eq!(globals.get::<_, String>("int")?, "123");
|
assert_eq!(globals.get::<_, String>("int")?, "123");
|
||||||
assert_eq!(globals.get::<_, i32>("str")?, 123);
|
assert_eq!(globals.get::<_, i32>("str")?, 123);
|
||||||
assert_eq!(globals.get::<_, i32>("num")?, 123);
|
assert_eq!(globals.get::<_, i32>("num")?, 123);
|
||||||
|
assert!(globals.get::<_, String>("func").is_err());
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -384,62 +378,139 @@ fn test_error() -> Result<()> {
|
|||||||
|
|
||||||
assert!(understand_recursion.call::<_, ()>(()).is_err());
|
assert!(understand_recursion.call::<_, ()>(()).is_err());
|
||||||
|
|
||||||
match catch_unwind(|| -> Result<()> {
|
Ok(())
|
||||||
let lua = Lua::new();
|
}
|
||||||
let globals = lua.globals();
|
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_panic() -> Result<()> {
|
||||||
|
fn make_lua(options: LuaOptions) -> Result<Lua> {
|
||||||
|
let lua = Lua::new_with(StdLib::ALL_SAFE, options)?;
|
||||||
|
let rust_panic_function =
|
||||||
|
lua.create_function(|_, msg: Option<StdString>| -> Result<()> {
|
||||||
|
if let Some(msg) = msg {
|
||||||
|
panic!("{}", msg)
|
||||||
|
}
|
||||||
|
panic!("rust panic")
|
||||||
|
})?;
|
||||||
|
lua.globals()
|
||||||
|
.set("rust_panic_function", rust_panic_function)?;
|
||||||
|
Ok(lua)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test triggering Lua error with sending Rust panic (must be resumed)
|
||||||
|
{
|
||||||
|
let lua = make_lua(LuaOptions::default())?;
|
||||||
|
|
||||||
|
match catch_unwind(AssertUnwindSafe(|| -> Result<()> {
|
||||||
|
lua.load(
|
||||||
|
r#"
|
||||||
|
_, err = pcall(rust_panic_function)
|
||||||
|
error(err)
|
||||||
|
"#,
|
||||||
|
)
|
||||||
|
.exec()
|
||||||
|
})) {
|
||||||
|
Ok(Ok(_)) => panic!("no panic was detected"),
|
||||||
|
Ok(Err(e)) => panic!("error during panic test {:?}", e),
|
||||||
|
Err(p) => assert!(*p.downcast::<&str>().unwrap() == "rust panic"),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Trigger same panic again
|
||||||
|
match lua.load("error(err)").exec() {
|
||||||
|
Ok(_) => panic!("no error was detected"),
|
||||||
|
Err(Error::PreviouslyResumedPanic) => {}
|
||||||
|
Err(e) => panic!("expected PreviouslyResumedPanic, got {:?}", e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test returning Rust panic (must be resumed)
|
||||||
|
{
|
||||||
|
let lua = make_lua(LuaOptions::default())?;
|
||||||
|
match catch_unwind(AssertUnwindSafe(|| -> Result<()> {
|
||||||
|
let _catched_panic = lua
|
||||||
|
.load(
|
||||||
|
r#"
|
||||||
|
-- Set global
|
||||||
|
_, err = pcall(rust_panic_function)
|
||||||
|
return err
|
||||||
|
"#,
|
||||||
|
)
|
||||||
|
.eval::<Value>()?;
|
||||||
|
Ok(())
|
||||||
|
})) {
|
||||||
|
Ok(_) => panic!("no panic was detected"),
|
||||||
|
Err(_) => {}
|
||||||
|
};
|
||||||
|
|
||||||
|
assert!(lua.globals().get::<_, Value>("err")? == Value::Nil);
|
||||||
|
match lua.load("tostring(err)").exec() {
|
||||||
|
Ok(_) => panic!("no error was detected"),
|
||||||
|
Err(Error::CallbackError { ref cause, .. }) => match cause.as_ref() {
|
||||||
|
Error::PreviouslyResumedPanic => {}
|
||||||
|
e => panic!("expected PreviouslyResumedPanic, got {:?}", e),
|
||||||
|
},
|
||||||
|
Err(e) => panic!("expected CallbackError, got {:?}", e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test representing Rust panic as a string
|
||||||
|
match catch_unwind(|| -> Result<()> {
|
||||||
|
let lua = make_lua(LuaOptions::default())?;
|
||||||
lua.load(
|
lua.load(
|
||||||
r#"
|
r#"
|
||||||
function rust_panic()
|
local _, err = pcall(rust_panic_function)
|
||||||
local _, err = pcall(function () rust_panic_function() end)
|
error(tostring(err))
|
||||||
if err ~= nil then
|
|
||||||
error(err)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
"#,
|
"#,
|
||||||
)
|
)
|
||||||
.exec()?;
|
.exec()
|
||||||
let rust_panic_function =
|
|
||||||
lua.create_function(|_, ()| -> Result<()> { panic!("test_panic") })?;
|
|
||||||
globals.set("rust_panic_function", rust_panic_function)?;
|
|
||||||
|
|
||||||
let rust_panic = globals.get::<_, Function>("rust_panic")?;
|
|
||||||
|
|
||||||
rust_panic.call::<_, ()>(())
|
|
||||||
}) {
|
|
||||||
Ok(Ok(_)) => panic!("no panic was detected"),
|
|
||||||
Ok(Err(e)) => panic!("error during panic test {:?}", e),
|
|
||||||
Err(p) => assert!(*p.downcast::<&str>().unwrap() == "test_panic"),
|
|
||||||
};
|
|
||||||
|
|
||||||
match catch_unwind(|| -> Result<()> {
|
|
||||||
let lua = Lua::new();
|
|
||||||
let globals = lua.globals();
|
|
||||||
|
|
||||||
lua.load(
|
|
||||||
r#"
|
|
||||||
function rust_panic()
|
|
||||||
local _, err = pcall(function () rust_panic_function() end)
|
|
||||||
if err ~= nil then
|
|
||||||
error(tostring(err))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
"#,
|
|
||||||
)
|
|
||||||
.exec()?;
|
|
||||||
let rust_panic_function =
|
|
||||||
lua.create_function(|_, ()| -> Result<()> { panic!("test_panic") })?;
|
|
||||||
globals.set("rust_panic_function", rust_panic_function)?;
|
|
||||||
|
|
||||||
let rust_panic = globals.get::<_, Function>("rust_panic")?;
|
|
||||||
|
|
||||||
rust_panic.call::<_, ()>(())
|
|
||||||
}) {
|
}) {
|
||||||
Ok(Ok(_)) => panic!("no error was detected"),
|
Ok(Ok(_)) => panic!("no error was detected"),
|
||||||
Ok(Err(Error::RuntimeError(_))) => {}
|
Ok(Err(Error::RuntimeError(_))) => {}
|
||||||
Ok(Err(e)) => panic!("unexpected error during panic test {:?}", e),
|
Ok(Err(e)) => panic!("expected RuntimeError, got {:?}", e),
|
||||||
Err(_) => panic!("panic was detected"),
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -543,7 +614,7 @@ fn test_pcall_xpcall() -> Result<()> {
|
|||||||
assert!(lua.load("xpcall()").exec().is_err());
|
assert!(lua.load("xpcall()").exec().is_err());
|
||||||
assert!(lua.load("xpcall(function() end)").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")]
|
#[cfg(feature = "lua51")]
|
||||||
lua.load(
|
lua.load(
|
||||||
r#"
|
r#"
|
||||||
@@ -729,6 +800,23 @@ fn test_drop_registry_value() -> Result<()> {
|
|||||||
Ok(())
|
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]
|
#[test]
|
||||||
fn test_lua_registry_ownership() -> Result<()> {
|
fn test_lua_registry_ownership() -> Result<()> {
|
||||||
let lua1 = Lua::new();
|
let lua1 = Lua::new();
|
||||||
@@ -760,7 +848,26 @@ fn test_mismatched_registry_key() -> Result<()> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[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 lua = Lua::new();
|
||||||
let f = lua.create_function(|_, ()| Ok(Variadic::from_iter(1..1000000)))?;
|
let f = lua.create_function(|_, ()| Ok(Variadic::from_iter(1..1000000)))?;
|
||||||
assert!(f.call::<_, Vec<u32>>(()).is_err());
|
assert!(f.call::<_, Vec<u32>>(()).is_err());
|
||||||
@@ -768,7 +875,7 @@ fn too_many_returns() -> Result<()> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn too_many_arguments() -> Result<()> {
|
fn test_too_many_arguments() -> Result<()> {
|
||||||
let lua = Lua::new();
|
let lua = Lua::new();
|
||||||
lua.load("function test(...) end").exec()?;
|
lua.load("function test(...) end").exec()?;
|
||||||
let args = Variadic::from_iter(1..1000000);
|
let args = Variadic::from_iter(1..1000000);
|
||||||
@@ -782,23 +889,20 @@ fn too_many_arguments() -> Result<()> {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(not(feature = "luajit"))]
|
#[cfg(not(feature = "luajit"))]
|
||||||
fn too_many_recursions() -> Result<()> {
|
fn test_too_many_recursions() -> Result<()> {
|
||||||
let lua = Lua::new();
|
let lua = Lua::new();
|
||||||
|
|
||||||
let f = lua
|
let f = lua
|
||||||
.create_function(move |lua, ()| lua.globals().get::<_, Function>("f")?.call::<_, ()>(()))?;
|
.create_function(move |lua, ()| lua.globals().get::<_, Function>("f")?.call::<_, ()>(()))?;
|
||||||
lua.globals().set("f", f)?;
|
|
||||||
|
|
||||||
assert!(lua
|
lua.globals().set("f", f.clone())?;
|
||||||
.globals()
|
assert!(f.call::<_, ()>(()).is_err());
|
||||||
.get::<_, Function>("f")?
|
|
||||||
.call::<_, ()>(())
|
|
||||||
.is_err());
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn too_many_binds() -> Result<()> {
|
fn test_too_many_binds() -> Result<()> {
|
||||||
let lua = Lua::new();
|
let lua = Lua::new();
|
||||||
let globals = lua.globals();
|
let globals = lua.globals();
|
||||||
lua.load(
|
lua.load(
|
||||||
@@ -819,7 +923,25 @@ fn too_many_binds() -> Result<()> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[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 lua = Lua::new();
|
||||||
let globals = lua.globals();
|
let globals = lua.globals();
|
||||||
|
|
||||||
@@ -854,7 +976,7 @@ fn large_args() -> Result<()> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn large_args_ref() -> Result<()> {
|
fn test_large_args_ref() -> Result<()> {
|
||||||
let lua = Lua::new();
|
let lua = Lua::new();
|
||||||
|
|
||||||
let f = lua.create_function(|_, args: Variadic<String>| {
|
let f = lua.create_function(|_, args: Variadic<String>| {
|
||||||
@@ -870,7 +992,7 @@ fn large_args_ref() -> Result<()> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn chunk_env() -> Result<()> {
|
fn test_chunk_env() -> Result<()> {
|
||||||
let lua = Lua::new();
|
let lua = Lua::new();
|
||||||
|
|
||||||
let assert: Function = lua.globals().get("assert")?;
|
let assert: Function = lua.globals().get("assert")?;
|
||||||
@@ -912,7 +1034,7 @@ fn chunk_env() -> Result<()> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn context_thread() -> Result<()> {
|
fn test_context_thread() -> Result<()> {
|
||||||
let lua = Lua::new();
|
let lua = Lua::new();
|
||||||
|
|
||||||
let f = lua
|
let f = lua
|
||||||
@@ -935,7 +1057,7 @@ fn context_thread() -> Result<()> {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(any(feature = "lua51", feature = "luajit"))]
|
#[cfg(any(feature = "lua51", feature = "luajit"))]
|
||||||
fn context_thread_51() -> Result<()> {
|
fn test_context_thread_51() -> Result<()> {
|
||||||
let lua = Lua::new();
|
let lua = Lua::new();
|
||||||
|
|
||||||
let thread = lua.create_thread(
|
let thread = lua.create_thread(
|
||||||
@@ -965,3 +1087,30 @@ fn test_jit_version() -> Result<()> {
|
|||||||
.contains("LuaJIT"));
|
.contains("LuaJIT"));
|
||||||
Ok(())
|
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
-14
@@ -1,15 +1,3 @@
|
|||||||
#![cfg_attr(
|
|
||||||
all(feature = "luajit", target_os = "macos", target_arch = "x86_64"),
|
|
||||||
feature(link_args)
|
|
||||||
)]
|
|
||||||
|
|
||||||
#[cfg_attr(
|
|
||||||
all(feature = "luajit", target_os = "macos", target_arch = "x86_64"),
|
|
||||||
link_args = "-pagezero_size 10000 -image_base 100000000",
|
|
||||||
allow(unused_attributes)
|
|
||||||
)]
|
|
||||||
extern "system" {}
|
|
||||||
|
|
||||||
use std::panic::catch_unwind;
|
use std::panic::catch_unwind;
|
||||||
|
|
||||||
use mlua::{Error, Function, Lua, Result, Thread, ThreadStatus};
|
use mlua::{Error, Function, Lua, Result, Thread, ThreadStatus};
|
||||||
@@ -106,7 +94,50 @@ fn test_thread() -> Result<()> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[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 lua = Lua::new();
|
||||||
|
|
||||||
let thrd_main = lua.create_function(|_, ()| Ok(()))?;
|
let thrd_main = lua.create_function(|_, ()| Ok(()))?;
|
||||||
@@ -130,7 +161,7 @@ fn coroutine_from_closure() -> Result<()> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn coroutine_panic() {
|
fn test_coroutine_panic() {
|
||||||
match catch_unwind(|| -> Result<()> {
|
match catch_unwind(|| -> Result<()> {
|
||||||
// check that coroutines propagate panics correctly
|
// check that coroutines propagate panics correctly
|
||||||
let lua = Lua::new();
|
let lua = Lua::new();
|
||||||
|
|||||||
@@ -1,15 +1,3 @@
|
|||||||
#![cfg_attr(
|
|
||||||
all(feature = "luajit", target_os = "macos", target_arch = "x86_64"),
|
|
||||||
feature(link_args)
|
|
||||||
)]
|
|
||||||
|
|
||||||
#[cfg_attr(
|
|
||||||
all(feature = "luajit", target_os = "macos", target_arch = "x86_64"),
|
|
||||||
link_args = "-pagezero_size 10000 -image_base 100000000",
|
|
||||||
allow(unused_attributes)
|
|
||||||
)]
|
|
||||||
extern "system" {}
|
|
||||||
|
|
||||||
use std::os::raw::c_void;
|
use std::os::raw::c_void;
|
||||||
|
|
||||||
use mlua::{Function, LightUserData, Lua, Result};
|
use mlua::{Function, LightUserData, Lua, Result};
|
||||||
|
|||||||
+205
-23
@@ -1,23 +1,14 @@
|
|||||||
#![cfg_attr(
|
use std::sync::{Arc, Mutex, RwLock};
|
||||||
all(feature = "luajit", target_os = "macos", target_arch = "x86_64"),
|
|
||||||
feature(link_args)
|
|
||||||
)]
|
|
||||||
|
|
||||||
#[cfg_attr(
|
#[cfg(not(feature = "send"))]
|
||||||
all(feature = "luajit", target_os = "macos", target_arch = "x86_64"),
|
use std::{cell::RefCell, rc::Rc};
|
||||||
link_args = "-pagezero_size 10000 -image_base 100000000",
|
|
||||||
allow(unused_attributes)
|
|
||||||
)]
|
|
||||||
extern "system" {}
|
|
||||||
|
|
||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
#[cfg(feature = "lua54")]
|
#[cfg(feature = "lua54")]
|
||||||
use std::sync::atomic::{AtomicI64, Ordering};
|
use std::sync::atomic::{AtomicI64, Ordering};
|
||||||
|
|
||||||
use mlua::{
|
use mlua::{
|
||||||
AnyUserData, ExternalError, Function, Lua, MetaMethod, Result, String, UserData,
|
AnyUserData, Error, ExternalError, Function, Lua, MetaMethod, Nil, Result, String, UserData,
|
||||||
UserDataMethods, Value,
|
UserDataFields, UserDataMethods, Value,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -25,8 +16,8 @@ fn test_user_data() -> Result<()> {
|
|||||||
struct UserData1(i64);
|
struct UserData1(i64);
|
||||||
struct UserData2(Box<i64>);
|
struct UserData2(Box<i64>);
|
||||||
|
|
||||||
impl UserData for UserData1 {};
|
impl UserData for UserData1 {}
|
||||||
impl UserData for UserData2 {};
|
impl UserData for UserData2 {}
|
||||||
|
|
||||||
let lua = Lua::new();
|
let lua = Lua::new();
|
||||||
let userdata1 = lua.create_userdata(UserData1(1))?;
|
let userdata1 = lua.create_userdata(UserData1(1))?;
|
||||||
@@ -167,10 +158,10 @@ fn test_metamethods() -> Result<()> {
|
|||||||
assert!(userdata2.equals(userdata3)?);
|
assert!(userdata2.equals(userdata3)?);
|
||||||
|
|
||||||
let userdata1: AnyUserData = globals.get("userdata1")?;
|
let userdata1: AnyUserData = globals.get("userdata1")?;
|
||||||
assert!(userdata1.has_metamethod(MetaMethod::Add)?);
|
assert!(userdata1.get_metatable()?.contains(MetaMethod::Add)?);
|
||||||
assert!(userdata1.has_metamethod(MetaMethod::Sub)?);
|
assert!(userdata1.get_metatable()?.contains(MetaMethod::Sub)?);
|
||||||
assert!(userdata1.has_metamethod(MetaMethod::Index)?);
|
assert!(userdata1.get_metatable()?.contains(MetaMethod::Index)?);
|
||||||
assert!(!userdata1.has_metamethod(MetaMethod::Pow)?);
|
assert!(!userdata1.get_metatable()?.contains(MetaMethod::Pow)?);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -262,7 +253,7 @@ fn test_gc_userdata() -> Result<()> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn detroys_userdata() -> Result<()> {
|
fn test_destroy_userdata() -> Result<()> {
|
||||||
struct MyUserdata(Arc<()>);
|
struct MyUserdata(Arc<()>);
|
||||||
|
|
||||||
impl UserData for MyUserdata {}
|
impl UserData for MyUserdata {}
|
||||||
@@ -284,7 +275,7 @@ fn detroys_userdata() -> Result<()> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn user_value() -> Result<()> {
|
fn test_user_value() -> Result<()> {
|
||||||
struct MyUserData;
|
struct MyUserData;
|
||||||
|
|
||||||
impl UserData for MyUserData {}
|
impl UserData for MyUserData {}
|
||||||
@@ -307,7 +298,7 @@ fn test_functions() -> Result<()> {
|
|||||||
methods.add_function("get_value", |_, ud: AnyUserData| {
|
methods.add_function("get_value", |_, ud: AnyUserData| {
|
||||||
Ok(ud.borrow::<MyUserData>()?.0)
|
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;
|
ud.borrow_mut::<MyUserData>()?.0 = value;
|
||||||
Ok(())
|
Ok(())
|
||||||
});
|
});
|
||||||
@@ -347,3 +338,194 @@ fn test_functions() -> Result<()> {
|
|||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_fields() -> Result<()> {
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
struct MyUserData(i64);
|
||||||
|
|
||||||
|
impl UserData for MyUserData {
|
||||||
|
fn add_fields<'lua, F: UserDataFields<'lua, Self>>(fields: &mut F) {
|
||||||
|
fields.add_field_method_get("val", |_, data| Ok(data.0));
|
||||||
|
fields.add_field_method_set("val", |_, data, val| {
|
||||||
|
data.0 = val;
|
||||||
|
Ok(())
|
||||||
|
});
|
||||||
|
|
||||||
|
// Use userdata "uservalue" storage
|
||||||
|
fields.add_field_function_get("uval", |_, ud| ud.get_user_value::<Option<String>>());
|
||||||
|
fields
|
||||||
|
.add_field_function_set("uval", |_, ud, s| ud.set_user_value::<Option<String>>(s));
|
||||||
|
|
||||||
|
fields.add_meta_field_with(MetaMethod::Index, |lua| {
|
||||||
|
let index = lua.create_table()?;
|
||||||
|
index.set("f", 321)?;
|
||||||
|
Ok(index)
|
||||||
|
});
|
||||||
|
fields.add_meta_field_with(MetaMethod::NewIndex, |lua| {
|
||||||
|
lua.create_function(|lua, (_, field, val): (AnyUserData, String, Value)| {
|
||||||
|
lua.globals().set(field, val)?;
|
||||||
|
Ok(())
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let lua = Lua::new();
|
||||||
|
let globals = lua.globals();
|
||||||
|
globals.set("ud", MyUserData(7))?;
|
||||||
|
lua.load(
|
||||||
|
r#"
|
||||||
|
assert(ud.val == 7)
|
||||||
|
ud.val = 10
|
||||||
|
assert(ud.val == 10)
|
||||||
|
|
||||||
|
assert(ud.uval == nil)
|
||||||
|
ud.uval = "hello"
|
||||||
|
assert(ud.uval == "hello")
|
||||||
|
|
||||||
|
assert(ud.f == 321)
|
||||||
|
|
||||||
|
ud.unknown = 789
|
||||||
|
assert(unknown == 789)
|
||||||
|
"#,
|
||||||
|
)
|
||||||
|
.exec()?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_metatable() -> Result<()> {
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
struct MyUserData(i64);
|
||||||
|
|
||||||
|
impl UserData for MyUserData {
|
||||||
|
fn add_fields<'lua, F: UserDataFields<'lua, Self>>(fields: &mut F) {
|
||||||
|
fields.add_meta_field_with("__type_name", |_| Ok("MyUserData"));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
|
||||||
|
methods.add_function("my_type_name", |_, data: AnyUserData| {
|
||||||
|
let metatable = data.get_metatable()?;
|
||||||
|
metatable.get::<_, String>("__type_name")
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let lua = Lua::new();
|
||||||
|
let globals = lua.globals();
|
||||||
|
globals.set("ud", MyUserData(7))?;
|
||||||
|
lua.load(
|
||||||
|
r#"
|
||||||
|
assert(ud:my_type_name() == "MyUserData")
|
||||||
|
"#,
|
||||||
|
)
|
||||||
|
.exec()?;
|
||||||
|
|
||||||
|
let ud: AnyUserData = globals.get("ud")?;
|
||||||
|
let metatable = ud.get_metatable()?;
|
||||||
|
|
||||||
|
match metatable.get::<_, Value>("__gc") {
|
||||||
|
Ok(_) => panic!("expected MetaMethodRestricted, got no error"),
|
||||||
|
Err(Error::MetaMethodRestricted(_)) => {}
|
||||||
|
Err(e) => panic!("expected MetaMethodRestricted, got {:?}", e),
|
||||||
|
}
|
||||||
|
|
||||||
|
match metatable.set(MetaMethod::Index, Nil) {
|
||||||
|
Ok(_) => panic!("expected MetaMethodRestricted, got no error"),
|
||||||
|
Err(Error::MetaMethodRestricted(_)) => {}
|
||||||
|
Err(e) => panic!("expected MetaMethodRestricted, got {:?}", e),
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut methods = metatable
|
||||||
|
.pairs()
|
||||||
|
.into_iter()
|
||||||
|
.map(|kv: Result<(_, Value)>| Ok(kv?.0))
|
||||||
|
.collect::<Result<Vec<_>>>()?;
|
||||||
|
methods.sort_by_cached_key(|k| k.name().to_owned());
|
||||||
|
assert_eq!(methods, vec![MetaMethod::Index, "__type_name".into()]);
|
||||||
|
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
struct MyUserData2(i64);
|
||||||
|
|
||||||
|
impl UserData for MyUserData2 {
|
||||||
|
fn add_fields<'lua, F: UserDataFields<'lua, Self>>(fields: &mut F) {
|
||||||
|
fields.add_meta_field_with("__index", |_| Ok(1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
match lua.create_userdata(MyUserData2(1)) {
|
||||||
|
Ok(_) => panic!("expected MetaMethodTypeError, got no error"),
|
||||||
|
Err(Error::MetaMethodTypeError { .. }) => {}
|
||||||
|
Err(e) => panic!("expected MetaMethodTypeError, got {:?}", e),
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_userdata_wrapped() -> Result<()> {
|
||||||
|
struct MyUserData(i64);
|
||||||
|
|
||||||
|
impl UserData for MyUserData {
|
||||||
|
fn add_fields<'lua, F: UserDataFields<'lua, Self>>(fields: &mut F) {
|
||||||
|
fields.add_field_method_get("data", |_, this| Ok(this.0));
|
||||||
|
fields.add_field_method_set("data", |_, this, val| {
|
||||||
|
this.0 = val;
|
||||||
|
Ok(())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let lua = Lua::new();
|
||||||
|
let globals = lua.globals();
|
||||||
|
|
||||||
|
#[cfg(not(feature = "send"))]
|
||||||
|
{
|
||||||
|
let ud1 = Rc::new(RefCell::new(MyUserData(1)));
|
||||||
|
globals.set("rc_refcell_ud", ud1.clone())?;
|
||||||
|
lua.load(
|
||||||
|
r#"
|
||||||
|
rc_refcell_ud.data = rc_refcell_ud.data + 1
|
||||||
|
assert(rc_refcell_ud.data == 2)
|
||||||
|
"#,
|
||||||
|
)
|
||||||
|
.exec()?;
|
||||||
|
assert_eq!(ud1.borrow().0, 2);
|
||||||
|
globals.set("rc_refcell_ud", Nil)?;
|
||||||
|
lua.gc_collect()?;
|
||||||
|
assert_eq!(Rc::strong_count(&ud1), 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
let ud2 = Arc::new(Mutex::new(MyUserData(2)));
|
||||||
|
globals.set("arc_mutex_ud", ud2.clone())?;
|
||||||
|
lua.load(
|
||||||
|
r#"
|
||||||
|
arc_mutex_ud.data = arc_mutex_ud.data + 1
|
||||||
|
assert(arc_mutex_ud.data == 3)
|
||||||
|
"#,
|
||||||
|
)
|
||||||
|
.exec()?;
|
||||||
|
assert_eq!(ud2.lock().unwrap().0, 3);
|
||||||
|
|
||||||
|
let ud3 = Arc::new(RwLock::new(MyUserData(3)));
|
||||||
|
globals.set("arc_rwlock_ud", ud3.clone())?;
|
||||||
|
lua.load(
|
||||||
|
r#"
|
||||||
|
arc_rwlock_ud.data = arc_rwlock_ud.data + 1
|
||||||
|
assert(arc_rwlock_ud.data == 4)
|
||||||
|
"#,
|
||||||
|
)
|
||||||
|
.exec()?;
|
||||||
|
assert_eq!(ud3.read().unwrap().0, 4);
|
||||||
|
|
||||||
|
// Test drop
|
||||||
|
globals.set("arc_mutex_ud", Nil)?;
|
||||||
|
globals.set("arc_rwlock_ud", Nil)?;
|
||||||
|
lua.gc_collect()?;
|
||||||
|
assert_eq!(Arc::strong_count(&ud2), 1);
|
||||||
|
assert_eq!(Arc::strong_count(&ud3), 1);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,15 +1,3 @@
|
|||||||
#![cfg_attr(
|
|
||||||
all(feature = "luajit", target_os = "macos", target_arch = "x86_64"),
|
|
||||||
feature(link_args)
|
|
||||||
)]
|
|
||||||
|
|
||||||
#[cfg_attr(
|
|
||||||
all(feature = "luajit", target_os = "macos", target_arch = "x86_64"),
|
|
||||||
link_args = "-pagezero_size 10000 -image_base 100000000",
|
|
||||||
allow(unused_attributes)
|
|
||||||
)]
|
|
||||||
extern "system" {}
|
|
||||||
|
|
||||||
use mlua::{Lua, Result, Value};
|
use mlua::{Lua, Result, Value};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
Reference in New Issue
Block a user