mirror of
https://github.com/mlua-rs/mlua
synced 2026-06-08 16:05:43 +00:00
Compare commits
97 Commits
v0.8.0-beta.4
...
v0.8.8
| Author | SHA1 | Date | |
|---|---|---|---|
| 34679e105d | |||
| bc194981fc | |||
| c9715aa5d9 | |||
| c108dc8213 | |||
| e86ef9d755 | |||
| 05eb20f9c6 | |||
| 9716918517 | |||
| c88417a3b6 | |||
| 6e95386f30 | |||
| 6807dfa22e | |||
| f27c49f931 | |||
| 693a808b6e | |||
| a6ca65aa74 | |||
| a7278cab78 | |||
| e1bbd00a33 | |||
| e98998d6ac | |||
| cca177df5b | |||
| 65396a910f | |||
| de69d10d73 | |||
| 959e61b97c | |||
| 1040c0a347 | |||
| 9045419586 | |||
| fcd162f3eb | |||
| 0354703dbf | |||
| 9831d0e397 | |||
| 7f14d93c2b | |||
| 29c6c9cb58 | |||
| e01af22bac | |||
| cfa959e599 | |||
| e523fb2c86 | |||
| 81a9998559 | |||
| 050ac4f5ad | |||
| 967cbb53b0 | |||
| 44a8d86d8a | |||
| 33278d4a64 | |||
| 20a16839aa | |||
| 83ed442bf9 | |||
| bf6708ba58 | |||
| 0cd724f63b | |||
| 5330b900fd | |||
| ee32dc33f3 | |||
| ab029b087d | |||
| 185fee956d | |||
| 4fc69be5f6 | |||
| 3ec076693a | |||
| 95adf8df8e | |||
| 4a25eab257 | |||
| 40fe937878 | |||
| a6b178328d | |||
| f3f173fcb6 | |||
| d3b48cf2f3 | |||
| f9ff6116db | |||
| f75af6d75f | |||
| 059e41bafb | |||
| f7ee6dc635 | |||
| 0919ff21c9 | |||
| 553251761f | |||
| b46b476f80 | |||
| 70e16b51ae | |||
| 9596b97faa | |||
| 8cbb3d8fae | |||
| e7f494530f | |||
| 3746c3614f | |||
| 9af1aaf889 | |||
| c20eb20a59 | |||
| 04ba93137c | |||
| 113f91ace3 | |||
| ff0d923aae | |||
| d9ac7abff2 | |||
| 48aa97351d | |||
| 4516ca0bb5 | |||
| 9005f32a98 | |||
| 93d36b9068 | |||
| f9f32bffce | |||
| da5010e6de | |||
| 4afca8f5bb | |||
| b065db37c2 | |||
| a100ebb3d7 | |||
| a05a58b258 | |||
| 3766cee4dd | |||
| 99becdb590 | |||
| 799b4e9f0d | |||
| 93f0b2a5de | |||
| 62db3adde1 | |||
| afa343ff08 | |||
| 4f06f614a9 | |||
| 30ba616a8a | |||
| 0076aa735a | |||
| bcf2cbea37 | |||
| 8cd594c609 | |||
| 8d71ea79aa | |||
| 2a8c5c7f82 | |||
| 6b2ceb60c4 | |||
| 3aac7131ee | |||
| 1807fa789c | |||
| 7db7526a08 | |||
| bea9b0f8b5 |
@@ -10,14 +10,14 @@ jobs:
|
||||
options: --security-opt seccomp=unconfined
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Generate code coverage
|
||||
- name: Generate coverage report
|
||||
run: |
|
||||
cargo tarpaulin --verbose --features lua54,vendored,async,send,serialize,macros --out xml --exclude-files benches --exclude-files build --exclude-files mlua_derive --exclude-files src/ffi --exclude-files tests
|
||||
|
||||
- name: Upload to codecov.io
|
||||
uses: codecov/codecov-action@v1
|
||||
- name: Upload report to codecov.io
|
||||
uses: codecov/codecov-action@v3
|
||||
with:
|
||||
token: ${{secrets.CODECOV_TOKEN}}
|
||||
fail_ci_if_error: false
|
||||
|
||||
+135
-144
@@ -7,98 +7,94 @@ jobs:
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-20.04, macos-latest, windows-latest]
|
||||
os: [ubuntu-22.04, macos-latest, windows-latest]
|
||||
rust: [stable]
|
||||
lua: [lua54, lua53, lua52, lua51, luajit, luau]
|
||||
include:
|
||||
- os: ubuntu-20.04
|
||||
target: x86_64-unknown-linux-gnu
|
||||
- os: macos-latest
|
||||
target: x86_64-apple-darwin
|
||||
- os: windows-latest
|
||||
target: x86_64-pc-windows-msvc
|
||||
- os: ubuntu-22.04
|
||||
target: x86_64-unknown-linux-gnu
|
||||
- os: macos-latest
|
||||
target: x86_64-apple-darwin
|
||||
- os: windows-latest
|
||||
target: x86_64-pc-windows-msvc
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: ${{ matrix.rust }}
|
||||
target: ${{ matrix.target }}
|
||||
override: true
|
||||
- uses: Swatinem/rust-cache@v1
|
||||
- name: Build ${{ matrix.lua }} vendored
|
||||
run: |
|
||||
cargo build --features "${{ matrix.lua }},vendored"
|
||||
cargo build --features "${{ matrix.lua }},vendored,async,send,serialize,macros,parking_lot"
|
||||
shell: bash
|
||||
- name: Build ${{ matrix.lua }} pkg-config
|
||||
if: ${{ matrix.os == 'ubuntu-20.04' && matrix.lua != 'lua54' }}
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y --no-install-recommends liblua5.3-dev liblua5.2-dev liblua5.1-0-dev libluajit-5.1-dev
|
||||
cargo build --features "${{ matrix.lua }}"
|
||||
- uses: actions/checkout@v3
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
toolchain: ${{ matrix.rust }}
|
||||
target: ${{ matrix.target }}
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
- name: Build ${{ matrix.lua }} vendored
|
||||
run: |
|
||||
cargo build --features "${{ matrix.lua }},vendored"
|
||||
cargo build --features "${{ matrix.lua }},vendored,async,send,serialize,macros,parking_lot"
|
||||
shell: bash
|
||||
- name: Build ${{ matrix.lua }} pkg-config
|
||||
if: ${{ matrix.os == 'ubuntu-22.04' }}
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y --no-install-recommends liblua5.4-dev liblua5.3-dev liblua5.2-dev liblua5.1-0-dev libluajit-5.1-dev
|
||||
cargo build --features "${{ matrix.lua }}"
|
||||
|
||||
build_aarch64_cross_macos:
|
||||
name: Cross-compile to aarch64-apple-darwin
|
||||
runs-on: macos-11.0
|
||||
runs-on: macos-latest
|
||||
needs: build
|
||||
strategy:
|
||||
matrix:
|
||||
lua: [lua54, lua53, lua52, lua51, luajit]
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: stable
|
||||
target: aarch64-apple-darwin
|
||||
override: true
|
||||
- name: Cross-compile
|
||||
run: cargo build --target aarch64-apple-darwin --features "${{ matrix.lua }},vendored,async,send,serialize,macros,parking_lot"
|
||||
- uses: actions/checkout@v3
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
toolchain: stable
|
||||
target: aarch64-apple-darwin
|
||||
- name: Cross-compile
|
||||
run: cargo build --target aarch64-apple-darwin --features "${{ matrix.lua }},vendored,async,send,serialize,macros,parking_lot"
|
||||
|
||||
build_aarch64_cross_ubuntu:
|
||||
name: Cross-compile to aarch64-unknown-linux-gnu
|
||||
runs-on: ubuntu-20.04
|
||||
runs-on: ubuntu-22.04
|
||||
needs: build
|
||||
strategy:
|
||||
matrix:
|
||||
lua: [lua54, lua53, lua52, lua51, luajit]
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: stable
|
||||
target: aarch64-unknown-linux-gnu
|
||||
override: true
|
||||
- name: Install ARM compiler toolchain
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y --no-install-recommends gcc-aarch64-linux-gnu libc6-dev-arm64-cross
|
||||
shell: bash
|
||||
- name: Cross-compile
|
||||
run: cargo build --target aarch64-unknown-linux-gnu --features "${{ matrix.lua }},vendored,async,send,serialize,macros,parking_lot"
|
||||
shell: bash
|
||||
- uses: actions/checkout@v3
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
toolchain: stable
|
||||
target: aarch64-unknown-linux-gnu
|
||||
- name: Install ARM compiler toolchain
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y --no-install-recommends gcc-aarch64-linux-gnu libc6-dev-arm64-cross
|
||||
shell: bash
|
||||
- name: Cross-compile
|
||||
run: cargo build --target aarch64-unknown-linux-gnu --features "${{ matrix.lua }},vendored,async,send,serialize,macros,parking_lot"
|
||||
shell: bash
|
||||
|
||||
build_armv7_cross_ubuntu:
|
||||
name: Cross-compile to armv7-unknown-linux-gnueabihf
|
||||
runs-on: ubuntu-20.04
|
||||
runs-on: ubuntu-22.04
|
||||
needs: build
|
||||
strategy:
|
||||
matrix:
|
||||
lua: [lua54, lua53, lua52, lua51]
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: stable
|
||||
target: armv7-unknown-linux-gnueabihf
|
||||
override: true
|
||||
- name: Install ARM compiler toolchain
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y --no-install-recommends gcc-arm-linux-gnueabihf libc-dev-armhf-cross
|
||||
shell: bash
|
||||
- name: Cross-compile
|
||||
run: cargo build --target armv7-unknown-linux-gnueabihf --features "${{ matrix.lua }},vendored,async,send,serialize,macros,parking_lot"
|
||||
shell: bash
|
||||
- uses: actions/checkout@v3
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
toolchain: stable
|
||||
target: armv7-unknown-linux-gnueabihf
|
||||
- name: Install ARM compiler toolchain
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y --no-install-recommends gcc-arm-linux-gnueabihf libc-dev-armhf-cross
|
||||
shell: bash
|
||||
- name: Cross-compile
|
||||
run: cargo build --target armv7-unknown-linux-gnueabihf --features "${{ matrix.lua }},vendored,async,send,serialize,macros,parking_lot"
|
||||
shell: bash
|
||||
|
||||
test:
|
||||
name: Test
|
||||
@@ -106,35 +102,34 @@ jobs:
|
||||
needs: build
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-20.04, macos-latest, windows-latest]
|
||||
os: [ubuntu-22.04, macos-latest, windows-latest]
|
||||
rust: [stable, nightly]
|
||||
lua: [lua54, lua53, lua52, lua51, luajit, luajit52, luau]
|
||||
include:
|
||||
- os: ubuntu-20.04
|
||||
target: x86_64-unknown-linux-gnu
|
||||
- os: macos-latest
|
||||
target: x86_64-apple-darwin
|
||||
- os: windows-latest
|
||||
target: x86_64-pc-windows-msvc
|
||||
- os: ubuntu-22.04
|
||||
target: x86_64-unknown-linux-gnu
|
||||
- os: macos-latest
|
||||
target: x86_64-apple-darwin
|
||||
- os: windows-latest
|
||||
target: x86_64-pc-windows-msvc
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: ${{ matrix.rust }}
|
||||
target: ${{ matrix.target }}
|
||||
override: true
|
||||
- uses: Swatinem/rust-cache@v1
|
||||
- name: Run ${{ matrix.lua }} tests
|
||||
run: |
|
||||
cargo test --features "${{ matrix.lua }},vendored"
|
||||
cargo test --features "${{ matrix.lua }},vendored,async,send,serialize,macros,parking_lot"
|
||||
shell: bash
|
||||
- name: Run compile tests (macos lua54)
|
||||
if: ${{ matrix.os == 'macos-latest' && matrix.lua == 'lua54' }}
|
||||
run: |
|
||||
TRYBUILD=overwrite cargo test --features "${{ matrix.lua }},vendored" -- --ignored
|
||||
TRYBUILD=overwrite cargo test --features "${{ matrix.lua }},vendored,async,send,serialize,macros,parking_lot" -- --ignored
|
||||
shell: bash
|
||||
- uses: actions/checkout@v3
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
toolchain: ${{ matrix.rust }}
|
||||
target: ${{ matrix.target }}
|
||||
- uses: Swatinem/rust-cache@v1
|
||||
- name: Run ${{ matrix.lua }} tests
|
||||
run: |
|
||||
cargo test --features "${{ matrix.lua }},vendored"
|
||||
cargo test --features "${{ matrix.lua }},vendored,async,send,serialize,macros,parking_lot"
|
||||
shell: bash
|
||||
- name: Run compile tests (macos lua54)
|
||||
if: ${{ matrix.os == 'macos-latest' && matrix.lua == 'lua54' }}
|
||||
run: |
|
||||
TRYBUILD=overwrite cargo test --features "${{ matrix.lua }},vendored" -- --ignored
|
||||
TRYBUILD=overwrite cargo test --features "${{ matrix.lua }},vendored,async,send,serialize,macros,parking_lot" -- --ignored
|
||||
shell: bash
|
||||
|
||||
test_with_sanitizer:
|
||||
name: Test with address sanitizer
|
||||
@@ -142,25 +137,24 @@ jobs:
|
||||
needs: build
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-20.04]
|
||||
os: [ubuntu-22.04]
|
||||
rust: [nightly]
|
||||
lua: [lua54, lua53, lua52, lua51, luajit, luau]
|
||||
include:
|
||||
- os: ubuntu-20.04
|
||||
target: x86_64-unknown-linux-gnu
|
||||
- os: ubuntu-22.04
|
||||
target: x86_64-unknown-linux-gnu
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: ${{ matrix.rust }}
|
||||
target: ${{ matrix.target }}
|
||||
override: true
|
||||
- uses: Swatinem/rust-cache@v1
|
||||
- name: Run ${{ matrix.lua }} tests with address sanitizer
|
||||
run: |
|
||||
RUSTFLAGS="-Z sanitizer=address" \
|
||||
cargo test --tests --features "${{ matrix.lua }},vendored,async,send,serialize,macros,parking_lot" --target x86_64-unknown-linux-gnu -- --skip test_too_many_recursions
|
||||
shell: bash
|
||||
- uses: actions/checkout@v3
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
toolchain: ${{ matrix.rust }}
|
||||
target: ${{ matrix.target }}
|
||||
- uses: Swatinem/rust-cache@v1
|
||||
- name: Run ${{ matrix.lua }} tests with address sanitizer
|
||||
run: |
|
||||
RUSTFLAGS="-Z sanitizer=address" \
|
||||
cargo test --tests --features "${{ matrix.lua }},vendored,async,send,serialize,macros,parking_lot" --target x86_64-unknown-linux-gnu -- --skip test_too_many_recursions
|
||||
shell: bash
|
||||
|
||||
test_modules:
|
||||
name: Test modules
|
||||
@@ -168,27 +162,26 @@ jobs:
|
||||
needs: build
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-20.04, macos-latest]
|
||||
os: [ubuntu-22.04, macos-latest]
|
||||
rust: [stable]
|
||||
lua: [lua54, lua53, lua52, lua51, luajit]
|
||||
include:
|
||||
- os: ubuntu-20.04
|
||||
target: x86_64-unknown-linux-gnu
|
||||
- os: macos-latest
|
||||
target: x86_64-apple-darwin
|
||||
- os: ubuntu-22.04
|
||||
target: x86_64-unknown-linux-gnu
|
||||
- os: macos-latest
|
||||
target: x86_64-apple-darwin
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: ${{ matrix.rust }}
|
||||
target: ${{ matrix.target }}
|
||||
override: true
|
||||
- uses: Swatinem/rust-cache@v1
|
||||
- name: Run ${{ matrix.lua }} module tests
|
||||
run: |
|
||||
(cd examples/module && cargo build --release --features "${{ matrix.lua }},vendored")
|
||||
(cd tests/module && cargo test --release --features "${{ matrix.lua }},vendored")
|
||||
shell: bash
|
||||
- uses: actions/checkout@v3
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
toolchain: ${{ matrix.rust }}
|
||||
target: ${{ matrix.target }}
|
||||
- uses: Swatinem/rust-cache@v1
|
||||
- name: Run ${{ matrix.lua }} module tests
|
||||
run: |
|
||||
(cd tests/module && cargo build --release --features "${{ matrix.lua }}")
|
||||
(cd tests/module/loader && cargo test --release --features "${{ matrix.lua }},vendored")
|
||||
shell: bash
|
||||
|
||||
test_modules_windows:
|
||||
name: Test modules on Windows
|
||||
@@ -201,41 +194,39 @@ jobs:
|
||||
run:
|
||||
shell: msys2 {0}
|
||||
steps:
|
||||
- uses: msys2/setup-msys2@v2
|
||||
- uses: actions/checkout@v2
|
||||
- name: Install Rust & Lua
|
||||
run: |
|
||||
pacman -S --noconfirm mingw-w64-x86_64-rust mingw-w64-x86_64-lua mingw-w64-x86_64-luajit mingw-w64-x86_64-pkg-config
|
||||
- name: Run ${{ matrix.lua }} module tests
|
||||
run: |
|
||||
(cd examples/module && cargo build --release --features "${{ matrix.lua }}")
|
||||
(cd tests/module && cargo test --release --features "${{ matrix.lua }}")
|
||||
- uses: msys2/setup-msys2@v2
|
||||
- uses: actions/checkout@v3
|
||||
- name: Install Rust & Lua
|
||||
run: |
|
||||
pacman -S --noconfirm mingw-w64-x86_64-rust mingw-w64-x86_64-lua mingw-w64-x86_64-luajit mingw-w64-x86_64-pkg-config
|
||||
- name: Run ${{ matrix.lua }} module tests
|
||||
run: |
|
||||
(cd tests/module && cargo build --release --features "${{ matrix.lua }}")
|
||||
(cd tests/module/loader && cargo test --release --features "${{ matrix.lua }}")
|
||||
|
||||
rustfmt:
|
||||
name: Rustfmt
|
||||
runs-on: ubuntu-20.04
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: stable
|
||||
components: rustfmt
|
||||
override: true
|
||||
- run: cargo fmt -- --check
|
||||
- uses: actions/checkout@v3
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
toolchain: stable
|
||||
components: rustfmt
|
||||
- run: cargo fmt -- --check
|
||||
|
||||
clippy:
|
||||
name: Clippy check
|
||||
runs-on: ubuntu-20.04
|
||||
runs-on: ubuntu-22.04
|
||||
strategy:
|
||||
matrix:
|
||||
lua: [lua54, lua53, lua52, lua51, luajit, luau]
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions-rs/toolchain@v1
|
||||
- uses: actions/checkout@v3
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
toolchain: nightly
|
||||
components: clippy
|
||||
override: true
|
||||
toolchain: nightly
|
||||
components: clippy
|
||||
- uses: actions-rs/clippy-check@v1
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
@@ -1,3 +1,72 @@
|
||||
## v0.8.8
|
||||
|
||||
- Fix potential deadlock when trying to reuse dropped registry keys.
|
||||
- Optimize userdata methods call when __index and fields_getters are nil
|
||||
|
||||
## v0.8.7
|
||||
|
||||
- Minimum Luau updated to 0.555 (`LUAI_MAXCSTACK` limit increased to 100000)
|
||||
- `_VERSION` in Luau now includes version number
|
||||
- Fixed lifetime of `DebugNames` in `Debug::names()` and `DebugSource` in `Debug::source()`
|
||||
- Fixed subtraction overflow when calculating index for `MultiValue::get()`
|
||||
|
||||
## v0.8.6
|
||||
|
||||
- Fixed bug when recycled Registry slot can be set to Nil
|
||||
|
||||
## v0.8.5
|
||||
|
||||
- Fixed potential unsoundness when using `Layout::from_size_align_unchecked` and Rust 1.65+
|
||||
- Performance optimizations around string and table creation in standalone mode
|
||||
- Added fast track path to Table `get`/`set`/`len` methods without metatable
|
||||
- Added new methods `push`/`pop`/`raw_push`/`raw_pop` to Table
|
||||
- Fix getting caller information from `Lua::load`
|
||||
- Better checks and tests when trying to modify a Luau readonly table
|
||||
|
||||
## v0.8.4
|
||||
|
||||
- Minimal Luau updated to 0.548
|
||||
|
||||
## v0.8.3
|
||||
|
||||
- Close to-be-closed variables for Lua 5.4 when using call_async functions (#192)
|
||||
- Fixed Lua assertion when inspecting another thread stack. (#195)
|
||||
- Use more reliable way to create LuaJIT VM (which can fail if use Rust allocator on non-x86 platforms)
|
||||
|
||||
## v0.8.2
|
||||
|
||||
- Performance optimizations in handling UserData
|
||||
- Minimal Luau updated to 0.536
|
||||
- Fixed bug in `Function::bind` when passing empty binds and no arguments (#189)
|
||||
|
||||
## v0.8.1
|
||||
|
||||
- Added `Lua::create_proxy` for accessing to UserData static fields and functions without instance
|
||||
- Added `Table::to_pointer()` and `String::to_pointer()` functions
|
||||
- Bugfixes and improvements (#176 #179)
|
||||
|
||||
## v0.8.0
|
||||
Changes since 0.7.4
|
||||
- Roblox Luau support
|
||||
- Removed C glue
|
||||
- Added async support to `__index` and `__newindex` metamethods
|
||||
- Added `Function::info()` to get information about functions (#149).
|
||||
- Added `parking_lot` dependency under feature flag (for `UserData`)
|
||||
- `Hash` implementation for Lua String
|
||||
- Added `Value::to_pointer()` function
|
||||
- Performance improvements
|
||||
|
||||
Breaking changes:
|
||||
- Refactored `AsChunk` trait (added implementation for `Path` and `PathBuf`).
|
||||
|
||||
## v0.8.0-beta.5
|
||||
|
||||
- Lua sources no longer needed to build modules
|
||||
- Added `__iter` metamethod for Luau
|
||||
- Added `Value::to_pointer()` function
|
||||
- Added `Function::coverage` for Luau to obtain coverage report
|
||||
- Bugfixes and improvements (#153 #161 #168)
|
||||
|
||||
## v0.8.0-beta.4
|
||||
|
||||
- Removed `&Lua` from `Lua::set_interrupt` as it's not safe (introduced in v0.8.0-beta.3)
|
||||
|
||||
+7
-9
@@ -1,12 +1,12 @@
|
||||
[package]
|
||||
name = "mlua"
|
||||
version = "0.8.0-beta.4" # remember to update html_root_url and mlua_derive
|
||||
version = "0.8.8" # remember to update html_root_url and mlua_derive
|
||||
authors = ["Aleksandr Orlenko <zxteam@pm.me>", "kyren <catherine@chucklefish.org>"]
|
||||
edition = "2018"
|
||||
edition = "2021"
|
||||
repository = "https://github.com/khvzak/mlua"
|
||||
documentation = "https://docs.rs/mlua"
|
||||
readme = "README.md"
|
||||
keywords = ["lua", "luajit", "async", "futures", "scripting"]
|
||||
keywords = ["lua", "luajit", "luau", "async", "scripting"]
|
||||
categories = ["api-bindings", "asynchronous"]
|
||||
license = "MIT"
|
||||
links = "lua"
|
||||
@@ -23,8 +23,6 @@ rustdoc-args = ["--cfg", "docsrs"]
|
||||
[workspace]
|
||||
members = [
|
||||
"mlua_derive",
|
||||
"examples/module",
|
||||
"tests/module",
|
||||
]
|
||||
|
||||
[features]
|
||||
@@ -43,7 +41,7 @@ serialize = ["serde", "erased-serde"]
|
||||
macros = ["mlua_derive/macros"]
|
||||
|
||||
[dependencies]
|
||||
mlua_derive = { version = "=0.8.0-beta.1", optional = true, path = "mlua_derive" }
|
||||
mlua_derive = { version = "=0.8.0", optional = true, path = "mlua_derive" }
|
||||
bstr = { version = "0.2", features = ["std"], default_features = false }
|
||||
once_cell = { version = "1.0" }
|
||||
num-traits = { version = "0.2.14" }
|
||||
@@ -59,11 +57,11 @@ parking_lot = { version = "0.12", optional = true }
|
||||
cc = { version = "1.0" }
|
||||
pkg-config = { version = "0.3.17" }
|
||||
lua-src = { version = ">= 544.0.0, < 550.0.0", optional = true }
|
||||
luajit-src = { version = ">= 210.3.1, < 220.0.0", optional = true }
|
||||
luau0-src = { version = "0.2.2", optional = true }
|
||||
luajit-src = { version = ">= 210.4.0, < 220.0.0", optional = true }
|
||||
luau0-src = { version = "0.5.0", optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
rustyline = "9.0"
|
||||
rustyline = "10.0"
|
||||
criterion = { version = "0.3.4", features = ["html_reports", "async_tokio"] }
|
||||
trybuild = "1.0"
|
||||
futures = "0.3.5"
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
# mlua FAQ
|
||||
|
||||
This file is for general questions that don't fit into the README or crate docs.
|
||||
|
||||
## Loading a C module fails with error `undefined symbol: lua_xxx`. How to fix?
|
||||
|
||||
Add the following rustflags to your [.cargo/config](http://doc.crates.io/config.html) in order to properly export Lua symbols:
|
||||
|
||||
```toml
|
||||
[target.x86_64-unknown-linux-gnu]
|
||||
rustflags = ["-C", "link-args=-rdynamic"]
|
||||
|
||||
[target.x86_64-apple-darwin]
|
||||
rustflags = ["-C", "link-args=-rdynamic"]
|
||||
```
|
||||
|
||||
## I want to add support for a Lua VM fork to mlua. Do you accept pull requests?
|
||||
|
||||
Adding new feature flag to support a Lua VM fork is a major step that requires huge effort to maintain it.
|
||||
Regular updates, testing, checking compatibility, etc.
|
||||
That's why I don't plan to support new Lua VM forks or other languages in mlua.
|
||||
@@ -7,11 +7,15 @@
|
||||
[crates.io]: https://crates.io/crates/mlua
|
||||
[API Documentation]: https://docs.rs/mlua/badge.svg
|
||||
[docs.rs]: https://docs.rs/mlua
|
||||
[Coverage Status]: https://codecov.io/gh/khvzak/mlua/branch/master/graph/badge.svg?token=99339FS1CG
|
||||
[Coverage Status]: https://codecov.io/gh/khvzak/mlua/branch/v0.8/graph/badge.svg?token=99339FS1CG
|
||||
[codecov.io]: https://codecov.io/gh/khvzak/mlua
|
||||
[MSRV]: https://img.shields.io/badge/rust-1.53+-brightgreen.svg?&logo=rust
|
||||
[MSRV]: https://img.shields.io/badge/rust-1.56+-brightgreen.svg?&logo=rust
|
||||
|
||||
[Guided Tour](examples/guided_tour.rs)
|
||||
[Guided Tour] | [Benchmarks] | [FAQ]
|
||||
|
||||
[Guided Tour]: examples/guided_tour.rs
|
||||
[Benchmarks]: https://github.com/khvzak/script-bench-rs
|
||||
[FAQ]: FAQ.md
|
||||
|
||||
`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.
|
||||
@@ -106,7 +110,7 @@ Add to `Cargo.toml` :
|
||||
|
||||
``` toml
|
||||
[dependencies]
|
||||
mlua = { version = "0.8.0-beta.4", features = ["lua54", "vendored"] }
|
||||
mlua = { version = "0.8", features = ["lua54", "vendored"] }
|
||||
```
|
||||
|
||||
`main.rs`
|
||||
@@ -141,7 +145,7 @@ Add to `Cargo.toml` :
|
||||
crate-type = ["cdylib"]
|
||||
|
||||
[dependencies]
|
||||
mlua = { version = "0.8.0-beta.4", features = ["lua54", "vendored", "module"] }
|
||||
mlua = { version = "0.8", features = ["lua54", "vendored", "module"] }
|
||||
```
|
||||
|
||||
`lib.rs` :
|
||||
@@ -256,6 +260,14 @@ If you encounter them, a bug report would be very welcome:
|
||||
|
||||
+ 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.
|
||||
|
||||
## Sandboxing
|
||||
|
||||
Please check the [Luau Sandboxing] page if you are interested in running untrusted Lua scripts in controlled environment.
|
||||
|
||||
`mlua` provides `Lua::sandbox` method for enabling sandbox mode (Luau only).
|
||||
|
||||
[Luau Sandboxing]: https://luau-lang.org/sandbox
|
||||
|
||||
## License
|
||||
|
||||
This project is licensed under the [MIT license](LICENSE)
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
use std::path::PathBuf;
|
||||
|
||||
pub fn probe_lua() -> Option<PathBuf> {
|
||||
unreachable!()
|
||||
None
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#![allow(dead_code)]
|
||||
|
||||
use std::env;
|
||||
use std::ops::Bound;
|
||||
use std::path::PathBuf;
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#![allow(dead_code)]
|
||||
|
||||
use std::path::PathBuf;
|
||||
|
||||
pub fn probe_lua() -> Option<PathBuf> {
|
||||
@@ -20,7 +22,6 @@ pub fn probe_lua() -> Option<PathBuf> {
|
||||
#[cfg(feature = "luau")]
|
||||
let artifacts = luau0_src::Build::new().build();
|
||||
|
||||
#[cfg(not(feature = "module"))]
|
||||
artifacts.print_cargo_metadata();
|
||||
|
||||
Some(artifacts.include_dir().to_owned())
|
||||
|
||||
@@ -105,6 +105,10 @@ fn main() {
|
||||
+ "Please, use `pkg-config` or custom mode to link to a Lua dll."
|
||||
);
|
||||
|
||||
#[cfg(all(feature = "luau", feature = "module"))]
|
||||
compile_error!("Luau does not support module mode");
|
||||
|
||||
#[cfg(any(not(feature = "module"), target_os = "windows"))]
|
||||
find::probe_lua();
|
||||
|
||||
println!("cargo:rerun-if-changed=build");
|
||||
|
||||
@@ -7,13 +7,14 @@ edition = "2018"
|
||||
[lib]
|
||||
crate-type = ["cdylib"]
|
||||
|
||||
[workspace]
|
||||
|
||||
[features]
|
||||
lua54 = ["mlua/lua54"]
|
||||
lua53 = ["mlua/lua53"]
|
||||
lua52 = ["mlua/lua52"]
|
||||
lua51 = ["mlua/lua51"]
|
||||
luajit = ["mlua/luajit"]
|
||||
vendored = ["mlua/vendored"]
|
||||
|
||||
[dependencies]
|
||||
mlua = { path = "../..", features = ["module"] }
|
||||
|
||||
@@ -8,32 +8,10 @@ fn used_memory(lua: &Lua, _: ()) -> LuaResult<usize> {
|
||||
Ok(lua.used_memory())
|
||||
}
|
||||
|
||||
fn check_userdata(_: &Lua, ud: MyUserData) -> LuaResult<i32> {
|
||||
Ok(ud.0)
|
||||
}
|
||||
|
||||
#[mlua::lua_module]
|
||||
fn rust_module(lua: &Lua) -> LuaResult<LuaTable> {
|
||||
let exports = lua.create_table()?;
|
||||
exports.set("sum", lua.create_function(sum)?)?;
|
||||
exports.set("used_memory", lua.create_function(used_memory)?)?;
|
||||
exports.set("check_userdata", lua.create_function(check_userdata)?)?;
|
||||
Ok(exports)
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
struct MyUserData(i32);
|
||||
|
||||
impl LuaUserData for MyUserData {}
|
||||
|
||||
#[mlua::lua_module]
|
||||
fn rust_module_second(lua: &Lua) -> LuaResult<LuaTable> {
|
||||
let exports = lua.create_table()?;
|
||||
exports.set("userdata", lua.create_userdata(MyUserData(123))?)?;
|
||||
Ok(exports)
|
||||
}
|
||||
|
||||
#[mlua::lua_module]
|
||||
fn rust_module_error(_: &Lua) -> LuaResult<LuaTable> {
|
||||
Err("custom module error".to_lua_err())
|
||||
}
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@ use rustyline::Editor;
|
||||
|
||||
fn main() {
|
||||
let lua = Lua::new();
|
||||
let mut editor = Editor::<()>::new();
|
||||
let mut editor = Editor::<()>::new().expect("Failed to make rustyline editor");
|
||||
|
||||
loop {
|
||||
let mut prompt = "> ";
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "mlua_derive"
|
||||
version = "0.8.0-beta.1"
|
||||
version = "0.8.0"
|
||||
authors = ["Aleksandr Orlenko <zxteam@pm.me>"]
|
||||
edition = "2018"
|
||||
description = "Procedural macros for the mlua crate."
|
||||
|
||||
+57
-9
@@ -1,4 +1,5 @@
|
||||
use std::borrow::Cow;
|
||||
use std::collections::HashMap;
|
||||
use std::ffi::CString;
|
||||
use std::io::Result as IoResult;
|
||||
use std::path::{Path, PathBuf};
|
||||
@@ -394,20 +395,68 @@ impl<'lua, 'a> Chunk<'lua, 'a> {
|
||||
/// Compiles the chunk and changes mode to binary.
|
||||
///
|
||||
/// It does nothing if the chunk is already binary.
|
||||
#[cfg(feature = "luau")]
|
||||
fn compile(&mut self) {
|
||||
if let Ok(ref source) = self.source {
|
||||
if self.detect_mode() == ChunkMode::Text {
|
||||
let data = self
|
||||
.compiler
|
||||
.get_or_insert_with(Default::default)
|
||||
.compile(source);
|
||||
self.mode = Some(ChunkMode::Binary);
|
||||
self.source = Ok(Cow::Owned(data));
|
||||
#[cfg(feature = "luau")]
|
||||
{
|
||||
let data = self
|
||||
.compiler
|
||||
.get_or_insert_with(Default::default)
|
||||
.compile(source);
|
||||
self.source = Ok(Cow::Owned(data));
|
||||
self.mode = Some(ChunkMode::Binary);
|
||||
}
|
||||
#[cfg(not(feature = "luau"))]
|
||||
if let Ok(func) = self.lua.load_chunk(source.as_ref(), None, None, None) {
|
||||
let data = func.dump(false);
|
||||
self.source = Ok(Cow::Owned(data));
|
||||
self.mode = Some(ChunkMode::Binary);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Fetches compiled bytecode of this chunk from the cache.
|
||||
///
|
||||
/// If not found, compiles the source code and stores it on the cache.
|
||||
pub(crate) fn try_cache(mut self) -> Self {
|
||||
struct ChunksCache(HashMap<Vec<u8>, Vec<u8>>);
|
||||
|
||||
// Try to fetch compiled chunk from cache
|
||||
let mut text_source = None;
|
||||
if let Ok(ref source) = self.source {
|
||||
if self.detect_mode() == ChunkMode::Text {
|
||||
if let Some(cache) = self.lua.app_data_ref::<ChunksCache>() {
|
||||
if let Some(data) = cache.0.get(source.as_ref()) {
|
||||
self.source = Ok(Cow::Owned(data.clone()));
|
||||
self.mode = Some(ChunkMode::Binary);
|
||||
return self;
|
||||
}
|
||||
}
|
||||
text_source = Some(source.as_ref().to_vec());
|
||||
}
|
||||
}
|
||||
|
||||
// Compile and cache the chunk
|
||||
if let Some(text_source) = text_source {
|
||||
self.compile();
|
||||
if let Ok(ref binary_source) = self.source {
|
||||
if self.detect_mode() == ChunkMode::Binary {
|
||||
if let Some(mut cache) = self.lua.app_data_mut::<ChunksCache>() {
|
||||
cache.0.insert(text_source, binary_source.as_ref().to_vec());
|
||||
} else {
|
||||
let mut cache = ChunksCache(HashMap::new());
|
||||
cache.0.insert(text_source, binary_source.as_ref().to_vec());
|
||||
self.lua.set_app_data(cache);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
fn to_expression(&self) -> Result<Function<'lua>> {
|
||||
// We assume that mode is Text
|
||||
let source = self.source.as_ref();
|
||||
@@ -429,14 +478,13 @@ impl<'lua, 'a> Chunk<'lua, 'a> {
|
||||
fn detect_mode(&self) -> ChunkMode {
|
||||
match (self.mode, &self.source) {
|
||||
(Some(mode), _) => mode,
|
||||
(None, Ok(source)) if source.len() == 0 => ChunkMode::Text,
|
||||
(None, Ok(source)) => {
|
||||
#[cfg(not(feature = "luau"))]
|
||||
if source.starts_with(ffi::LUA_SIGNATURE) {
|
||||
return ChunkMode::Binary;
|
||||
}
|
||||
#[cfg(feature = "luau")]
|
||||
if source[0] < b'\n' {
|
||||
if *source.first().unwrap_or(&u8::MAX) < b'\n' {
|
||||
return ChunkMode::Binary;
|
||||
}
|
||||
ChunkMode::Text
|
||||
|
||||
+47
-14
@@ -250,18 +250,21 @@ impl<'lua> ToLua<'lua> for &str {
|
||||
}
|
||||
|
||||
impl<'lua> ToLua<'lua> for Cow<'_, str> {
|
||||
#[inline]
|
||||
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> {
|
||||
#[inline]
|
||||
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
|
||||
Ok(Value::String(lua.create_string(&*self)?))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> FromLua<'lua> for Box<str> {
|
||||
#[inline]
|
||||
fn from_lua(value: Value<'lua>, lua: &'lua Lua) -> Result<Self> {
|
||||
let ty = value.type_name();
|
||||
Ok(lua
|
||||
@@ -278,12 +281,14 @@ impl<'lua> FromLua<'lua> for Box<str> {
|
||||
}
|
||||
|
||||
impl<'lua> ToLua<'lua> for CString {
|
||||
#[inline]
|
||||
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
|
||||
Ok(Value::String(lua.create_string(self.as_bytes())?))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> FromLua<'lua> for CString {
|
||||
#[inline]
|
||||
fn from_lua(value: Value<'lua>, lua: &'lua Lua) -> Result<Self> {
|
||||
let ty = value.type_name();
|
||||
let string = lua
|
||||
@@ -306,24 +311,28 @@ impl<'lua> FromLua<'lua> for CString {
|
||||
}
|
||||
|
||||
impl<'lua> ToLua<'lua> for &CStr {
|
||||
#[inline]
|
||||
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
|
||||
Ok(Value::String(lua.create_string(self.to_bytes())?))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> ToLua<'lua> for Cow<'_, CStr> {
|
||||
#[inline]
|
||||
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 {
|
||||
#[inline]
|
||||
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
|
||||
Ok(Value::String(lua.create_string(&self)?))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> FromLua<'lua> for BString {
|
||||
#[inline]
|
||||
fn from_lua(value: Value<'lua>, lua: &'lua Lua) -> Result<Self> {
|
||||
let ty = value.type_name();
|
||||
Ok(BString::from(
|
||||
@@ -340,14 +349,16 @@ impl<'lua> FromLua<'lua> for BString {
|
||||
}
|
||||
|
||||
impl<'lua> ToLua<'lua> for &BStr {
|
||||
#[inline]
|
||||
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
|
||||
Ok(Value::String(lua.create_string(&self)?))
|
||||
Ok(Value::String(lua.create_string(self)?))
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! lua_convert_int {
|
||||
($x:ty) => {
|
||||
impl<'lua> ToLua<'lua> for $x {
|
||||
#[inline]
|
||||
fn to_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
|
||||
cast(self)
|
||||
.map(Value::Integer)
|
||||
@@ -362,22 +373,27 @@ macro_rules! lua_convert_int {
|
||||
}
|
||||
|
||||
impl<'lua> FromLua<'lua> for $x {
|
||||
#[inline]
|
||||
fn from_lua(value: Value<'lua>, lua: &'lua Lua) -> Result<Self> {
|
||||
let ty = value.type_name();
|
||||
(if let Value::Integer(i) = value {
|
||||
cast(i)
|
||||
} else if let Some(i) = lua.coerce_integer(value.clone())? {
|
||||
cast(i)
|
||||
} else {
|
||||
cast(lua.coerce_number(value)?.ok_or_else(|| {
|
||||
Error::FromLuaConversionError {
|
||||
from: ty,
|
||||
to: stringify!($x),
|
||||
message: Some(
|
||||
"expected number or string coercible to number".to_string(),
|
||||
),
|
||||
(match value {
|
||||
Value::Integer(i) => cast(i),
|
||||
Value::Number(n) => cast(n),
|
||||
_ => {
|
||||
if let Some(i) = lua.coerce_integer(value.clone())? {
|
||||
cast(i)
|
||||
} else {
|
||||
cast(lua.coerce_number(value)?.ok_or_else(|| {
|
||||
Error::FromLuaConversionError {
|
||||
from: ty,
|
||||
to: stringify!($x),
|
||||
message: Some(
|
||||
"expected number or string coercible to number".to_string(),
|
||||
),
|
||||
}
|
||||
})?)
|
||||
}
|
||||
})?)
|
||||
}
|
||||
})
|
||||
.ok_or_else(|| Error::FromLuaConversionError {
|
||||
from: ty,
|
||||
@@ -405,6 +421,7 @@ lua_convert_int!(usize);
|
||||
macro_rules! lua_convert_float {
|
||||
($x:ty) => {
|
||||
impl<'lua> ToLua<'lua> for $x {
|
||||
#[inline]
|
||||
fn to_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
|
||||
cast(self)
|
||||
.ok_or_else(|| Error::ToLuaConversionError {
|
||||
@@ -417,6 +434,7 @@ macro_rules! lua_convert_float {
|
||||
}
|
||||
|
||||
impl<'lua> FromLua<'lua> for $x {
|
||||
#[inline]
|
||||
fn from_lua(value: Value<'lua>, lua: &'lua Lua) -> Result<Self> {
|
||||
let ty = value.type_name();
|
||||
lua.coerce_number(value)?
|
||||
@@ -444,6 +462,7 @@ impl<'lua, T> ToLua<'lua> for &[T]
|
||||
where
|
||||
T: Clone + ToLua<'lua>,
|
||||
{
|
||||
#[inline]
|
||||
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
|
||||
Ok(Value::Table(
|
||||
lua.create_sequence_from(self.iter().cloned())?,
|
||||
@@ -455,6 +474,7 @@ impl<'lua, T, const N: usize> ToLua<'lua> for [T; N]
|
||||
where
|
||||
T: ToLua<'lua>,
|
||||
{
|
||||
#[inline]
|
||||
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
|
||||
Ok(Value::Table(lua.create_sequence_from(self)?))
|
||||
}
|
||||
@@ -464,6 +484,7 @@ impl<'lua, T, const N: usize> FromLua<'lua> for [T; N]
|
||||
where
|
||||
T: FromLua<'lua>,
|
||||
{
|
||||
#[inline]
|
||||
fn from_lua(value: Value<'lua>, _lua: &'lua Lua) -> Result<Self> {
|
||||
match value {
|
||||
#[cfg(feature = "luau")]
|
||||
@@ -496,24 +517,28 @@ where
|
||||
}
|
||||
|
||||
impl<'lua, T: ToLua<'lua>> ToLua<'lua> for Box<[T]> {
|
||||
#[inline]
|
||||
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]> {
|
||||
#[inline]
|
||||
fn from_lua(value: Value<'lua>, lua: &'lua Lua) -> Result<Self> {
|
||||
Ok(Vec::<T>::from_lua(value, lua)?.into_boxed_slice())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua, T: ToLua<'lua>> ToLua<'lua> for Vec<T> {
|
||||
#[inline]
|
||||
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
|
||||
Ok(Value::Table(lua.create_sequence_from(self)?))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua, T: FromLua<'lua>> FromLua<'lua> for Vec<T> {
|
||||
#[inline]
|
||||
fn from_lua(value: Value<'lua>, _lua: &'lua Lua) -> Result<Self> {
|
||||
match value {
|
||||
#[cfg(feature = "luau")]
|
||||
@@ -535,6 +560,7 @@ impl<'lua, T: FromLua<'lua>> FromLua<'lua> for Vec<T> {
|
||||
impl<'lua, K: Eq + Hash + ToLua<'lua>, V: ToLua<'lua>, S: BuildHasher> ToLua<'lua>
|
||||
for HashMap<K, V, S>
|
||||
{
|
||||
#[inline]
|
||||
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
|
||||
Ok(Value::Table(lua.create_table_from(self)?))
|
||||
}
|
||||
@@ -543,6 +569,7 @@ impl<'lua, K: Eq + Hash + ToLua<'lua>, V: ToLua<'lua>, S: BuildHasher> ToLua<'lu
|
||||
impl<'lua, K: Eq + Hash + FromLua<'lua>, V: FromLua<'lua>, S: BuildHasher + Default> FromLua<'lua>
|
||||
for HashMap<K, V, S>
|
||||
{
|
||||
#[inline]
|
||||
fn from_lua(value: Value<'lua>, _: &'lua Lua) -> Result<Self> {
|
||||
if let Value::Table(table) = value {
|
||||
table.pairs().collect()
|
||||
@@ -557,12 +584,14 @@ impl<'lua, K: Eq + Hash + FromLua<'lua>, V: FromLua<'lua>, S: BuildHasher + Defa
|
||||
}
|
||||
|
||||
impl<'lua, K: Ord + ToLua<'lua>, V: ToLua<'lua>> ToLua<'lua> for BTreeMap<K, V> {
|
||||
#[inline]
|
||||
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
|
||||
Ok(Value::Table(lua.create_table_from(self)?))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua, K: Ord + FromLua<'lua>, V: FromLua<'lua>> FromLua<'lua> for BTreeMap<K, V> {
|
||||
#[inline]
|
||||
fn from_lua(value: Value<'lua>, _: &'lua Lua) -> Result<Self> {
|
||||
if let Value::Table(table) = value {
|
||||
table.pairs().collect()
|
||||
@@ -577,6 +606,7 @@ impl<'lua, K: Ord + FromLua<'lua>, V: FromLua<'lua>> FromLua<'lua> for BTreeMap<
|
||||
}
|
||||
|
||||
impl<'lua, T: Eq + Hash + ToLua<'lua>, S: BuildHasher> ToLua<'lua> for HashSet<T, S> {
|
||||
#[inline]
|
||||
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
|
||||
Ok(Value::Table(lua.create_table_from(
|
||||
self.into_iter().map(|val| (val, true)),
|
||||
@@ -585,6 +615,7 @@ 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> {
|
||||
#[inline]
|
||||
fn from_lua(value: Value<'lua>, _: &'lua Lua) -> Result<Self> {
|
||||
match value {
|
||||
Value::Table(table) if table.len()? > 0 => table.sequence_values().collect(),
|
||||
@@ -602,6 +633,7 @@ impl<'lua, T: Eq + Hash + FromLua<'lua>, S: BuildHasher + Default> FromLua<'lua>
|
||||
}
|
||||
|
||||
impl<'lua, T: Ord + ToLua<'lua>> ToLua<'lua> for BTreeSet<T> {
|
||||
#[inline]
|
||||
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
|
||||
Ok(Value::Table(lua.create_table_from(
|
||||
self.into_iter().map(|val| (val, true)),
|
||||
@@ -610,6 +642,7 @@ impl<'lua, T: Ord + ToLua<'lua>> ToLua<'lua> for BTreeSet<T> {
|
||||
}
|
||||
|
||||
impl<'lua, T: Ord + FromLua<'lua>> FromLua<'lua> for BTreeSet<T> {
|
||||
#[inline]
|
||||
fn from_lua(value: Value<'lua>, _: &'lua Lua) -> Result<Self> {
|
||||
match value {
|
||||
Value::Table(table) if table.len()? > 0 => table.sequence_values().collect(),
|
||||
|
||||
@@ -568,11 +568,10 @@ pub unsafe fn luaL_requiref(
|
||||
lua_getfield(L, -1, modname);
|
||||
}
|
||||
}
|
||||
if cfg!(feature = "lua51") && glb != 0 {
|
||||
if glb != 0 {
|
||||
lua_pushvalue(L, -1);
|
||||
lua_setglobal(L, modname);
|
||||
}
|
||||
if cfg!(feature = "luajit") && glb == 0 {
|
||||
} else {
|
||||
lua_pushnil(L);
|
||||
lua_setglobal(L, modname);
|
||||
}
|
||||
|
||||
+10
-1
@@ -67,7 +67,10 @@ pub const LUA_MINSTACK: c_int = 20;
|
||||
pub type lua_Number = c_double;
|
||||
|
||||
/// A Lua integer, usually equivalent to `i64`
|
||||
pub type lua_Integer = isize;
|
||||
#[cfg(target_pointer_width = "32")]
|
||||
pub type lua_Integer = i32;
|
||||
#[cfg(target_pointer_width = "64")]
|
||||
pub type lua_Integer = i64;
|
||||
|
||||
/// 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;
|
||||
@@ -321,6 +324,12 @@ pub unsafe fn lua_tostring(L: *mut lua_State, i: c_int) -> *const c_char {
|
||||
lua_tolstring(L, i, ptr::null_mut())
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn lua_xpush(from: *mut lua_State, to: *mut lua_State, idx: c_int) {
|
||||
lua_pushvalue(from, idx);
|
||||
lua_xmove(from, to, 1);
|
||||
}
|
||||
|
||||
//
|
||||
// Debug API
|
||||
//
|
||||
|
||||
+10
-1
@@ -69,7 +69,10 @@ pub const LUA_RIDX_LAST: lua_Integer = LUA_RIDX_GLOBALS;
|
||||
pub type lua_Number = c_double;
|
||||
|
||||
/// A Lua integer, usually equivalent to `i64`
|
||||
pub type lua_Integer = isize;
|
||||
#[cfg(target_pointer_width = "32")]
|
||||
pub type lua_Integer = i32;
|
||||
#[cfg(target_pointer_width = "64")]
|
||||
pub type lua_Integer = i64;
|
||||
|
||||
/// A Lua unsigned integer, equivalent to `u32` in Lua 5.2
|
||||
pub type lua_Unsigned = c_uint;
|
||||
@@ -407,6 +410,12 @@ pub unsafe fn lua_tostring(L: *mut lua_State, i: c_int) -> *const c_char {
|
||||
lua_tolstring(L, i, ptr::null_mut())
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn lua_xpush(from: *mut lua_State, to: *mut lua_State, idx: c_int) {
|
||||
lua_pushvalue(from, idx);
|
||||
lua_xmove(from, to, 1);
|
||||
}
|
||||
|
||||
//
|
||||
// Debug API
|
||||
//
|
||||
|
||||
@@ -434,6 +434,12 @@ pub unsafe fn lua_replace(L: *mut lua_State, idx: c_int) {
|
||||
lua_pop(L, 1)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn lua_xpush(from: *mut lua_State, to: *mut lua_State, idx: c_int) {
|
||||
lua_pushvalue(from, idx);
|
||||
lua_xmove(from, to, 1);
|
||||
}
|
||||
|
||||
//
|
||||
// Debug API
|
||||
//
|
||||
|
||||
@@ -455,6 +455,12 @@ pub unsafe fn lua_replace(L: *mut lua_State, idx: c_int) {
|
||||
lua_pop(L, 1)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn lua_xpush(from: *mut lua_State, to: *mut lua_State, idx: c_int) {
|
||||
lua_pushvalue(from, idx);
|
||||
lua_xmove(from, to, 1);
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn lua_newuserdata(L: *mut lua_State, sz: usize) -> *mut c_void {
|
||||
lua_newuserdatauv(L, sz, 1)
|
||||
|
||||
@@ -341,7 +341,7 @@ pub unsafe fn luaL_loadbufferx(
|
||||
fn free(p: *mut c_void);
|
||||
}
|
||||
|
||||
let chunk_is_text = (*data as u8) >= b'\n';
|
||||
let chunk_is_text = size == 0 || (*data as u8) >= b'\n';
|
||||
if !mode.is_null() {
|
||||
let modeb = CStr::from_ptr(mode).to_bytes();
|
||||
if !chunk_is_text && !modeb.contains(&b'b') {
|
||||
@@ -423,7 +423,7 @@ pub unsafe fn luaL_traceback(
|
||||
level = numlevels - COMPAT53_LEVELS2; // and skip to last ones
|
||||
} else {
|
||||
lua_getinfo(L1, level, cstr!("sln"), &mut ar);
|
||||
lua_pushfstring(L, cstr!("\n\t%s:"), ar.short_src.as_ptr());
|
||||
lua_pushfstring(L, cstr!("\n\t%s:"), ar.short_src);
|
||||
if ar.currentline > 0 {
|
||||
lua_pushfstring(L, cstr!("%d:"), ar.currentline);
|
||||
}
|
||||
@@ -513,6 +513,9 @@ pub unsafe fn luaL_requiref(
|
||||
if glb != 0 {
|
||||
lua_pushvalue(L, -1);
|
||||
lua_setglobal(L, modname);
|
||||
} else {
|
||||
lua_pushnil(L);
|
||||
lua_setglobal(L, modname);
|
||||
}
|
||||
lua_replace(L, -2);
|
||||
}
|
||||
|
||||
+23
-6
@@ -7,12 +7,15 @@ use std::ptr;
|
||||
// Option for multiple returns in 'lua_pcall' and 'lua_call'
|
||||
pub const LUA_MULTRET: c_int = -1;
|
||||
|
||||
// Max number of Lua stack slots
|
||||
const LUAI_MAXCSTACK: c_int = 100000;
|
||||
|
||||
//
|
||||
// Pseudo-indices
|
||||
//
|
||||
pub const LUA_REGISTRYINDEX: c_int = -10000;
|
||||
pub const LUA_ENVIRONINDEX: c_int = -10001;
|
||||
pub const LUA_GLOBALSINDEX: c_int = -10002;
|
||||
pub const LUA_REGISTRYINDEX: c_int = -LUAI_MAXCSTACK - 2000;
|
||||
pub const LUA_ENVIRONINDEX: c_int = -LUAI_MAXCSTACK - 2001;
|
||||
pub const LUA_GLOBALSINDEX: c_int = -LUAI_MAXCSTACK - 2002;
|
||||
|
||||
pub const fn lua_upvalueindex(i: c_int) -> c_int {
|
||||
LUA_GLOBALSINDEX - i
|
||||
@@ -133,6 +136,7 @@ extern "C" {
|
||||
pub fn lua_namecallatom(L: *mut lua_State, atom: *mut c_int) -> *const c_char;
|
||||
pub fn lua_objlen(L: *mut lua_State, idx: c_int) -> usize;
|
||||
pub fn lua_tocfunction(L: *mut lua_State, idx: c_int) -> Option<lua_CFunction>;
|
||||
pub fn lua_tolightuserdata(L: *mut lua_State, idx: c_int) -> *mut c_void;
|
||||
pub fn lua_touserdata(L: *mut lua_State, idx: c_int) -> *mut c_void;
|
||||
pub fn lua_touserdatatagged(L: *mut lua_State, idx: c_int, tag: c_int) -> *mut c_void;
|
||||
pub fn lua_userdatatag(L: *mut lua_State, idx: c_int) -> c_int;
|
||||
@@ -256,11 +260,18 @@ extern "C" {
|
||||
extern "C" {
|
||||
pub fn lua_error(L: *mut lua_State) -> !;
|
||||
pub fn lua_next(L: *mut lua_State, idx: c_int) -> c_int;
|
||||
pub fn lua_rawiter(L: *mut lua_State, idx: c_int, iter: c_int) -> c_int;
|
||||
pub fn lua_concat(L: *mut lua_State, n: c_int);
|
||||
// TODO: lua_encodepointer
|
||||
pub fn lua_clock() -> c_double;
|
||||
pub fn lua_setuserdatadtor(L: *mut lua_State, tag: c_int, dtor: Option<lua_Udestructor>);
|
||||
pub fn lua_setuserdatatag(L: *mut lua_State, idx: c_int, tag: c_int);
|
||||
pub fn lua_setuserdatadtor(
|
||||
L: *mut lua_State,
|
||||
tag: c_int,
|
||||
dtor: Option<unsafe extern "C" fn(*mut lua_State, *mut c_void)>,
|
||||
);
|
||||
pub fn lua_clonefunction(L: *mut lua_State, idx: c_int);
|
||||
pub fn lua_cleartable(L: *mut lua_State, idx: c_int);
|
||||
}
|
||||
|
||||
//
|
||||
@@ -432,7 +443,12 @@ extern "C" {
|
||||
pub fn lua_setupvalue(L: *mut lua_State, funcindex: c_int, n: c_int) -> *const c_char;
|
||||
|
||||
pub fn lua_singlestep(L: *mut lua_State, enabled: c_int);
|
||||
pub fn lua_breakpoint(L: *mut lua_State, funcindex: c_int, line: c_int, enabled: c_int);
|
||||
pub fn lua_breakpoint(
|
||||
L: *mut lua_State,
|
||||
funcindex: c_int,
|
||||
line: c_int,
|
||||
enabled: c_int,
|
||||
) -> c_int;
|
||||
|
||||
pub fn lua_getcoverage(
|
||||
L: *mut lua_State,
|
||||
@@ -449,13 +465,14 @@ pub struct lua_Debug {
|
||||
pub name: *const c_char,
|
||||
pub what: *const c_char,
|
||||
pub source: *const c_char,
|
||||
pub short_src: *const c_char,
|
||||
pub linedefined: c_int,
|
||||
pub currentline: c_int,
|
||||
pub nupvals: u8,
|
||||
pub nparams: u8,
|
||||
pub isvararg: c_char,
|
||||
pub short_src: [c_char; LUA_IDSIZE],
|
||||
pub userdata: *mut c_void,
|
||||
pub ssbuf: [c_char; LUA_IDSIZE],
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
+3
-1
@@ -79,9 +79,11 @@ pub(crate) fn keep_lua_symbols() {
|
||||
symbols.push(lua_tocfunction as _);
|
||||
symbols.push(luaL_loadstring as _);
|
||||
symbols.push(luaL_openlibs as _);
|
||||
if cfg!(any(feature = "lua54", feature = "lua53", feature = "lua52")) {
|
||||
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
|
||||
{
|
||||
symbols.push(lua_getglobal as _);
|
||||
symbols.push(lua_setglobal as _);
|
||||
symbols.push(luaL_setfuncs as _);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+104
-31
@@ -1,6 +1,7 @@
|
||||
use std::mem;
|
||||
use std::os::raw::c_int;
|
||||
use std::os::raw::{c_int, c_void};
|
||||
use std::ptr;
|
||||
use std::slice;
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
use crate::ffi;
|
||||
@@ -29,6 +30,17 @@ pub struct FunctionInfo {
|
||||
pub last_line_defined: i32,
|
||||
}
|
||||
|
||||
/// Luau function coverage snapshot.
|
||||
#[cfg(any(feature = "luau", doc))]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct CoverageInfo {
|
||||
pub function: Option<std::string::String>,
|
||||
pub line_defined: i32,
|
||||
pub depth: i32,
|
||||
pub hits: Vec<i32>,
|
||||
}
|
||||
|
||||
impl<'lua> Function<'lua> {
|
||||
/// Calls the function, passing `args` as function arguments.
|
||||
///
|
||||
@@ -102,7 +114,7 @@ impl<'lua> Function<'lua> {
|
||||
R::from_lua_multi(results, lua)
|
||||
}
|
||||
|
||||
/// Returns a Feature that, when polled, calls `self`, passing `args` as function arguments,
|
||||
/// Returns a future that, when polled, calls `self`, passing `args` as function arguments,
|
||||
/// and drives the execution.
|
||||
///
|
||||
/// Internally it wraps the function to an [`AsyncThread`].
|
||||
@@ -178,24 +190,19 @@ impl<'lua> Function<'lua> {
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn bind<A: ToLuaMulti<'lua>>(&self, args: A) -> Result<Function<'lua>> {
|
||||
unsafe extern "C" fn bind_call_impl(state: *mut ffi::lua_State) -> c_int {
|
||||
unsafe extern "C" fn args_wrapper_impl(state: *mut ffi::lua_State) -> c_int {
|
||||
let nargs = ffi::lua_gettop(state);
|
||||
let nbinds = ffi::lua_tointeger(state, ffi::lua_upvalueindex(2)) as c_int;
|
||||
ffi::luaL_checkstack(state, nbinds + 2, ptr::null());
|
||||
|
||||
ffi::lua_settop(state, nargs + nbinds + 1);
|
||||
ffi::lua_rotate(state, -(nargs + nbinds + 1), nbinds + 1);
|
||||
|
||||
ffi::lua_pushvalue(state, ffi::lua_upvalueindex(1));
|
||||
ffi::lua_replace(state, 1);
|
||||
let nbinds = ffi::lua_tointeger(state, ffi::lua_upvalueindex(1)) as c_int;
|
||||
ffi::luaL_checkstack(state, nbinds, ptr::null());
|
||||
|
||||
for i in 0..nbinds {
|
||||
ffi::lua_pushvalue(state, ffi::lua_upvalueindex(i + 3));
|
||||
ffi::lua_replace(state, i + 2);
|
||||
ffi::lua_pushvalue(state, ffi::lua_upvalueindex(i + 2));
|
||||
}
|
||||
if nargs > 0 {
|
||||
ffi::lua_rotate(state, 1, nbinds);
|
||||
}
|
||||
|
||||
ffi::lua_call(state, nargs + nbinds, ffi::LUA_MULTRET);
|
||||
ffi::lua_gettop(state)
|
||||
nargs + nbinds
|
||||
}
|
||||
|
||||
let lua = self.0.lua;
|
||||
@@ -203,25 +210,40 @@ impl<'lua> Function<'lua> {
|
||||
let args = args.to_lua_multi(lua)?;
|
||||
let nargs = args.len() as c_int;
|
||||
|
||||
if nargs + 2 > ffi::LUA_MAX_UPVALUES {
|
||||
if nargs == 0 {
|
||||
return Ok(self.clone());
|
||||
}
|
||||
|
||||
if nargs + 1 > ffi::LUA_MAX_UPVALUES {
|
||||
return Err(Error::BindError);
|
||||
}
|
||||
|
||||
unsafe {
|
||||
let args_wrapper = unsafe {
|
||||
let _sg = StackGuard::new(lua.state);
|
||||
check_stack(lua.state, nargs + 5)?;
|
||||
check_stack(lua.state, nargs + 3)?;
|
||||
|
||||
lua.push_ref(&self.0);
|
||||
ffi::lua_pushinteger(lua.state, nargs as ffi::lua_Integer);
|
||||
for arg in args {
|
||||
lua.push_value(arg)?;
|
||||
}
|
||||
protect_lua!(lua.state, nargs + 2, 1, fn(state) {
|
||||
ffi::lua_pushcclosure(state, bind_call_impl, ffi::lua_gettop(state));
|
||||
protect_lua!(lua.state, nargs + 1, 1, fn(state) {
|
||||
ffi::lua_pushcclosure(state, args_wrapper_impl, ffi::lua_gettop(state));
|
||||
})?;
|
||||
|
||||
Ok(Function(lua.pop_ref()))
|
||||
}
|
||||
Function(lua.pop_ref())
|
||||
};
|
||||
|
||||
lua.load(
|
||||
r#"
|
||||
local func, args_wrapper = ...
|
||||
return function(...)
|
||||
return func(args_wrapper(...))
|
||||
end
|
||||
"#,
|
||||
)
|
||||
.try_cache()
|
||||
.set_name("_mlua_bind")?
|
||||
.call((self.clone(), args_wrapper))
|
||||
}
|
||||
|
||||
/// Returns information about the function.
|
||||
@@ -251,10 +273,13 @@ impl<'lua> Function<'lua> {
|
||||
name_what: None,
|
||||
what: ptr_to_cstr_bytes(ar.what).map(|s| s.to_vec()),
|
||||
source: ptr_to_cstr_bytes(ar.source).map(|s| s.to_vec()),
|
||||
short_src: ptr_to_cstr_bytes(&ar.short_src as *const _).map(|s| s.to_vec()),
|
||||
line_defined: ar.linedefined as i32,
|
||||
#[cfg(not(feature = "luau"))]
|
||||
last_line_defined: ar.lastlinedefined as i32,
|
||||
short_src: ptr_to_cstr_bytes(ar.short_src.as_ptr()).map(|s| s.to_vec()),
|
||||
#[cfg(feature = "luau")]
|
||||
short_src: ptr_to_cstr_bytes(ar.short_src).map(|s| s.to_vec()),
|
||||
line_defined: ar.linedefined,
|
||||
#[cfg(not(feature = "luau"))]
|
||||
last_line_defined: ar.lastlinedefined,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -270,9 +295,6 @@ impl<'lua> Function<'lua> {
|
||||
#[cfg(not(feature = "luau"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(not(feature = "luau"))))]
|
||||
pub fn dump(&self, strip: bool) -> Vec<u8> {
|
||||
use std::os::raw::c_void;
|
||||
use std::slice;
|
||||
|
||||
unsafe extern "C" fn writer(
|
||||
_state: *mut ffi::lua_State,
|
||||
buf: *const c_void,
|
||||
@@ -293,13 +315,64 @@ impl<'lua> Function<'lua> {
|
||||
|
||||
lua.push_ref(&self.0);
|
||||
let data_ptr = &mut data as *mut Vec<u8> as *mut c_void;
|
||||
let strip = if strip { 1 } else { 0 };
|
||||
ffi::lua_dump(lua.state, writer, data_ptr, strip);
|
||||
ffi::lua_dump(lua.state, writer, data_ptr, strip as i32);
|
||||
ffi::lua_pop(lua.state, 1);
|
||||
}
|
||||
|
||||
data
|
||||
}
|
||||
|
||||
/// Retrieves recorded coverage information about this Lua function including inner calls.
|
||||
///
|
||||
/// This function takes a callback as an argument and calls it providing [`CoverageInfo`] snapshot
|
||||
/// per each executed inner function.
|
||||
///
|
||||
/// Recording of coverage information is controlled by [`Compiler::set_coverage_level`] option.
|
||||
///
|
||||
/// Requires `feature = "luau"`
|
||||
///
|
||||
/// [`Compiler::set_coverage_level`]: crate::chunk::Compiler::set_coverage_level
|
||||
#[cfg(any(feature = "luau", docsrs))]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
|
||||
pub fn coverage<F>(&self, mut func: F)
|
||||
where
|
||||
F: FnMut(CoverageInfo),
|
||||
{
|
||||
use std::ffi::CStr;
|
||||
use std::os::raw::c_char;
|
||||
|
||||
unsafe extern "C" fn callback<F: FnMut(CoverageInfo)>(
|
||||
data: *mut c_void,
|
||||
function: *const c_char,
|
||||
line_defined: c_int,
|
||||
depth: c_int,
|
||||
hits: *const c_int,
|
||||
size: usize,
|
||||
) {
|
||||
let function = if !function.is_null() {
|
||||
Some(CStr::from_ptr(function).to_string_lossy().to_string())
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let rust_callback = &mut *(data as *mut F);
|
||||
rust_callback(CoverageInfo {
|
||||
function,
|
||||
line_defined,
|
||||
depth,
|
||||
hits: slice::from_raw_parts(hits, size).to_vec(),
|
||||
});
|
||||
}
|
||||
|
||||
let lua = self.0.lua;
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(lua.state);
|
||||
assert_stack(lua.state, 1);
|
||||
|
||||
lua.push_ref(&self.0);
|
||||
let func_ptr = &mut func as *mut F as *mut c_void;
|
||||
ffi::lua_getcoverage(lua.state, -1, func_ptr, callback::<F>);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> PartialEq for Function<'lua> {
|
||||
|
||||
+9
-6
@@ -63,7 +63,7 @@ impl<'lua> Debug<'lua> {
|
||||
}
|
||||
|
||||
/// Corresponds to the `n` what mask.
|
||||
pub fn names(&self) -> DebugNames<'lua> {
|
||||
pub fn names(&self) -> DebugNames {
|
||||
unsafe {
|
||||
#[cfg(not(feature = "luau"))]
|
||||
mlua_assert!(
|
||||
@@ -87,7 +87,7 @@ impl<'lua> Debug<'lua> {
|
||||
}
|
||||
|
||||
/// Corresponds to the `S` what mask.
|
||||
pub fn source(&self) -> DebugSource<'lua> {
|
||||
pub fn source(&self) -> DebugSource {
|
||||
unsafe {
|
||||
#[cfg(not(feature = "luau"))]
|
||||
mlua_assert!(
|
||||
@@ -102,10 +102,13 @@ impl<'lua> Debug<'lua> {
|
||||
|
||||
DebugSource {
|
||||
source: ptr_to_cstr_bytes((*self.ar.get()).source),
|
||||
short_src: ptr_to_cstr_bytes((*self.ar.get()).short_src.as_ptr()),
|
||||
line_defined: (*self.ar.get()).linedefined as i32,
|
||||
#[cfg(not(feature = "luau"))]
|
||||
last_line_defined: (*self.ar.get()).lastlinedefined as i32,
|
||||
short_src: ptr_to_cstr_bytes((*self.ar.get()).short_src.as_ptr()),
|
||||
#[cfg(feature = "luau")]
|
||||
short_src: ptr_to_cstr_bytes((*self.ar.get()).short_src),
|
||||
line_defined: (*self.ar.get()).linedefined,
|
||||
#[cfg(not(feature = "luau"))]
|
||||
last_line_defined: (*self.ar.get()).lastlinedefined,
|
||||
what: ptr_to_cstr_bytes((*self.ar.get()).what),
|
||||
}
|
||||
}
|
||||
@@ -125,7 +128,7 @@ impl<'lua> Debug<'lua> {
|
||||
"lua_getinfo failed with `l`"
|
||||
);
|
||||
|
||||
(*self.ar.get()).currentline as i32
|
||||
(*self.ar.get()).currentline
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -72,7 +72,7 @@
|
||||
//! [`serde::Deserialize`]: https://docs.serde.rs/serde/de/trait.Deserialize.html
|
||||
|
||||
// mlua types in rustdoc of other crates get linked to here.
|
||||
#![doc(html_root_url = "https://docs.rs/mlua/0.8.0-beta.4")]
|
||||
#![doc(html_root_url = "https://docs.rs/mlua/0.8.8")]
|
||||
// Deny warnings inside doc tests / examples. When this isn't present, rustdoc doesn't show *any*
|
||||
// warnings at all.
|
||||
#![doc(test(attr(deny(warnings))))]
|
||||
@@ -128,7 +128,7 @@ pub use crate::hook::HookTriggers;
|
||||
|
||||
#[cfg(any(feature = "luau", doc))]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
|
||||
pub use crate::{chunk::Compiler, types::VmState};
|
||||
pub use crate::{chunk::Compiler, function::CoverageInfo, types::VmState};
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
pub use crate::thread::AsyncThread;
|
||||
|
||||
+328
-165
@@ -8,6 +8,7 @@ use std::mem::ManuallyDrop;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use std::os::raw::{c_char, c_int, c_void};
|
||||
use std::panic::{catch_unwind, resume_unwind, AssertUnwindSafe, Location};
|
||||
use std::ptr::NonNull;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::{mem, ptr, str};
|
||||
|
||||
@@ -24,11 +25,11 @@ use crate::string::String;
|
||||
use crate::table::Table;
|
||||
use crate::thread::Thread;
|
||||
use crate::types::{
|
||||
Callback, CallbackUpvalue, DestructedUserdataMT, Integer, LightUserData, LuaRef, MaybeSend,
|
||||
Callback, CallbackUpvalue, DestructedUserdata, Integer, LightUserData, LuaRef, MaybeSend,
|
||||
Number, RegistryKey,
|
||||
};
|
||||
use crate::userdata::{AnyUserData, UserData, UserDataCell};
|
||||
use crate::userdata_impl::{StaticUserDataFields, StaticUserDataMethods};
|
||||
use crate::userdata_impl::{StaticUserDataFields, StaticUserDataMethods, UserDataProxy};
|
||||
use crate::util::{
|
||||
self, assert_stack, callback_error, check_stack, get_destructed_userdata_metatable,
|
||||
get_gc_metatable, get_gc_userdata, get_main_state, get_userdata, init_error_registry,
|
||||
@@ -87,6 +88,8 @@ pub(crate) struct ExtraData {
|
||||
|
||||
registered_userdata: FxHashMap<TypeId, c_int>,
|
||||
registered_userdata_mt: FxHashMap<*const c_void, Option<TypeId>>,
|
||||
|
||||
// When Lua instance dropped, setting `None` would prevent collecting `RegistryKey`s
|
||||
registry_unref_list: Arc<Mutex<Option<Vec<c_int>>>>,
|
||||
|
||||
#[cfg(not(feature = "send"))]
|
||||
@@ -95,7 +98,7 @@ pub(crate) struct ExtraData {
|
||||
app_data: RefCell<HashMap<TypeId, Box<dyn Any + Send>>>,
|
||||
|
||||
libs: StdLib,
|
||||
mem_info: Option<ptr::NonNull<MemoryInfo>>,
|
||||
mem_info: Option<NonNull<MemoryInfo>>,
|
||||
|
||||
ref_thread: *mut ffi::lua_State,
|
||||
ref_stack_size: c_int,
|
||||
@@ -110,6 +113,9 @@ pub(crate) struct ExtraData {
|
||||
#[cfg(feature = "async")]
|
||||
recycled_thread_cache: Vec<c_int>,
|
||||
|
||||
// Address of `WrappedFailure` metatable
|
||||
wrapped_failure_mt_ptr: *const c_void,
|
||||
|
||||
// Index of `Option<Waker>` userdata on the ref thread
|
||||
#[cfg(feature = "async")]
|
||||
ref_waker_idx: c_int,
|
||||
@@ -125,7 +131,7 @@ pub(crate) struct ExtraData {
|
||||
sandboxed: bool,
|
||||
}
|
||||
|
||||
#[cfg_attr(any(feature = "lua51", feature = "luajit"), allow(dead_code))]
|
||||
#[derive(Default)]
|
||||
struct MemoryInfo {
|
||||
used_memory: isize,
|
||||
memory_limit: isize,
|
||||
@@ -226,7 +232,7 @@ const MULTIVALUE_CACHE_SIZE: usize = 32;
|
||||
/// Requires `feature = "send"`
|
||||
#[cfg(feature = "send")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "send")))]
|
||||
unsafe impl Send for LuaInner {}
|
||||
unsafe impl Send for Lua {}
|
||||
|
||||
#[cfg(not(feature = "module"))]
|
||||
impl Drop for LuaInner {
|
||||
@@ -378,31 +384,31 @@ impl Lua {
|
||||
}
|
||||
|
||||
unsafe fn inner_new(libs: StdLib, options: LuaOptions) -> Lua {
|
||||
#[cfg_attr(
|
||||
any(feature = "lua51", feature = "luajit", feature = "luau"),
|
||||
allow(dead_code)
|
||||
)]
|
||||
unsafe extern "C" fn allocator(
|
||||
extra_data: *mut c_void,
|
||||
ptr: *mut c_void,
|
||||
osize: usize,
|
||||
nsize: usize,
|
||||
) -> *mut c_void {
|
||||
use std::alloc;
|
||||
use std::alloc::{self, Layout};
|
||||
|
||||
let mem_info = &mut *(extra_data as *mut MemoryInfo);
|
||||
|
||||
if nsize == 0 {
|
||||
// Free memory
|
||||
if !ptr.is_null() {
|
||||
let layout =
|
||||
alloc::Layout::from_size_align_unchecked(osize, ffi::SYS_MIN_ALIGN);
|
||||
let layout = Layout::from_size_align_unchecked(osize, ffi::SYS_MIN_ALIGN);
|
||||
alloc::dealloc(ptr as *mut u8, layout);
|
||||
mem_info.used_memory -= osize as isize;
|
||||
}
|
||||
return ptr::null_mut();
|
||||
}
|
||||
|
||||
// Do not allocate more than isize::MAX
|
||||
if nsize > isize::MAX as usize {
|
||||
return ptr::null_mut();
|
||||
}
|
||||
|
||||
// Are we fit to the memory limits?
|
||||
let mut mem_diff = nsize as isize;
|
||||
if !ptr.is_null() {
|
||||
@@ -412,53 +418,54 @@ impl Lua {
|
||||
if mem_info.memory_limit > 0 && new_used_memory > mem_info.memory_limit {
|
||||
return ptr::null_mut();
|
||||
}
|
||||
|
||||
let new_layout = alloc::Layout::from_size_align_unchecked(nsize, ffi::SYS_MIN_ALIGN);
|
||||
mem_info.used_memory += mem_diff;
|
||||
|
||||
if ptr.is_null() {
|
||||
// Allocate new memory
|
||||
let new_layout = match Layout::from_size_align(nsize, ffi::SYS_MIN_ALIGN) {
|
||||
Ok(layout) => layout,
|
||||
Err(_) => return ptr::null_mut(),
|
||||
};
|
||||
let new_ptr = alloc::alloc(new_layout) as *mut c_void;
|
||||
if !new_ptr.is_null() {
|
||||
mem_info.used_memory += mem_diff;
|
||||
if new_ptr.is_null() {
|
||||
alloc::handle_alloc_error(new_layout);
|
||||
}
|
||||
return new_ptr;
|
||||
}
|
||||
|
||||
// Reallocate memory
|
||||
let old_layout = alloc::Layout::from_size_align_unchecked(osize, ffi::SYS_MIN_ALIGN);
|
||||
let old_layout = Layout::from_size_align_unchecked(osize, ffi::SYS_MIN_ALIGN);
|
||||
let new_ptr = alloc::realloc(ptr as *mut u8, old_layout, nsize) as *mut c_void;
|
||||
|
||||
if !new_ptr.is_null() {
|
||||
mem_info.used_memory += mem_diff;
|
||||
} else if !ptr.is_null() && nsize < osize {
|
||||
// Should not happen
|
||||
alloc::handle_alloc_error(new_layout);
|
||||
if new_ptr.is_null() {
|
||||
alloc::handle_alloc_error(old_layout);
|
||||
}
|
||||
|
||||
new_ptr
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
|
||||
let mem_info = Box::into_raw(Box::new(MemoryInfo {
|
||||
used_memory: 0,
|
||||
memory_limit: 0,
|
||||
}));
|
||||
// Skip Rust allocator for non-vendored LuaJIT (see https://github.com/khvzak/mlua/issues/176)
|
||||
let use_rust_allocator = !(cfg!(feature = "luajit") && cfg!(not(feature = "vendored")));
|
||||
|
||||
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
|
||||
let state = ffi::lua_newstate(allocator, mem_info as *mut c_void);
|
||||
#[cfg(any(feature = "lua51", feature = "luajit", feature = "luau"))]
|
||||
let state = ffi::luaL_newstate();
|
||||
let (state, mem_info) = if use_rust_allocator {
|
||||
let mut mem_info = Box::into_raw(Box::new(MemoryInfo::default()));
|
||||
let mut state = ffi::lua_newstate(allocator, mem_info as *mut c_void);
|
||||
// If state is null (it's possible for LuaJIT on non-x86 arch) then switch to Lua internal allocator
|
||||
if state.is_null() {
|
||||
drop(Box::from_raw(mem_info));
|
||||
mem_info = ptr::null_mut();
|
||||
state = ffi::luaL_newstate();
|
||||
}
|
||||
(state, mem_info)
|
||||
} else {
|
||||
(ffi::luaL_newstate(), ptr::null_mut())
|
||||
};
|
||||
assert!(!state.is_null(), "Failed to instantiate Lua VM");
|
||||
|
||||
ffi::luaL_requiref(state, cstr!("_G"), ffi::luaopen_base, 1);
|
||||
ffi::lua_pop(state, 1);
|
||||
|
||||
let lua = Lua::init_from_ptr(state);
|
||||
let extra = &mut *lua.extra.get();
|
||||
|
||||
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
|
||||
{
|
||||
extra.mem_info = ptr::NonNull::new(mem_info);
|
||||
}
|
||||
extra.mem_info = NonNull::new(mem_info);
|
||||
|
||||
mlua_expect!(
|
||||
load_from_std_lib(state, libs),
|
||||
@@ -505,6 +512,7 @@ impl Lua {
|
||||
/// by calling this function again.
|
||||
#[allow(clippy::missing_safety_doc)]
|
||||
pub unsafe fn init_from_ptr(state: *mut ffi::lua_State) -> Lua {
|
||||
assert!(!state.is_null(), "Lua state is NULL");
|
||||
let main_state = get_main_state(state).unwrap_or(state);
|
||||
let main_state_top = ffi::lua_gettop(main_state);
|
||||
|
||||
@@ -550,11 +558,18 @@ impl Lua {
|
||||
"Error while creating ref thread",
|
||||
);
|
||||
|
||||
let wrapped_failure_mt_ptr = {
|
||||
get_gc_metatable::<WrappedFailure>(state);
|
||||
let ptr = ffi::lua_topointer(state, -1);
|
||||
ffi::lua_pop(state, 1);
|
||||
ptr
|
||||
};
|
||||
|
||||
// Create empty Waker slot on the ref thread
|
||||
#[cfg(feature = "async")]
|
||||
let ref_waker_idx = {
|
||||
mlua_expect!(
|
||||
push_gc_userdata::<Option<Waker>>(ref_thread, None),
|
||||
push_gc_userdata::<Option<Waker>>(ref_thread, None, true),
|
||||
"Error while creating Waker slot"
|
||||
);
|
||||
ffi::lua_gettop(ref_thread)
|
||||
@@ -580,6 +595,7 @@ impl Lua {
|
||||
multivalue_cache: Vec::with_capacity(MULTIVALUE_CACHE_SIZE),
|
||||
#[cfg(feature = "async")]
|
||||
recycled_thread_cache: Vec::new(),
|
||||
wrapped_failure_mt_ptr,
|
||||
#[cfg(feature = "async")]
|
||||
ref_waker_idx,
|
||||
#[cfg(not(feature = "luau"))]
|
||||
@@ -594,7 +610,7 @@ impl Lua {
|
||||
|
||||
mlua_expect!(
|
||||
(|state| {
|
||||
push_gc_userdata(state, Arc::clone(&extra))?;
|
||||
push_gc_userdata(state, Arc::clone(&extra), true)?;
|
||||
protect_lua!(state, 1, 0, fn(state) {
|
||||
let extra_key = &EXTRA_REGISTRY_KEY as *const u8 as *const c_void;
|
||||
ffi::lua_rawsetp(state, ffi::LUA_REGISTRYINDEX, extra_key);
|
||||
@@ -603,13 +619,13 @@ impl Lua {
|
||||
"Error while storing extra data",
|
||||
);
|
||||
|
||||
// Register `DestructedUserdataMT` type
|
||||
// Register `DestructedUserdata` type
|
||||
get_destructed_userdata_metatable(main_state);
|
||||
let destructed_mt_ptr = ffi::lua_topointer(main_state, -1);
|
||||
let destructed_mt_typeid = Some(TypeId::of::<DestructedUserdataMT>());
|
||||
let destructed_ud_typeid = TypeId::of::<DestructedUserdata>();
|
||||
(*extra.get())
|
||||
.registered_userdata_mt
|
||||
.insert(destructed_mt_ptr, destructed_mt_typeid);
|
||||
.insert(destructed_mt_ptr, Some(destructed_ud_typeid));
|
||||
ffi::lua_pop(main_state, 1);
|
||||
|
||||
mlua_debug_assert!(
|
||||
@@ -875,10 +891,8 @@ impl Lua {
|
||||
ffi::luaL_sandboxthread(state);
|
||||
} else {
|
||||
// Restore original `LUA_GLOBALSINDEX`
|
||||
self.ref_thread_exec(|ref_thread| {
|
||||
ffi::lua_xpush(ref_thread, state, ffi::LUA_GLOBALSINDEX);
|
||||
ffi::lua_replace(state, ffi::LUA_GLOBALSINDEX);
|
||||
});
|
||||
ffi::lua_xpush(self.ref_thread(), state, ffi::LUA_GLOBALSINDEX);
|
||||
ffi::lua_replace(state, ffi::LUA_GLOBALSINDEX);
|
||||
ffi::luaL_sandbox(state, 0);
|
||||
}
|
||||
})?;
|
||||
@@ -1117,7 +1131,7 @@ impl Lua {
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "lua54")))]
|
||||
pub fn warning<S: Into<Vec<u8>>>(&self, msg: S, tocont: bool) -> Result<()> {
|
||||
let msg = CString::new(msg).map_err(|err| Error::RuntimeError(err.to_string()))?;
|
||||
unsafe { ffi::lua_warning(self.state, msg.as_ptr(), if tocont { 1 } else { 0 }) };
|
||||
unsafe { ffi::lua_warning(self.state, msg.as_ptr(), tocont as c_int) };
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1359,9 +1373,8 @@ impl Lua {
|
||||
where
|
||||
S: AsChunk<'lua> + ?Sized,
|
||||
{
|
||||
let name = chunk
|
||||
.name()
|
||||
.unwrap_or_else(|| Location::caller().to_string());
|
||||
let caller = Location::caller();
|
||||
let name = chunk.name().unwrap_or_else(|| caller.to_string());
|
||||
|
||||
Chunk {
|
||||
lua: self,
|
||||
@@ -1421,21 +1434,22 @@ impl Lua {
|
||||
S: AsRef<[u8]> + ?Sized,
|
||||
{
|
||||
unsafe {
|
||||
if self.unlikely_memory_error() {
|
||||
push_string(self.ref_thread(), s.as_ref(), false)?;
|
||||
return Ok(String(self.pop_ref_thread()));
|
||||
}
|
||||
|
||||
let _sg = StackGuard::new(self.state);
|
||||
check_stack(self.state, 3)?;
|
||||
push_string(self.state, s)?;
|
||||
|
||||
push_string(self.state, s.as_ref(), true)?;
|
||||
Ok(String(self.pop_ref()))
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates and returns a new empty table.
|
||||
pub fn create_table(&self) -> Result<Table> {
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(self.state);
|
||||
check_stack(self.state, 2)?;
|
||||
protect_lua!(self.state, 0, 1, fn(state) ffi::lua_newtable(state))?;
|
||||
Ok(Table(self.pop_ref()))
|
||||
}
|
||||
self.create_table_with_capacity(0, 0)
|
||||
}
|
||||
|
||||
/// Creates and returns a new empty table, with the specified capacity.
|
||||
@@ -1444,9 +1458,15 @@ impl Lua {
|
||||
/// Lua may use these hints to preallocate memory for the new table.
|
||||
pub fn create_table_with_capacity(&self, narr: c_int, nrec: c_int) -> Result<Table> {
|
||||
unsafe {
|
||||
if self.unlikely_memory_error() {
|
||||
push_table(self.ref_thread(), narr, nrec, false)?;
|
||||
return Ok(Table(self.pop_ref_thread()));
|
||||
}
|
||||
|
||||
let _sg = StackGuard::new(self.state);
|
||||
check_stack(self.state, 3)?;
|
||||
push_table(self.state, narr, nrec)?;
|
||||
|
||||
push_table(self.state, narr, nrec, true)?;
|
||||
Ok(Table(self.pop_ref()))
|
||||
}
|
||||
}
|
||||
@@ -1464,11 +1484,16 @@ impl Lua {
|
||||
|
||||
let iter = iter.into_iter();
|
||||
let lower_bound = iter.size_hint().0;
|
||||
push_table(self.state, 0, lower_bound as c_int)?;
|
||||
let protect = !self.unlikely_memory_error();
|
||||
push_table(self.state, 0, lower_bound as c_int, protect)?;
|
||||
for (k, v) in iter {
|
||||
self.push_value(k.to_lua(self)?)?;
|
||||
self.push_value(v.to_lua(self)?)?;
|
||||
protect_lua!(self.state, 3, 1, fn(state) ffi::lua_rawset(state, -3))?;
|
||||
if protect {
|
||||
protect_lua!(self.state, 3, 1, fn(state) ffi::lua_rawset(state, -3))?;
|
||||
} else {
|
||||
ffi::lua_rawset(self.state, -3);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Table(self.pop_ref()))
|
||||
@@ -1487,12 +1512,17 @@ impl Lua {
|
||||
|
||||
let iter = iter.into_iter();
|
||||
let lower_bound = iter.size_hint().0;
|
||||
push_table(self.state, lower_bound as c_int, 0)?;
|
||||
let protect = !self.unlikely_memory_error();
|
||||
push_table(self.state, lower_bound as c_int, 0, protect)?;
|
||||
for (i, v) in iter.enumerate() {
|
||||
self.push_value(v.to_lua(self)?)?;
|
||||
protect_lua!(self.state, 2, 1, |state| {
|
||||
ffi::lua_rawseti(state, -2, (i + 1) as Integer);
|
||||
})?;
|
||||
if protect {
|
||||
protect_lua!(self.state, 2, 1, |state| {
|
||||
ffi::lua_rawseti(state, -2, (i + 1) as Integer);
|
||||
})?;
|
||||
} else {
|
||||
ffi::lua_rawseti(self.state, -2, (i + 1) as Integer);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Table(self.pop_ref()))
|
||||
@@ -1651,7 +1681,11 @@ impl Lua {
|
||||
let _sg = StackGuard::new(self.state);
|
||||
check_stack(self.state, 3)?;
|
||||
|
||||
let thread_state = protect_lua!(self.state, 0, 1, |state| ffi::lua_newthread(state))?;
|
||||
let thread_state = if self.unlikely_memory_error() {
|
||||
ffi::lua_newthread(self.state)
|
||||
} else {
|
||||
protect_lua!(self.state, 0, 1, |state| ffi::lua_newthread(state))?
|
||||
};
|
||||
self.push_ref(&func.0);
|
||||
ffi::lua_xmove(self.state, thread_state, 1);
|
||||
|
||||
@@ -1700,15 +1734,16 @@ impl Lua {
|
||||
all(feature = "luajit", feature = "vendored"),
|
||||
feature = "luau",
|
||||
))]
|
||||
pub(crate) unsafe fn recycle_thread(&self, thread: &mut Thread) {
|
||||
pub(crate) unsafe fn recycle_thread(&self, thread: &mut Thread) -> bool {
|
||||
let extra = &mut *self.extra.get();
|
||||
let thread_state = ffi::lua_tothread(extra.ref_thread, thread.0.index);
|
||||
if extra.recycled_thread_cache.len() < extra.recycled_thread_cache.capacity() {
|
||||
let thread_state = ffi::lua_tothread(extra.ref_thread, thread.0.index);
|
||||
#[cfg(feature = "lua54")]
|
||||
let status = ffi::lua_resetthread(thread_state);
|
||||
#[cfg(feature = "lua54")]
|
||||
if status != ffi::LUA_OK {
|
||||
return;
|
||||
// Error object is on top, drop it
|
||||
ffi::lua_settop(thread_state, 0);
|
||||
}
|
||||
#[cfg(all(feature = "luajit", feature = "vendored"))]
|
||||
ffi::lua_resetthread(self.state, thread_state);
|
||||
@@ -1716,10 +1751,15 @@ impl Lua {
|
||||
ffi::lua_resetthread(thread_state);
|
||||
extra.recycled_thread_cache.push(thread.0.index);
|
||||
thread.0.index = 0;
|
||||
return true;
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
/// Create a Lua userdata object from a custom userdata type.
|
||||
///
|
||||
/// All userdata instances of type `T` shares the same metatable.
|
||||
#[inline]
|
||||
pub fn create_userdata<T>(&self, data: T) -> Result<AnyUserData>
|
||||
where
|
||||
T: 'static + MaybeSend + UserData,
|
||||
@@ -1732,6 +1772,7 @@ impl Lua {
|
||||
/// Requires `feature = "serialize"`
|
||||
#[cfg(feature = "serialize")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "serialize")))]
|
||||
#[inline]
|
||||
pub fn create_ser_userdata<T>(&self, data: T) -> Result<AnyUserData>
|
||||
where
|
||||
T: 'static + MaybeSend + UserData + Serialize,
|
||||
@@ -1739,6 +1780,46 @@ impl Lua {
|
||||
unsafe { self.make_userdata(UserDataCell::new_ser(data)) }
|
||||
}
|
||||
|
||||
/// Create a Lua userdata "proxy" object from a custom userdata type.
|
||||
///
|
||||
/// Proxy object is an empty userdata object that has `T` metatable attached.
|
||||
/// The main purpose of this object is to provide access to static fields and functions
|
||||
/// without creating an instance of type `T`.
|
||||
///
|
||||
/// You can get or set uservalues on this object but you cannot borrow any Rust type.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use mlua::{Lua, Result, UserData, UserDataFields, UserDataMethods};
|
||||
/// # fn main() -> Result<()> {
|
||||
/// # let lua = Lua::new();
|
||||
/// struct MyUserData(i32);
|
||||
///
|
||||
/// impl UserData for MyUserData {
|
||||
/// fn add_fields<'lua, F: UserDataFields<'lua, Self>>(fields: &mut F) {
|
||||
/// fields.add_field_method_get("val", |_, this| Ok(this.0));
|
||||
/// }
|
||||
///
|
||||
/// fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
|
||||
/// methods.add_function("new", |_, value: i32| Ok(MyUserData(value)));
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// lua.globals().set("MyUserData", lua.create_proxy::<MyUserData>()?)?;
|
||||
///
|
||||
/// lua.load("assert(MyUserData.new(321).val == 321)").exec()?;
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn create_proxy<T>(&self) -> Result<AnyUserData>
|
||||
where
|
||||
T: 'static + UserData,
|
||||
{
|
||||
unsafe { self.make_userdata(UserDataCell::new(UserDataProxy::<T>(PhantomData))) }
|
||||
}
|
||||
|
||||
/// Returns a handle to the global environment.
|
||||
pub fn globals(&self) -> Table {
|
||||
unsafe {
|
||||
@@ -1826,9 +1907,13 @@ impl Lua {
|
||||
check_stack(self.state, 4)?;
|
||||
|
||||
self.push_value(v)?;
|
||||
let res = protect_lua!(self.state, 1, 1, |state| {
|
||||
ffi::lua_tolstring(state, -1, ptr::null_mut())
|
||||
})?;
|
||||
let res = if self.unlikely_memory_error() {
|
||||
ffi::lua_tolstring(self.state, -1, ptr::null_mut())
|
||||
} else {
|
||||
protect_lua!(self.state, 1, 1, |state| {
|
||||
ffi::lua_tolstring(state, -1, ptr::null_mut())
|
||||
})?
|
||||
};
|
||||
if !res.is_null() {
|
||||
Some(String(self.pop_ref()))
|
||||
} else {
|
||||
@@ -1944,7 +2029,8 @@ impl Lua {
|
||||
let _sg = StackGuard::new(self.state);
|
||||
check_stack(self.state, 3)?;
|
||||
|
||||
push_string(self.state, name)?;
|
||||
let protect = !self.unlikely_memory_error();
|
||||
push_string(self.state, name.as_ref(), protect)?;
|
||||
ffi::lua_rawget(self.state, ffi::LUA_REGISTRYINDEX);
|
||||
|
||||
self.pop_value()
|
||||
@@ -1976,34 +2062,38 @@ impl Lua {
|
||||
/// [`RegistryKey`]: crate::RegistryKey
|
||||
pub fn create_registry_value<'lua, T: ToLua<'lua>>(&'lua self, t: T) -> Result<RegistryKey> {
|
||||
let t = t.to_lua(self)?;
|
||||
if t == Value::Nil {
|
||||
// Special case to skip calling `luaL_ref` and use `LUA_REFNIL` instead
|
||||
let unref_list = unsafe { (*self.extra.get()).registry_unref_list.clone() };
|
||||
return Ok(RegistryKey::new(ffi::LUA_REFNIL, unref_list));
|
||||
}
|
||||
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(self.state);
|
||||
check_stack(self.state, 4)?;
|
||||
|
||||
let unref_list = (*self.extra.get()).registry_unref_list.clone();
|
||||
self.push_value(t)?;
|
||||
|
||||
// Try to reuse previously allocated RegistryKey
|
||||
let unref_list2 = unref_list.clone();
|
||||
let mut unref_list2 = mlua_expect!(unref_list2.lock(), "unref list poisoned");
|
||||
if let Some(registry_id) = unref_list2.as_mut().and_then(|x| x.pop()) {
|
||||
// Try to reuse previously allocated slot
|
||||
let unref_list = (*self.extra.get()).registry_unref_list.clone();
|
||||
let free_registry_id = mlua_expect!(unref_list.lock(), "unref list poisoned")
|
||||
.as_mut()
|
||||
.and_then(|x| x.pop());
|
||||
if let Some(registry_id) = free_registry_id {
|
||||
// It must be safe to replace the value without triggering memory error
|
||||
ffi::lua_rawseti(self.state, ffi::LUA_REGISTRYINDEX, registry_id as Integer);
|
||||
return Ok(RegistryKey {
|
||||
registry_id,
|
||||
unref_list,
|
||||
});
|
||||
return Ok(RegistryKey::new(registry_id, unref_list));
|
||||
}
|
||||
|
||||
// Allocate a new RegistryKey
|
||||
let registry_id = protect_lua!(self.state, 1, 0, |state| {
|
||||
ffi::luaL_ref(state, ffi::LUA_REGISTRYINDEX)
|
||||
})?;
|
||||
|
||||
Ok(RegistryKey {
|
||||
registry_id,
|
||||
unref_list,
|
||||
})
|
||||
let registry_id = if self.unlikely_memory_error() {
|
||||
ffi::luaL_ref(self.state, ffi::LUA_REGISTRYINDEX)
|
||||
} else {
|
||||
protect_lua!(self.state, 1, 0, |state| {
|
||||
ffi::luaL_ref(state, ffi::LUA_REGISTRYINDEX)
|
||||
})?
|
||||
};
|
||||
Ok(RegistryKey::new(registry_id, unref_list))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2018,16 +2108,16 @@ impl Lua {
|
||||
return Err(Error::MismatchedRegistryKey);
|
||||
}
|
||||
|
||||
let value = unsafe {
|
||||
let _sg = StackGuard::new(self.state);
|
||||
check_stack(self.state, 1)?;
|
||||
let value = match key.is_nil() {
|
||||
true => Value::Nil,
|
||||
false => unsafe {
|
||||
let _sg = StackGuard::new(self.state);
|
||||
check_stack(self.state, 1)?;
|
||||
|
||||
ffi::lua_rawgeti(
|
||||
self.state,
|
||||
ffi::LUA_REGISTRYINDEX,
|
||||
key.registry_id as Integer,
|
||||
);
|
||||
self.pop_value()
|
||||
let id = key.registry_id as Integer;
|
||||
ffi::lua_rawgeti(self.state, ffi::LUA_REGISTRYINDEX, id);
|
||||
self.pop_value()
|
||||
},
|
||||
};
|
||||
T::from_lua(value, self)
|
||||
}
|
||||
@@ -2066,20 +2156,31 @@ impl Lua {
|
||||
}
|
||||
|
||||
let t = t.to_lua(self)?;
|
||||
if t == Value::Nil && key.is_nil() {
|
||||
// Nothing to replace
|
||||
return Ok(());
|
||||
} else if t != Value::Nil && key.registry_id == ffi::LUA_REFNIL {
|
||||
// We cannot update `LUA_REFNIL` slot
|
||||
let err = "cannot replace nil value with non-nil".to_string();
|
||||
return Err(Error::RuntimeError(err));
|
||||
}
|
||||
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(self.state);
|
||||
check_stack(self.state, 2)?;
|
||||
|
||||
self.push_value(t)?;
|
||||
let id = key.registry_id as Integer;
|
||||
if t == Value::Nil {
|
||||
self.push_value(Value::Integer(id))?;
|
||||
key.set_nil(true);
|
||||
} else {
|
||||
self.push_value(t)?;
|
||||
key.set_nil(false);
|
||||
}
|
||||
// It must be safe to replace the value without triggering memory error
|
||||
ffi::lua_rawseti(
|
||||
self.state,
|
||||
ffi::LUA_REGISTRYINDEX,
|
||||
key.registry_id as Integer,
|
||||
);
|
||||
|
||||
Ok(())
|
||||
ffi::lua_rawseti(self.state, ffi::LUA_REGISTRYINDEX, id);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Returns true if the given `RegistryKey` was created by a `Lua` which shares the underlying
|
||||
@@ -2137,6 +2238,7 @@ impl Lua {
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
#[track_caller]
|
||||
pub fn set_app_data<T: 'static + MaybeSend>(&self, data: T) {
|
||||
let extra = unsafe { &mut (*self.extra.get()) };
|
||||
extra
|
||||
@@ -2147,6 +2249,7 @@ impl Lua {
|
||||
}
|
||||
|
||||
/// Gets a reference to an application data object stored by [`Lua::set_app_data()`] of type `T`.
|
||||
#[track_caller]
|
||||
pub fn app_data_ref<T: 'static>(&self) -> Option<Ref<T>> {
|
||||
let extra = unsafe { &(*self.extra.get()) };
|
||||
let app_data = extra
|
||||
@@ -2158,6 +2261,7 @@ impl Lua {
|
||||
}
|
||||
|
||||
/// Gets a mutable reference to an application data object stored by [`Lua::set_app_data()`] of type `T`.
|
||||
#[track_caller]
|
||||
pub fn app_data_mut<T: 'static>(&self) -> Option<RefMut<T>> {
|
||||
let extra = unsafe { &(*self.extra.get()) };
|
||||
let mut app_data = extra
|
||||
@@ -2169,6 +2273,7 @@ impl Lua {
|
||||
}
|
||||
|
||||
/// Removes an application data of type `T`.
|
||||
#[track_caller]
|
||||
pub fn remove_app_data<T: 'static>(&self) -> Option<T> {
|
||||
let extra = unsafe { &mut (*self.extra.get()) };
|
||||
extra
|
||||
@@ -2187,7 +2292,7 @@ impl Lua {
|
||||
}
|
||||
|
||||
Value::Boolean(b) => {
|
||||
ffi::lua_pushboolean(self.state, if b { 1 } else { 0 });
|
||||
ffi::lua_pushboolean(self.state, b as c_int);
|
||||
}
|
||||
|
||||
Value::LightUserData(ud) => {
|
||||
@@ -2228,7 +2333,8 @@ impl Lua {
|
||||
}
|
||||
|
||||
Value::Error(err) => {
|
||||
push_gc_userdata(self.state, WrappedFailure::Error(err))?;
|
||||
let protect = !self.unlikely_memory_error();
|
||||
push_gc_userdata(self.state, WrappedFailure::Error(err), protect)?;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2238,6 +2344,8 @@ impl Lua {
|
||||
// Uses 2 stack spaces, does not call checkstack
|
||||
pub(crate) unsafe fn pop_value(&self) -> Value {
|
||||
let state = self.state;
|
||||
let extra = &mut *self.extra.get();
|
||||
|
||||
match ffi::lua_type(state, -1) {
|
||||
ffi::LUA_TNIL => {
|
||||
ffi::lua_pop(state, 1);
|
||||
@@ -2256,15 +2364,29 @@ impl Lua {
|
||||
ud
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "lua54", feature = "lua53"))]
|
||||
ffi::LUA_TNUMBER => {
|
||||
if ffi::lua_isinteger(state, -1) != 0 {
|
||||
let i = Value::Integer(ffi::lua_tointeger(state, -1));
|
||||
ffi::lua_pop(state, 1);
|
||||
i
|
||||
let v = if ffi::lua_isinteger(state, -1) != 0 {
|
||||
Value::Integer(ffi::lua_tointeger(state, -1))
|
||||
} else {
|
||||
let n = Value::Number(ffi::lua_tonumber(state, -1));
|
||||
ffi::lua_pop(state, 1);
|
||||
n
|
||||
Value::Number(ffi::lua_tonumber(state, -1))
|
||||
};
|
||||
ffi::lua_pop(state, 1);
|
||||
v
|
||||
}
|
||||
|
||||
#[cfg(any(
|
||||
feature = "lua52",
|
||||
feature = "lua51",
|
||||
feature = "luajit",
|
||||
feature = "luau"
|
||||
))]
|
||||
ffi::LUA_TNUMBER => {
|
||||
let n = ffi::lua_tonumber(state, -1);
|
||||
ffi::lua_pop(state, 1);
|
||||
match num_traits::cast(n) {
|
||||
Some(i) if (n - (i as Number)).abs() < Number::EPSILON => Value::Integer(i),
|
||||
_ => Value::Number(n),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2284,9 +2406,11 @@ impl Lua {
|
||||
ffi::LUA_TFUNCTION => Value::Function(Function(self.pop_ref())),
|
||||
|
||||
ffi::LUA_TUSERDATA => {
|
||||
let wrapped_failure_mt_ptr = extra.wrapped_failure_mt_ptr;
|
||||
// We must prevent interaction with userdata types other than UserData OR a WrappedError.
|
||||
// WrappedPanics are automatically resumed.
|
||||
match get_gc_userdata::<WrappedFailure>(state, -1).as_mut() {
|
||||
match get_gc_userdata::<WrappedFailure>(state, -1, wrapped_failure_mt_ptr).as_mut()
|
||||
{
|
||||
Some(WrappedFailure::Error(err)) => {
|
||||
let err = err.clone();
|
||||
ffi::lua_pop(state, 1);
|
||||
@@ -2325,12 +2449,6 @@ impl Lua {
|
||||
"Lua instance passed Value created from a different main Lua state"
|
||||
);
|
||||
let extra = &*self.extra.get();
|
||||
#[cfg(not(feature = "luau"))]
|
||||
{
|
||||
ffi::lua_pushvalue(extra.ref_thread, lref.index);
|
||||
ffi::lua_xmove(extra.ref_thread, self.state, 1);
|
||||
}
|
||||
#[cfg(feature = "luau")]
|
||||
ffi::lua_xpush(extra.ref_thread, self.state, lref.index);
|
||||
}
|
||||
|
||||
@@ -2350,6 +2468,13 @@ impl Lua {
|
||||
LuaRef { lua: self, index }
|
||||
}
|
||||
|
||||
// Same as `pop_ref` but assumes the value is already on the reference thread
|
||||
pub(crate) unsafe fn pop_ref_thread(&self) -> LuaRef {
|
||||
let extra = &mut *self.extra.get();
|
||||
let index = ref_stack_pop(extra);
|
||||
LuaRef { lua: self, index }
|
||||
}
|
||||
|
||||
pub(crate) fn clone_ref<'lua>(&'lua self, lref: &LuaRef<'lua>) -> LuaRef<'lua> {
|
||||
unsafe {
|
||||
let extra = &mut *self.extra.get();
|
||||
@@ -2368,16 +2493,6 @@ impl Lua {
|
||||
}
|
||||
}
|
||||
|
||||
/// Executes the function provided on the ref thread
|
||||
#[inline]
|
||||
pub(crate) unsafe fn ref_thread_exec<F, R>(&self, f: F) -> R
|
||||
where
|
||||
F: FnOnce(*mut ffi::lua_State) -> R,
|
||||
{
|
||||
let ref_thread = (*self.extra.get()).ref_thread;
|
||||
f(ref_thread)
|
||||
}
|
||||
|
||||
unsafe fn push_userdata_metatable<T: 'static + UserData>(&self) -> Result<()> {
|
||||
let extra = &mut *self.extra.get();
|
||||
|
||||
@@ -2399,7 +2514,7 @@ impl Lua {
|
||||
let metatable_nrec = methods.meta_methods.len() + fields.meta_fields.len();
|
||||
#[cfg(feature = "async")]
|
||||
let metatable_nrec = metatable_nrec + methods.async_meta_methods.len();
|
||||
push_table(self.state, 0, metatable_nrec as c_int)?;
|
||||
push_table(self.state, 0, metatable_nrec as c_int, true)?;
|
||||
for (k, m) in methods.meta_methods {
|
||||
self.push_value(Value::Function(self.create_callback(m)?))?;
|
||||
rawset_field(self.state, -2, k.validate()?.name())?;
|
||||
@@ -2420,7 +2535,7 @@ impl Lua {
|
||||
let mut field_getters_index = None;
|
||||
let field_getters_nrec = fields.field_getters.len();
|
||||
if field_getters_nrec > 0 {
|
||||
push_table(self.state, 0, field_getters_nrec as c_int)?;
|
||||
push_table(self.state, 0, field_getters_nrec as c_int, true)?;
|
||||
for (k, m) in fields.field_getters {
|
||||
self.push_value(Value::Function(self.create_callback(m)?))?;
|
||||
rawset_field(self.state, -2, &k)?;
|
||||
@@ -2432,7 +2547,7 @@ impl Lua {
|
||||
let mut field_setters_index = None;
|
||||
let field_setters_nrec = fields.field_setters.len();
|
||||
if field_setters_nrec > 0 {
|
||||
push_table(self.state, 0, field_setters_nrec as c_int)?;
|
||||
push_table(self.state, 0, field_setters_nrec as c_int, true)?;
|
||||
for (k, m) in fields.field_setters {
|
||||
self.push_value(Value::Function(self.create_callback(m)?))?;
|
||||
rawset_field(self.state, -2, &k)?;
|
||||
@@ -2446,7 +2561,7 @@ impl Lua {
|
||||
#[cfg(feature = "async")]
|
||||
let methods_nrec = methods_nrec + methods.async_methods.len();
|
||||
if methods_nrec > 0 {
|
||||
push_table(self.state, 0, methods_nrec as c_int)?;
|
||||
push_table(self.state, 0, methods_nrec as c_int, true)?;
|
||||
for (k, m) in methods.methods {
|
||||
self.push_value(Value::Function(self.create_callback(m)?))?;
|
||||
rawset_field(self.state, -2, &k)?;
|
||||
@@ -2502,6 +2617,7 @@ impl Lua {
|
||||
pub(crate) unsafe fn push_userdata_ref(&self, lref: &LuaRef) -> Result<Option<TypeId>> {
|
||||
self.push_ref(lref);
|
||||
if ffi::lua_getmetatable(self.state, -1) == 0 {
|
||||
ffi::lua_pop(self.state, 1);
|
||||
return Err(Error::UserDataTypeMismatch);
|
||||
}
|
||||
let mt_ptr = ffi::lua_topointer(self.state, -1);
|
||||
@@ -2509,7 +2625,7 @@ impl Lua {
|
||||
|
||||
let extra = &*self.extra.get();
|
||||
match extra.registered_userdata_mt.get(&mt_ptr) {
|
||||
Some(&type_id) if type_id == Some(TypeId::of::<DestructedUserdataMT>()) => {
|
||||
Some(&type_id) if type_id == Some(TypeId::of::<DestructedUserdata>()) => {
|
||||
Err(Error::UserDataDestructed)
|
||||
}
|
||||
Some(&type_id) => Ok(type_id),
|
||||
@@ -2577,10 +2693,15 @@ impl Lua {
|
||||
|
||||
let func = mem::transmute(func);
|
||||
let extra = Arc::clone(&self.extra);
|
||||
push_gc_userdata(self.state, CallbackUpvalue { data: func, extra })?;
|
||||
protect_lua!(self.state, 1, 1, fn(state) {
|
||||
ffi::lua_pushcclosure(state, call_callback, 1);
|
||||
})?;
|
||||
let protect = !self.unlikely_memory_error();
|
||||
push_gc_userdata(self.state, CallbackUpvalue { data: func, extra }, protect)?;
|
||||
if protect {
|
||||
protect_lua!(self.state, 1, 1, fn(state) {
|
||||
ffi::lua_pushcclosure(state, call_callback, 1);
|
||||
})?;
|
||||
} else {
|
||||
ffi::lua_pushcclosure(self.state, call_callback, 1);
|
||||
}
|
||||
|
||||
Ok(Function(self.pop_ref()))
|
||||
}
|
||||
@@ -2636,10 +2757,15 @@ impl Lua {
|
||||
let func = &*(*upvalue).data;
|
||||
let fut = func(lua, args);
|
||||
let extra = Arc::clone(&(*upvalue).extra);
|
||||
push_gc_userdata(state, AsyncPollUpvalue { data: fut, extra })?;
|
||||
protect_lua!(state, 1, 1, fn(state) {
|
||||
let protect = !lua.unlikely_memory_error();
|
||||
push_gc_userdata(state, AsyncPollUpvalue { data: fut, extra }, protect)?;
|
||||
if protect {
|
||||
protect_lua!(state, 1, 1, fn(state) {
|
||||
ffi::lua_pushcclosure(state, poll_future, 1);
|
||||
})?;
|
||||
} else {
|
||||
ffi::lua_pushcclosure(state, poll_future, 1);
|
||||
})?;
|
||||
}
|
||||
|
||||
Ok(1)
|
||||
})
|
||||
@@ -2698,10 +2824,16 @@ impl Lua {
|
||||
|
||||
let func = mem::transmute(func);
|
||||
let extra = Arc::clone(&self.extra);
|
||||
push_gc_userdata(self.state, AsyncCallbackUpvalue { data: func, extra })?;
|
||||
protect_lua!(self.state, 1, 1, fn(state) {
|
||||
ffi::lua_pushcclosure(state, call_callback, 1);
|
||||
})?;
|
||||
let protect = !self.unlikely_memory_error();
|
||||
let upvalue = AsyncCallbackUpvalue { data: func, extra };
|
||||
push_gc_userdata(self.state, upvalue, protect)?;
|
||||
if protect {
|
||||
protect_lua!(self.state, 1, 1, fn(state) {
|
||||
ffi::lua_pushcclosure(state, call_callback, 1);
|
||||
})?;
|
||||
} else {
|
||||
ffi::lua_pushcclosure(self.state, call_callback, 1);
|
||||
}
|
||||
|
||||
Function(self.pop_ref())
|
||||
};
|
||||
@@ -2741,6 +2873,7 @@ impl Lua {
|
||||
end
|
||||
"#,
|
||||
)
|
||||
.try_cache()
|
||||
.set_name("_mlua_async_poll")?
|
||||
.set_environment(env)?
|
||||
.into_function()
|
||||
@@ -2774,19 +2907,25 @@ impl Lua {
|
||||
// We push metatable first to ensure having correct metatable with `__gc` method
|
||||
ffi::lua_pushnil(self.state);
|
||||
self.push_userdata_metatable::<T>()?;
|
||||
let protect = !self.unlikely_memory_error();
|
||||
#[cfg(not(feature = "lua54"))]
|
||||
push_userdata(self.state, data)?;
|
||||
push_userdata(self.state, data, protect)?;
|
||||
#[cfg(feature = "lua54")]
|
||||
push_userdata_uv(self.state, data, USER_VALUE_MAXSLOT as c_int)?;
|
||||
push_userdata_uv(self.state, data, USER_VALUE_MAXSLOT as c_int, protect)?;
|
||||
ffi::lua_replace(self.state, -3);
|
||||
ffi::lua_setmetatable(self.state, -2);
|
||||
|
||||
// Set empty environment for Lua 5.1
|
||||
#[cfg(any(feature = "lua51", feature = "luajit"))]
|
||||
protect_lua!(self.state, 1, 1, fn(state) {
|
||||
ffi::lua_newtable(state);
|
||||
ffi::lua_setuservalue(state, -2);
|
||||
})?;
|
||||
if protect {
|
||||
protect_lua!(self.state, 1, 1, fn(state) {
|
||||
ffi::lua_newtable(state);
|
||||
ffi::lua_setuservalue(state, -2);
|
||||
})?;
|
||||
} else {
|
||||
ffi::lua_newtable(self.state);
|
||||
ffi::lua_setuservalue(self.state, -2);
|
||||
}
|
||||
|
||||
Ok(AnyUserData(self.pop_ref()))
|
||||
}
|
||||
@@ -2822,7 +2961,7 @@ impl Lua {
|
||||
let _sg = StackGuard::new(state);
|
||||
assert_stack(state, 1);
|
||||
let extra = extra_data(state)?;
|
||||
let inner = &*(*extra.get()).inner.as_ref().unwrap();
|
||||
let inner = (*extra.get()).inner.as_ref().unwrap();
|
||||
Some(Lua(Arc::clone(inner)))
|
||||
}
|
||||
|
||||
@@ -2844,20 +2983,37 @@ impl Lua {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) unsafe fn unlikely_memory_error(&self) -> bool {
|
||||
let extra = &mut *self.extra.get();
|
||||
// MemoryInfo is empty in module mode so we cannot predict memory limits
|
||||
extra
|
||||
.mem_info
|
||||
.map(|x| x.as_ref().memory_limit == 0)
|
||||
.unwrap_or_default()
|
||||
}
|
||||
}
|
||||
|
||||
impl LuaInner {
|
||||
#[inline(always)]
|
||||
pub(crate) fn ref_thread(&self) -> *mut ffi::lua_State {
|
||||
unsafe { (*self.extra.get()).ref_thread }
|
||||
}
|
||||
}
|
||||
|
||||
struct StateGuard<'a>(&'a mut LuaInner, *mut ffi::lua_State);
|
||||
|
||||
impl<'a> StateGuard<'a> {
|
||||
fn new(inner: &'a mut LuaInner, mut state: *mut ffi::lua_State) -> Self {
|
||||
mem::swap(&mut (*inner).state, &mut state);
|
||||
mem::swap(&mut inner.state, &mut state);
|
||||
Self(inner, state)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Drop for StateGuard<'a> {
|
||||
fn drop(&mut self) {
|
||||
mem::swap(&mut (*self.0).state, &mut self.1);
|
||||
mem::swap(&mut self.0.state, &mut self.1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2910,6 +3066,18 @@ where
|
||||
|
||||
let nargs = ffi::lua_gettop(state);
|
||||
|
||||
// We need 2 extra stack spaces to store userdata and error/panic metatable.
|
||||
// Luau workaround can be removed after solving https://github.com/Roblox/luau/issues/446
|
||||
// Also see #142 and #153
|
||||
if !cfg!(feature = "luau") || extra.wrapped_failures_cache.is_empty() {
|
||||
let extra_stack = if nargs < 2 { 2 - nargs } else { 1 };
|
||||
ffi::luaL_checkstack(
|
||||
state,
|
||||
extra_stack,
|
||||
cstr!("not enough stack space for callback error handling"),
|
||||
);
|
||||
}
|
||||
|
||||
enum PreallocatedFailure {
|
||||
New(*mut WrappedFailure),
|
||||
Cached(i32),
|
||||
@@ -2920,13 +3088,6 @@ where
|
||||
let prealloc_failure = match extra.wrapped_failures_cache.pop() {
|
||||
Some(index) => PreallocatedFailure::Cached(index),
|
||||
None => {
|
||||
// We need 2 extra stack spaces to store userdata and error/panic metatable.
|
||||
let extra_stack = if nargs < 2 { 2 - nargs } else { 1 };
|
||||
ffi::luaL_checkstack(
|
||||
state,
|
||||
extra_stack,
|
||||
cstr!("not enough stack space for callback error handling"),
|
||||
);
|
||||
let ud = WrappedFailure::new_userdata(state);
|
||||
ffi::lua_rotate(state, 1, 1);
|
||||
PreallocatedFailure::New(ud)
|
||||
@@ -2940,6 +3101,8 @@ where
|
||||
}
|
||||
PreallocatedFailure::Cached(index) => {
|
||||
ffi::lua_settop(state, 0);
|
||||
#[cfg(feature = "luau")]
|
||||
assert_stack(state, 2);
|
||||
ffi::lua_pushvalue(extra.ref_thread, index);
|
||||
ffi::lua_xmove(extra.ref_thread, state, 1);
|
||||
ffi::lua_pushnil(extra.ref_thread);
|
||||
|
||||
+11
-3
@@ -22,6 +22,12 @@ impl Lua {
|
||||
globals.raw_set("require", self.create_function(lua_require)?)?;
|
||||
globals.raw_set("vector", self.create_c_function(lua_vector)?)?;
|
||||
|
||||
// Set `_VERSION` global to include version number
|
||||
// The environment variable `LUAU_VERSION` set by the build script
|
||||
if let Some(version) = option_env!("LUAU_VERSION") {
|
||||
globals.raw_set("_VERSION", format!("Luau {version}"))?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -85,10 +91,12 @@ fn lua_require(lua: &Lua, name: Option<std::string::String>) -> Result<Value> {
|
||||
search_path = "?.luau;?.lua".into();
|
||||
}
|
||||
|
||||
let mut source = None;
|
||||
let (mut source, mut source_name) = (None, String::new());
|
||||
for path in search_path.split(';') {
|
||||
if let Ok(buf) = std::fs::read(path.replacen('?', &name, 1)) {
|
||||
let file_path = path.replacen('?', &name, 1);
|
||||
if let Ok(buf) = std::fs::read(&file_path) {
|
||||
source = Some(buf);
|
||||
source_name = file_path;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -96,7 +104,7 @@ fn lua_require(lua: &Lua, name: Option<std::string::String>) -> Result<Value> {
|
||||
|
||||
let value = lua
|
||||
.load(&source)
|
||||
.set_name(&format!("={}", name))?
|
||||
.set_name(&format!("={}", source_name))?
|
||||
.set_mode(ChunkMode::Text)
|
||||
.call::<_, Value>(())?;
|
||||
|
||||
|
||||
+12
-1
@@ -11,6 +11,7 @@ use crate::value::{FromLua, FromLuaMulti, MultiValue, Nil, ToLua, ToLuaMulti};
|
||||
/// Result is convertible to `MultiValue` following the common Lua idiom of returning the result
|
||||
/// on success, or in the case of an error, returning `nil` and an error message.
|
||||
impl<'lua, T: ToLua<'lua>, E: ToLua<'lua>> ToLuaMulti<'lua> for StdResult<T, E> {
|
||||
#[inline]
|
||||
fn to_lua_multi(self, lua: &'lua Lua) -> Result<MultiValue<'lua>> {
|
||||
let mut result = MultiValue::new_or_cached(lua);
|
||||
match self {
|
||||
@@ -25,6 +26,7 @@ impl<'lua, T: ToLua<'lua>, E: ToLua<'lua>> ToLuaMulti<'lua> for StdResult<T, E>
|
||||
}
|
||||
|
||||
impl<'lua, T: ToLua<'lua>> ToLuaMulti<'lua> for T {
|
||||
#[inline]
|
||||
fn to_lua_multi(self, lua: &'lua Lua) -> Result<MultiValue<'lua>> {
|
||||
let mut v = MultiValue::new_or_cached(lua);
|
||||
v.push_front(self.to_lua(lua)?);
|
||||
@@ -33,6 +35,7 @@ impl<'lua, T: ToLua<'lua>> ToLuaMulti<'lua> for T {
|
||||
}
|
||||
|
||||
impl<'lua, T: FromLua<'lua>> FromLuaMulti<'lua> for T {
|
||||
#[inline]
|
||||
fn from_lua_multi(mut values: MultiValue<'lua>, lua: &'lua Lua) -> Result<Self> {
|
||||
let res = T::from_lua(values.pop_front().unwrap_or(Nil), lua);
|
||||
lua.cache_multivalue(values);
|
||||
@@ -41,12 +44,14 @@ impl<'lua, T: FromLua<'lua>> FromLuaMulti<'lua> for T {
|
||||
}
|
||||
|
||||
impl<'lua> ToLuaMulti<'lua> for MultiValue<'lua> {
|
||||
#[inline]
|
||||
fn to_lua_multi(self, _: &'lua Lua) -> Result<MultiValue<'lua>> {
|
||||
Ok(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> FromLuaMulti<'lua> for MultiValue<'lua> {
|
||||
#[inline]
|
||||
fn from_lua_multi(values: MultiValue<'lua>, _: &'lua Lua) -> Result<Self> {
|
||||
Ok(values)
|
||||
}
|
||||
@@ -83,7 +88,7 @@ pub struct Variadic<T>(Vec<T>);
|
||||
|
||||
impl<T> Variadic<T> {
|
||||
/// Creates an empty `Variadic` wrapper containing no values.
|
||||
pub fn new() -> Variadic<T> {
|
||||
pub const fn new() -> Variadic<T> {
|
||||
Variadic(Vec::new())
|
||||
}
|
||||
}
|
||||
@@ -124,6 +129,7 @@ impl<T> DerefMut for Variadic<T> {
|
||||
}
|
||||
|
||||
impl<'lua, T: ToLua<'lua>> ToLuaMulti<'lua> for Variadic<T> {
|
||||
#[inline]
|
||||
fn to_lua_multi(self, lua: &'lua Lua) -> Result<MultiValue<'lua>> {
|
||||
let mut values = MultiValue::new_or_cached(lua);
|
||||
values.refill(self.0.into_iter().map(|e| e.to_lua(lua)))?;
|
||||
@@ -132,6 +138,7 @@ impl<'lua, T: ToLua<'lua>> ToLuaMulti<'lua> for Variadic<T> {
|
||||
}
|
||||
|
||||
impl<'lua, T: FromLua<'lua>> FromLuaMulti<'lua> for Variadic<T> {
|
||||
#[inline]
|
||||
fn from_lua_multi(mut values: MultiValue<'lua>, lua: &'lua Lua) -> Result<Self> {
|
||||
let res = values
|
||||
.drain_all()
|
||||
@@ -146,12 +153,14 @@ impl<'lua, T: FromLua<'lua>> FromLuaMulti<'lua> for Variadic<T> {
|
||||
macro_rules! impl_tuple {
|
||||
() => (
|
||||
impl<'lua> ToLuaMulti<'lua> for () {
|
||||
#[inline]
|
||||
fn to_lua_multi(self, lua: &'lua Lua) -> Result<MultiValue<'lua>> {
|
||||
Ok(MultiValue::new_or_cached(lua))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> FromLuaMulti<'lua> for () {
|
||||
#[inline]
|
||||
fn from_lua_multi(values: MultiValue<'lua>, lua: &'lua Lua) -> Result<Self> {
|
||||
lua.cache_multivalue(values);
|
||||
Ok(())
|
||||
@@ -166,6 +175,7 @@ macro_rules! impl_tuple {
|
||||
{
|
||||
#[allow(unused_mut)]
|
||||
#[allow(non_snake_case)]
|
||||
#[inline]
|
||||
fn to_lua_multi(self, lua: &'lua Lua) -> Result<MultiValue<'lua>> {
|
||||
let ($($name,)* $last,) = self;
|
||||
|
||||
@@ -181,6 +191,7 @@ macro_rules! impl_tuple {
|
||||
{
|
||||
#[allow(unused_mut)]
|
||||
#[allow(non_snake_case)]
|
||||
#[inline]
|
||||
fn from_lua_multi(mut values: MultiValue<'lua>, lua: &'lua Lua) -> Result<Self> {
|
||||
$(let $name = values.pop_front().unwrap_or(Nil);)*
|
||||
let $last = FromLuaMulti::from_lua_multi(values, lua)?;
|
||||
|
||||
+1
-1
@@ -21,7 +21,7 @@ pub use crate::HookTriggers as LuaHookTriggers;
|
||||
|
||||
#[cfg(feature = "luau")]
|
||||
#[doc(no_inline)]
|
||||
pub use crate::VmState as LuaVmState;
|
||||
pub use crate::{CoverageInfo as LuaCoverageInfo, VmState as LuaVmState};
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
#[doc(no_inline)]
|
||||
|
||||
+6
-4
@@ -355,13 +355,14 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
|
||||
crate::util::push_userdata::<UserDataCell<Rc<RefCell<T>>>>(
|
||||
lua.state,
|
||||
UserDataCell::new(data.clone()),
|
||||
true,
|
||||
)?;
|
||||
ffi::lua_touserdata(lua.state, -1)
|
||||
};
|
||||
|
||||
// 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)?;
|
||||
push_table(lua.state, 0, meta_methods_nrec as c_int, true)?;
|
||||
|
||||
for (k, m) in ud_methods.meta_methods {
|
||||
let data = data.clone();
|
||||
@@ -377,7 +378,7 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
|
||||
let mut field_getters_index = None;
|
||||
let field_getters_nrec = ud_fields.field_getters.len();
|
||||
if field_getters_nrec > 0 {
|
||||
push_table(lua.state, 0, field_getters_nrec as c_int)?;
|
||||
push_table(lua.state, 0, field_getters_nrec as c_int, true)?;
|
||||
for (k, m) in ud_fields.field_getters {
|
||||
let data = data.clone();
|
||||
lua.push_value(Value::Function(wrap_method(self, data, ud_ptr, m)?))?;
|
||||
@@ -389,7 +390,7 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
|
||||
let mut field_setters_index = None;
|
||||
let field_setters_nrec = ud_fields.field_setters.len();
|
||||
if field_setters_nrec > 0 {
|
||||
push_table(lua.state, 0, field_setters_nrec as c_int)?;
|
||||
push_table(lua.state, 0, field_setters_nrec as c_int, true)?;
|
||||
for (k, m) in ud_fields.field_setters {
|
||||
let data = data.clone();
|
||||
lua.push_value(Value::Function(wrap_method(self, data, ud_ptr, m)?))?;
|
||||
@@ -402,7 +403,7 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
|
||||
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)?;
|
||||
push_table(lua.state, 0, methods_nrec as c_int, true)?;
|
||||
for (k, m) in ud_methods.methods {
|
||||
let data = data.clone();
|
||||
lua.push_value(Value::Function(wrap_method(self, data, ud_ptr, m)?))?;
|
||||
@@ -596,6 +597,7 @@ impl<'lua, 'scope> Drop for Scope<'lua, 'scope> {
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::type_complexity)]
|
||||
enum NonStaticMethod<'lua, T> {
|
||||
Method(Box<dyn Fn(&'lua Lua, &T, MultiValue<'lua>) -> Result<MultiValue<'lua>>>),
|
||||
MethodMut(Box<dyn FnMut(&'lua Lua, &mut T, MultiValue<'lua>) -> Result<MultiValue<'lua>>>),
|
||||
|
||||
+12
-10
@@ -8,7 +8,6 @@ use rustc_hash::FxHashSet;
|
||||
use serde::de::{self, IntoDeserializer};
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
use crate::ffi;
|
||||
use crate::table::{Table, TablePairs, TableSequence};
|
||||
use crate::value::Value;
|
||||
|
||||
@@ -72,7 +71,7 @@ impl Options {
|
||||
///
|
||||
/// [`deny_recursive_tables`]: #structfield.deny_recursive_tables
|
||||
#[must_use]
|
||||
pub fn deny_recursive_tables(mut self, enabled: bool) -> Self {
|
||||
pub const fn deny_recursive_tables(mut self, enabled: bool) -> Self {
|
||||
self.deny_recursive_tables = enabled;
|
||||
self
|
||||
}
|
||||
@@ -320,10 +319,17 @@ impl<'lua, 'de> serde::Deserializer<'de> for Deserializer<'lua> {
|
||||
self.deserialize_map(visitor)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn deserialize_newtype_struct<V>(self, _name: &'static str, visitor: V) -> Result<V::Value>
|
||||
where
|
||||
V: de::Visitor<'de>,
|
||||
{
|
||||
visitor.visit_newtype_struct(self)
|
||||
}
|
||||
|
||||
serde::forward_to_deserialize_any! {
|
||||
bool i8 i16 i32 i64 i128 u8 u16 u32 u64 u128 f32 f64 char str string bytes
|
||||
byte_buf unit unit_struct newtype_struct
|
||||
identifier ignored_any
|
||||
byte_buf unit unit_struct identifier ignored_any
|
||||
}
|
||||
}
|
||||
|
||||
@@ -556,9 +562,7 @@ impl RecursionGuard {
|
||||
#[inline]
|
||||
fn new(table: &Table, visited: &Rc<RefCell<FxHashSet<*const c_void>>>) -> Self {
|
||||
let visited = Rc::clone(visited);
|
||||
let lua = table.0.lua;
|
||||
let ptr =
|
||||
unsafe { lua.ref_thread_exec(|refthr| ffi::lua_topointer(refthr, table.0.index)) };
|
||||
let ptr = table.to_pointer();
|
||||
visited.borrow_mut().insert(ptr);
|
||||
RecursionGuard { ptr, visited }
|
||||
}
|
||||
@@ -578,9 +582,7 @@ fn check_value_if_skip(
|
||||
) -> Result<bool> {
|
||||
match value {
|
||||
Value::Table(table) => {
|
||||
let lua = table.0.lua;
|
||||
let ptr =
|
||||
unsafe { lua.ref_thread_exec(|refthr| ffi::lua_topointer(refthr, table.0.index)) };
|
||||
let ptr = table.to_pointer();
|
||||
if visited.borrow().contains(&ptr) {
|
||||
if options.deny_recursive_tables {
|
||||
return Err(de::Error::custom("recursive table detected"));
|
||||
|
||||
+3
-7
@@ -10,7 +10,7 @@ use crate::ffi;
|
||||
use crate::lua::Lua;
|
||||
use crate::table::Table;
|
||||
use crate::types::LightUserData;
|
||||
use crate::util::{assert_stack, check_stack, StackGuard};
|
||||
use crate::util::check_stack;
|
||||
use crate::value::Value;
|
||||
|
||||
/// Trait for serializing/deserializing Lua values using Serde.
|
||||
@@ -205,12 +205,8 @@ impl<'lua> LuaSerdeExt<'lua> for Lua {
|
||||
|
||||
fn array_metatable(&'lua self) -> Table<'lua> {
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(self.state);
|
||||
assert_stack(self.state, 1);
|
||||
|
||||
push_array_metatable(self.state);
|
||||
|
||||
Table(self.pop_ref())
|
||||
push_array_metatable(self.ref_thread());
|
||||
Table(self.pop_ref_thread())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+11
-4
@@ -327,10 +327,17 @@ impl<'lua> ser::SerializeSeq for SerializeVec<'lua> {
|
||||
|
||||
lua.push_ref(&self.table.0);
|
||||
lua.push_value(value)?;
|
||||
protect_lua!(lua.state, 2, 0, fn(state) {
|
||||
let len = ffi::lua_rawlen(state, -2) as Integer;
|
||||
ffi::lua_rawseti(state, -2, len + 1);
|
||||
})
|
||||
if lua.unlikely_memory_error() {
|
||||
let len = ffi::lua_rawlen(lua.state, -2) as Integer;
|
||||
ffi::lua_rawseti(lua.state, -2, len + 1);
|
||||
ffi::lua_pop(lua.state, 1);
|
||||
Ok(())
|
||||
} else {
|
||||
protect_lua!(lua.state, 2, 0, fn(state) {
|
||||
let len = ffi::lua_rawlen(state, -2) as Integer;
|
||||
ffi::lua_rawseti(state, -2, len + 1);
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+18
-8
@@ -1,5 +1,6 @@
|
||||
use std::borrow::{Borrow, Cow};
|
||||
use std::hash::{Hash, Hasher};
|
||||
use std::os::raw::c_void;
|
||||
use std::string::String as StdString;
|
||||
use std::{slice, str};
|
||||
|
||||
@@ -12,7 +13,6 @@ use {
|
||||
use crate::error::{Error, Result};
|
||||
use crate::ffi;
|
||||
use crate::types::LuaRef;
|
||||
use crate::util::{assert_stack, StackGuard};
|
||||
|
||||
/// Handle to an internal Lua string.
|
||||
///
|
||||
@@ -39,6 +39,7 @@ impl<'lua> String<'lua> {
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn to_str(&self) -> Result<&str> {
|
||||
str::from_utf8(self.as_bytes()).map_err(|e| Error::FromLuaConversionError {
|
||||
from: "string",
|
||||
@@ -65,6 +66,7 @@ impl<'lua> String<'lua> {
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn to_string_lossy(&self) -> Cow<'_, str> {
|
||||
StdString::from_utf8_lossy(self.as_bytes())
|
||||
}
|
||||
@@ -86,6 +88,7 @@ impl<'lua> String<'lua> {
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn as_bytes(&self) -> &[u8] {
|
||||
let nulled = self.as_bytes_with_nul();
|
||||
&nulled[..nulled.len() - 1]
|
||||
@@ -93,25 +96,32 @@ impl<'lua> String<'lua> {
|
||||
|
||||
/// Get the bytes that make up this string, including the trailing nul byte.
|
||||
pub fn as_bytes_with_nul(&self) -> &[u8] {
|
||||
let lua = self.0.lua;
|
||||
let ref_thread = self.0.lua.ref_thread();
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(lua.state);
|
||||
assert_stack(lua.state, 1);
|
||||
|
||||
lua.push_ref(&self.0);
|
||||
mlua_debug_assert!(
|
||||
ffi::lua_type(lua.state, -1) == ffi::LUA_TSTRING,
|
||||
ffi::lua_type(ref_thread, self.0.index) == ffi::LUA_TSTRING,
|
||||
"string ref is not string type"
|
||||
);
|
||||
|
||||
let mut size = 0;
|
||||
// This will not trigger a 'm' error, because the reference is guaranteed to be of
|
||||
// string type
|
||||
let data = ffi::lua_tolstring(lua.state, -1, &mut size);
|
||||
let data = ffi::lua_tolstring(ref_thread, self.0.index, &mut size);
|
||||
|
||||
slice::from_raw_parts(data as *const u8, size + 1)
|
||||
}
|
||||
}
|
||||
|
||||
/// Converts the string to a generic C pointer.
|
||||
///
|
||||
/// There is no way to convert the pointer back to its original value.
|
||||
///
|
||||
/// Typically this function is used only for hashing and debug information.
|
||||
#[inline]
|
||||
pub fn to_pointer(&self) -> *const c_void {
|
||||
let ref_thread = self.0.lua.ref_thread();
|
||||
unsafe { ffi::lua_topointer(ref_thread, self.0.index) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> AsRef<[u8]> for String<'lua> {
|
||||
|
||||
+172
-27
@@ -1,10 +1,11 @@
|
||||
use std::marker::PhantomData;
|
||||
use std::os::raw::c_void;
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
use {
|
||||
rustc_hash::FxHashSet,
|
||||
serde::ser::{self, Serialize, SerializeMap, SerializeSeq, Serializer},
|
||||
std::{cell::RefCell, os::raw::c_void, result::Result as StdResult},
|
||||
std::{cell::RefCell, result::Result as StdResult},
|
||||
};
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
@@ -57,6 +58,11 @@ impl<'lua> Table<'lua> {
|
||||
///
|
||||
/// [`raw_set`]: #method.raw_set
|
||||
pub fn set<K: ToLua<'lua>, V: ToLua<'lua>>(&self, key: K, value: V) -> Result<()> {
|
||||
// Fast track
|
||||
if !self.has_metatable() {
|
||||
return self.raw_set(key, value);
|
||||
}
|
||||
|
||||
let lua = self.0.lua;
|
||||
let key = key.to_lua(lua)?;
|
||||
let value = value.to_lua(lua)?;
|
||||
@@ -97,6 +103,11 @@ impl<'lua> Table<'lua> {
|
||||
///
|
||||
/// [`raw_get`]: #method.raw_get
|
||||
pub fn get<K: ToLua<'lua>, V: FromLua<'lua>>(&self, key: K) -> Result<V> {
|
||||
// Fast track
|
||||
if !self.has_metatable() {
|
||||
return self.raw_get(key);
|
||||
}
|
||||
|
||||
let lua = self.0.lua;
|
||||
let key = key.to_lua(lua)?;
|
||||
|
||||
@@ -115,18 +126,54 @@ impl<'lua> Table<'lua> {
|
||||
|
||||
/// Checks whether the table contains a non-nil value for `key`.
|
||||
pub fn contains_key<K: ToLua<'lua>>(&self, key: K) -> Result<bool> {
|
||||
let lua = self.0.lua;
|
||||
let key = key.to_lua(lua)?;
|
||||
Ok(self.get::<_, Value>(key)? != Value::Nil)
|
||||
}
|
||||
|
||||
/// Appends a value to the back of the table.
|
||||
pub fn push<V: ToLua<'lua>>(&self, value: V) -> Result<()> {
|
||||
// Fast track
|
||||
if !self.has_metatable() {
|
||||
return self.raw_push(value);
|
||||
}
|
||||
|
||||
let lua = self.0.lua;
|
||||
let value = value.to_lua(lua)?;
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(lua.state);
|
||||
check_stack(lua.state, 4)?;
|
||||
|
||||
lua.push_ref(&self.0);
|
||||
lua.push_value(key)?;
|
||||
protect_lua!(lua.state, 2, 1, fn(state) ffi::lua_gettable(state, -2))?;
|
||||
Ok(ffi::lua_isnil(lua.state, -1) == 0)
|
||||
lua.push_value(value)?;
|
||||
protect_lua!(lua.state, 2, 0, fn(state) {
|
||||
let len = ffi::luaL_len(state, -2) as Integer;
|
||||
ffi::lua_seti(state, -2, len + 1);
|
||||
})?
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Removes the last element from the table and returns it.
|
||||
pub fn pop<V: FromLua<'lua>>(&self) -> Result<V> {
|
||||
// Fast track
|
||||
if !self.has_metatable() {
|
||||
return self.raw_pop();
|
||||
}
|
||||
|
||||
let lua = self.0.lua;
|
||||
let value = unsafe {
|
||||
let _sg = StackGuard::new(lua.state);
|
||||
check_stack(lua.state, 4)?;
|
||||
|
||||
lua.push_ref(&self.0);
|
||||
protect_lua!(lua.state, 1, 1, fn(state) {
|
||||
let len = ffi::luaL_len(state, -1) as Integer;
|
||||
ffi::lua_geti(state, -1, len);
|
||||
ffi::lua_pushnil(state);
|
||||
ffi::lua_seti(state, -3, len);
|
||||
})?;
|
||||
lua.pop_value()
|
||||
};
|
||||
V::from_lua(value, lua)
|
||||
}
|
||||
|
||||
/// Compares two tables for equality.
|
||||
@@ -187,6 +234,9 @@ impl<'lua> Table<'lua> {
|
||||
|
||||
/// Sets a key-value pair without invoking metamethods.
|
||||
pub fn raw_set<K: ToLua<'lua>, V: ToLua<'lua>>(&self, key: K, value: V) -> Result<()> {
|
||||
#[cfg(feature = "luau")]
|
||||
self.check_readonly_write()?;
|
||||
|
||||
let lua = self.0.lua;
|
||||
let key = key.to_lua(lua)?;
|
||||
let value = value.to_lua(lua)?;
|
||||
@@ -198,7 +248,14 @@ impl<'lua> Table<'lua> {
|
||||
lua.push_ref(&self.0);
|
||||
lua.push_value(key)?;
|
||||
lua.push_value(value)?;
|
||||
protect_lua!(lua.state, 3, 0, fn(state) ffi::lua_rawset(state, -3))
|
||||
|
||||
if lua.unlikely_memory_error() {
|
||||
ffi::lua_rawset(lua.state, -3);
|
||||
ffi::lua_pop(lua.state, 1);
|
||||
Ok(())
|
||||
} else {
|
||||
protect_lua!(lua.state, 3, 0, fn(state) ffi::lua_rawset(state, -3))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -247,6 +304,56 @@ impl<'lua> Table<'lua> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Appends a value to the back of the table without invoking metamethods.
|
||||
pub fn raw_push<V: ToLua<'lua>>(&self, value: V) -> Result<()> {
|
||||
#[cfg(feature = "luau")]
|
||||
self.check_readonly_write()?;
|
||||
|
||||
let lua = self.0.lua;
|
||||
let value = value.to_lua(lua)?;
|
||||
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(lua.state);
|
||||
check_stack(lua.state, 4)?;
|
||||
|
||||
lua.push_ref(&self.0);
|
||||
lua.push_value(value)?;
|
||||
|
||||
unsafe fn callback(state: *mut ffi::lua_State) {
|
||||
let len = ffi::lua_rawlen(state, -2) as Integer;
|
||||
ffi::lua_rawseti(state, -2, len + 1);
|
||||
}
|
||||
|
||||
if lua.unlikely_memory_error() {
|
||||
callback(lua.state);
|
||||
} else {
|
||||
protect_lua!(lua.state, 2, 0, fn(state) callback(state))?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Removes the last element from the table and returns it, without invoking metamethods.
|
||||
pub fn raw_pop<V: FromLua<'lua>>(&self) -> Result<V> {
|
||||
#[cfg(feature = "luau")]
|
||||
self.check_readonly_write()?;
|
||||
|
||||
let lua = self.0.lua;
|
||||
let value = unsafe {
|
||||
let _sg = StackGuard::new(lua.state);
|
||||
check_stack(lua.state, 3)?;
|
||||
|
||||
lua.push_ref(&self.0);
|
||||
let len = ffi::lua_rawlen(lua.state, -1) as Integer;
|
||||
ffi::lua_rawgeti(lua.state, -1, len);
|
||||
// Set slot to nil (it must be safe to do)
|
||||
ffi::lua_pushnil(lua.state);
|
||||
ffi::lua_rawseti(lua.state, -3, len);
|
||||
lua.pop_value()
|
||||
};
|
||||
V::from_lua(value, lua)
|
||||
}
|
||||
|
||||
/// Removes a key from the table.
|
||||
///
|
||||
/// If `key` is an integer, mlua shifts down the elements from `table[key+1]`,
|
||||
@@ -288,6 +395,11 @@ impl<'lua> Table<'lua> {
|
||||
///
|
||||
/// [`raw_len`]: #method.raw_len
|
||||
pub fn len(&self) -> Result<Integer> {
|
||||
// Fast track
|
||||
if !self.has_metatable() {
|
||||
return Ok(self.raw_len());
|
||||
}
|
||||
|
||||
let lua = self.0.lua;
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(lua.state);
|
||||
@@ -300,14 +412,8 @@ impl<'lua> Table<'lua> {
|
||||
|
||||
/// Returns the result of the Lua `#` operator, without invoking the `__len` metamethod.
|
||||
pub fn raw_len(&self) -> Integer {
|
||||
let lua = self.0.lua;
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(lua.state);
|
||||
assert_stack(lua.state, 1);
|
||||
|
||||
lua.push_ref(&self.0);
|
||||
ffi::lua_rawlen(lua.state, -1) as Integer
|
||||
}
|
||||
let ref_thread = self.0.lua.ref_thread();
|
||||
unsafe { ffi::lua_rawlen(ref_thread, self.0.index) as Integer }
|
||||
}
|
||||
|
||||
/// Returns a reference to the metatable of this table, or `None` if no metatable is set.
|
||||
@@ -333,6 +439,12 @@ impl<'lua> Table<'lua> {
|
||||
/// If `metatable` is `None`, the metatable is removed (if no metatable is set, this does
|
||||
/// nothing).
|
||||
pub fn set_metatable(&self, metatable: Option<Table<'lua>>) {
|
||||
// Workaround to throw readonly error without returning Result
|
||||
#[cfg(feature = "luau")]
|
||||
if self.is_readonly() {
|
||||
panic!("attempt to modify a readonly table");
|
||||
}
|
||||
|
||||
let lua = self.0.lua;
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(lua.state);
|
||||
@@ -348,21 +460,33 @@ impl<'lua> Table<'lua> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns true if the table has metatable attached.
|
||||
#[doc(hidden)]
|
||||
#[inline]
|
||||
pub fn has_metatable(&self) -> bool {
|
||||
let ref_thread = self.0.lua.ref_thread();
|
||||
unsafe {
|
||||
if ffi::lua_getmetatable(ref_thread, self.0.index) != 0 {
|
||||
ffi::lua_pop(ref_thread, 1);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
/// Sets `readonly` attribute on the table.
|
||||
///
|
||||
/// Requires `feature = "luau"`
|
||||
#[cfg(any(feature = "luau", doc))]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
|
||||
pub fn set_readonly(&self, enabled: bool) {
|
||||
let lua = self.0.lua;
|
||||
let ref_thread = self.0.lua.ref_thread();
|
||||
unsafe {
|
||||
lua.ref_thread_exec(|refthr| {
|
||||
ffi::lua_setreadonly(refthr, self.0.index, enabled as _);
|
||||
if !enabled {
|
||||
// Reset "safeenv" flag
|
||||
ffi::lua_setsafeenv(refthr, self.0.index, 0);
|
||||
}
|
||||
});
|
||||
ffi::lua_setreadonly(ref_thread, self.0.index, enabled as _);
|
||||
if !enabled {
|
||||
// Reset "safeenv" flag
|
||||
ffi::lua_setsafeenv(ref_thread, self.0.index, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -372,8 +496,20 @@ impl<'lua> Table<'lua> {
|
||||
#[cfg(any(feature = "luau", doc))]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
|
||||
pub fn is_readonly(&self) -> bool {
|
||||
let lua = self.0.lua;
|
||||
unsafe { lua.ref_thread_exec(|refthr| ffi::lua_getreadonly(refthr, self.0.index) != 0) }
|
||||
let ref_thread = self.0.lua.ref_thread();
|
||||
unsafe { ffi::lua_getreadonly(ref_thread, self.0.index) != 0 }
|
||||
}
|
||||
|
||||
/// Converts the table to a generic C pointer.
|
||||
///
|
||||
/// Different tables will give different pointers.
|
||||
/// There is no way to convert the pointer back to its original value.
|
||||
///
|
||||
/// Typically this function is used only for hashing and debug information.
|
||||
#[inline]
|
||||
pub fn to_pointer(&self) -> *const c_void {
|
||||
let ref_thread = self.0.lua.ref_thread();
|
||||
unsafe { ffi::lua_topointer(ref_thread, self.0.index) }
|
||||
}
|
||||
|
||||
/// Consume this table and return an iterator over the pairs of the table.
|
||||
@@ -513,6 +649,16 @@ impl<'lua> Table<'lua> {
|
||||
ffi::lua_rawequal(lua.state, -1, -2) != 0
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "luau")]
|
||||
#[inline(always)]
|
||||
pub(crate) fn check_readonly_write(&self) -> Result<()> {
|
||||
if self.is_readonly() {
|
||||
let err = "attempt to modify a readonly table".to_string();
|
||||
return Err(Error::RuntimeError(err));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> PartialEq for Table<'lua> {
|
||||
@@ -693,8 +839,7 @@ impl<'lua> Serialize for Table<'lua> {
|
||||
static VISITED: RefCell<FxHashSet<*const c_void>> = RefCell::new(FxHashSet::default());
|
||||
}
|
||||
|
||||
let lua = self.0.lua;
|
||||
let ptr = unsafe { lua.ref_thread_exec(|refthr| ffi::lua_topointer(refthr, self.0.index)) };
|
||||
let ptr = self.to_pointer();
|
||||
let res = VISITED.with(|visited| {
|
||||
{
|
||||
let mut visited = visited.borrow_mut();
|
||||
|
||||
+19
-9
@@ -4,7 +4,7 @@ use std::os::raw::c_int;
|
||||
use crate::error::{Error, Result};
|
||||
use crate::ffi;
|
||||
use crate::types::LuaRef;
|
||||
use crate::util::{check_stack, error_traceback, pop_error, StackGuard};
|
||||
use crate::util::{check_stack, error_traceback_thread, pop_error, StackGuard};
|
||||
use crate::value::{FromLuaMulti, ToLuaMulti};
|
||||
|
||||
#[cfg(any(
|
||||
@@ -118,8 +118,7 @@ impl<'lua> Thread<'lua> {
|
||||
let _sg = StackGuard::new(lua.state);
|
||||
check_stack(lua.state, cmp::max(nargs + 1, 3))?;
|
||||
|
||||
let thread_state =
|
||||
lua.ref_thread_exec(|ref_thread| ffi::lua_tothread(ref_thread, self.0.index));
|
||||
let thread_state = ffi::lua_tothread(lua.ref_thread(), self.0.index);
|
||||
|
||||
let status = ffi::lua_status(thread_state);
|
||||
if status != ffi::LUA_YIELD && ffi::lua_gettop(thread_state) == 0 {
|
||||
@@ -136,8 +135,12 @@ impl<'lua> Thread<'lua> {
|
||||
|
||||
let ret = ffi::lua_resume(thread_state, lua.state, nargs, &mut nresults as *mut c_int);
|
||||
if ret != ffi::LUA_OK && ret != ffi::LUA_YIELD {
|
||||
protect_lua!(lua.state, 0, 0, |_| error_traceback(thread_state))?;
|
||||
return Err(pop_error(thread_state, ret));
|
||||
check_stack(lua.state, 3)?;
|
||||
protect_lua!(lua.state, 0, 1, |state| error_traceback_thread(
|
||||
state,
|
||||
thread_state
|
||||
))?;
|
||||
return Err(pop_error(lua.state, ret));
|
||||
}
|
||||
|
||||
let mut results = args; // Reuse MultiValue container
|
||||
@@ -156,8 +159,7 @@ impl<'lua> Thread<'lua> {
|
||||
pub fn status(&self) -> ThreadStatus {
|
||||
let lua = self.0.lua;
|
||||
unsafe {
|
||||
let thread_state =
|
||||
lua.ref_thread_exec(|ref_thread| ffi::lua_tothread(ref_thread, self.0.index));
|
||||
let thread_state = ffi::lua_tothread(lua.ref_thread(), self.0.index);
|
||||
|
||||
let status = ffi::lua_status(thread_state);
|
||||
if status != ffi::LUA_OK && status != ffi::LUA_YIELD {
|
||||
@@ -322,7 +324,7 @@ impl<'lua> Thread<'lua> {
|
||||
pub fn sandbox(&self) -> Result<()> {
|
||||
let lua = self.0.lua;
|
||||
unsafe {
|
||||
let thread = lua.ref_thread_exec(|t| ffi::lua_tothread(t, self.0.index));
|
||||
let thread = ffi::lua_tothread(lua.ref_thread(), self.0.index);
|
||||
check_stack(thread, 1)?;
|
||||
check_stack(lua.state, 3)?;
|
||||
// Inherit `LUA_GLOBALSINDEX` from the caller
|
||||
@@ -357,7 +359,15 @@ impl<'lua, R> Drop for AsyncThread<'lua, R> {
|
||||
fn drop(&mut self) {
|
||||
if self.recycle {
|
||||
unsafe {
|
||||
self.thread.0.lua.recycle_thread(&mut self.thread);
|
||||
let lua = self.thread.0.lua;
|
||||
// For Lua 5.4 this also closes all pending to-be-closed variables
|
||||
if !lua.recycle_thread(&mut self.thread) {
|
||||
#[cfg(feature = "lua54")]
|
||||
if self.thread.status() == ThreadStatus::Error {
|
||||
let thread_state = ffi::lua_tothread(lua.ref_thread(), self.thread.0.index);
|
||||
ffi::lua_resetthread(thread_state);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+34
-5
@@ -1,6 +1,7 @@
|
||||
use std::cell::UnsafeCell;
|
||||
use std::hash::{Hash, Hasher};
|
||||
use std::os::raw::{c_int, c_void};
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::{fmt, mem, ptr};
|
||||
|
||||
@@ -83,7 +84,7 @@ pub trait MaybeSend {}
|
||||
#[cfg(not(feature = "send"))]
|
||||
impl<T> MaybeSend for T {}
|
||||
|
||||
pub(crate) struct DestructedUserdataMT;
|
||||
pub(crate) struct DestructedUserdata;
|
||||
|
||||
/// An auto generated key into the Lua registry.
|
||||
///
|
||||
@@ -104,6 +105,7 @@ pub(crate) struct DestructedUserdataMT;
|
||||
/// [`AnyUserData::get_user_value`]: crate::AnyUserData::get_user_value
|
||||
pub struct RegistryKey {
|
||||
pub(crate) registry_id: c_int,
|
||||
pub(crate) is_nil: AtomicBool,
|
||||
pub(crate) unref_list: Arc<Mutex<Option<Vec<c_int>>>>,
|
||||
}
|
||||
|
||||
@@ -129,15 +131,27 @@ impl Eq for RegistryKey {}
|
||||
|
||||
impl Drop for RegistryKey {
|
||||
fn drop(&mut self) {
|
||||
let mut unref_list = mlua_expect!(self.unref_list.lock(), "unref list poisoned");
|
||||
if let Some(list) = unref_list.as_mut() {
|
||||
list.push(self.registry_id);
|
||||
// We don't need to collect nil slot
|
||||
if self.registry_id > ffi::LUA_REFNIL {
|
||||
let mut unref_list = mlua_expect!(self.unref_list.lock(), "unref list poisoned");
|
||||
if let Some(list) = unref_list.as_mut() {
|
||||
list.push(self.registry_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl RegistryKey {
|
||||
// Destroys the RegistryKey without adding to the drop list
|
||||
// Creates a new instance of `RegistryKey`
|
||||
pub(crate) const fn new(id: c_int, unref_list: Arc<Mutex<Option<Vec<c_int>>>>) -> Self {
|
||||
RegistryKey {
|
||||
registry_id: id,
|
||||
is_nil: AtomicBool::new(id == ffi::LUA_REFNIL),
|
||||
unref_list,
|
||||
}
|
||||
}
|
||||
|
||||
// Destroys the `RegistryKey` without adding to the unref list
|
||||
pub(crate) fn take(self) -> c_int {
|
||||
let registry_id = self.registry_id;
|
||||
unsafe {
|
||||
@@ -146,6 +160,21 @@ impl RegistryKey {
|
||||
}
|
||||
registry_id
|
||||
}
|
||||
|
||||
// Returns true if this `RegistryKey` holds a nil value
|
||||
#[inline(always)]
|
||||
pub(crate) fn is_nil(&self) -> bool {
|
||||
self.is_nil.load(Ordering::Relaxed)
|
||||
}
|
||||
|
||||
// Marks value of this `RegistryKey` as `Nil`
|
||||
#[inline(always)]
|
||||
pub(crate) fn set_nil(&self, enabled: bool) {
|
||||
// We cannot replace previous value with nil in as this will break
|
||||
// Lua mechanism to find free keys.
|
||||
// Instead, we set a special flag to mark value as nil.
|
||||
self.is_nil.store(enabled, Ordering::Relaxed);
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct LuaRef<'lua> {
|
||||
|
||||
@@ -118,7 +118,17 @@ pub enum MetaMethod {
|
||||
///
|
||||
/// [`ipairs`]: https://www.lua.org/manual/5.2/manual.html#pdf-ipairs
|
||||
#[cfg(any(feature = "lua52", feature = "luajit52", doc))]
|
||||
#[cfg_attr(docsrs, doc(cfg(any(feature = "lua52", feature = "luajit52"))))]
|
||||
IPairs,
|
||||
/// The `__iter` metamethod.
|
||||
///
|
||||
/// Executed before the iteration begins, and should return an iterator function like `next`
|
||||
/// (or a custom one).
|
||||
///
|
||||
/// Requires `feature = "lua"`
|
||||
#[cfg(any(feature = "luau", doc))]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
|
||||
Iter,
|
||||
/// The `__close` metamethod.
|
||||
///
|
||||
/// Executed when a variable, that marked as to-be-closed, goes out of scope.
|
||||
@@ -203,6 +213,8 @@ impl MetaMethod {
|
||||
MetaMethod::Pairs => "__pairs",
|
||||
#[cfg(any(feature = "lua52", feature = "luajit52"))]
|
||||
MetaMethod::IPairs => "__ipairs",
|
||||
#[cfg(feature = "luau")]
|
||||
MetaMethod::Iter => "__iter",
|
||||
|
||||
#[cfg(feature = "lua54")]
|
||||
MetaMethod::Close => "__close",
|
||||
@@ -270,6 +282,8 @@ impl From<StdString> for MetaMethod {
|
||||
"__pairs" => MetaMethod::Pairs,
|
||||
#[cfg(any(feature = "lua52", feature = "luajit52"))]
|
||||
"__ipairs" => MetaMethod::IPairs,
|
||||
#[cfg(feature = "luau")]
|
||||
"__iter" => MetaMethod::Iter,
|
||||
|
||||
#[cfg(feature = "lua54")]
|
||||
"__close" => MetaMethod::Close,
|
||||
|
||||
@@ -622,3 +622,8 @@ lua_userdata_impl!(Arc<RwLock<T>>);
|
||||
lua_userdata_impl!(Arc<parking_lot::Mutex<T>>);
|
||||
#[cfg(feature = "parking_lot")]
|
||||
lua_userdata_impl!(Arc<parking_lot::RwLock<T>>);
|
||||
|
||||
// A special proxy object for UserData
|
||||
pub(crate) struct UserDataProxy<T>(pub(crate) PhantomData<T>);
|
||||
|
||||
lua_userdata_impl!(UserDataProxy<T>);
|
||||
|
||||
+122
-61
@@ -1,6 +1,7 @@
|
||||
use std::any::{Any, TypeId};
|
||||
use std::ffi::CStr;
|
||||
use std::fmt::Write;
|
||||
use std::mem::MaybeUninit;
|
||||
use std::os::raw::{c_char, c_int, c_void};
|
||||
use std::panic::{catch_unwind, resume_unwind, AssertUnwindSafe};
|
||||
use std::sync::Arc;
|
||||
@@ -136,26 +137,21 @@ where
|
||||
F: Fn(*mut ffi::lua_State) -> R,
|
||||
R: Copy,
|
||||
{
|
||||
union URes<R: Copy> {
|
||||
uninit: (),
|
||||
init: R,
|
||||
}
|
||||
|
||||
struct Params<F, R: Copy> {
|
||||
function: F,
|
||||
result: URes<R>,
|
||||
result: MaybeUninit<R>,
|
||||
nresults: c_int,
|
||||
}
|
||||
|
||||
unsafe extern "C" fn do_call<F, R>(state: *mut ffi::lua_State) -> c_int
|
||||
where
|
||||
R: Copy,
|
||||
F: Fn(*mut ffi::lua_State) -> R,
|
||||
R: Copy,
|
||||
{
|
||||
let params = ffi::lua_touserdata(state, -1) as *mut Params<F, R>;
|
||||
ffi::lua_pop(state, 1);
|
||||
|
||||
(*params).result.init = ((*params).function)(state);
|
||||
(*params).result.write(((*params).function)(state));
|
||||
|
||||
if (*params).nresults == ffi::LUA_MULTRET {
|
||||
ffi::lua_gettop(state)
|
||||
@@ -174,7 +170,7 @@ where
|
||||
|
||||
let mut params = Params {
|
||||
function: f,
|
||||
result: URes { uninit: () },
|
||||
result: MaybeUninit::uninit(),
|
||||
nresults,
|
||||
};
|
||||
|
||||
@@ -185,7 +181,7 @@ where
|
||||
if ret == ffi::LUA_OK {
|
||||
// `LUA_OK` is only returned when the `do_call` function has completed successfully, so
|
||||
// `params.result` is definitely initialized.
|
||||
Ok(params.result.init)
|
||||
Ok(params.result.assume_init())
|
||||
} else {
|
||||
Err(pop_error(state, ret))
|
||||
}
|
||||
@@ -203,7 +199,7 @@ pub unsafe fn pop_error(state: *mut ffi::lua_State, err_code: c_int) -> Error {
|
||||
"pop_error called with non-error return code"
|
||||
);
|
||||
|
||||
match get_gc_userdata::<WrappedFailure>(state, -1).as_mut() {
|
||||
match get_gc_userdata::<WrappedFailure>(state, -1, ptr::null()).as_mut() {
|
||||
Some(WrappedFailure::Error(err)) => {
|
||||
ffi::lua_pop(state, 1);
|
||||
err.clone()
|
||||
@@ -246,22 +242,34 @@ pub unsafe fn pop_error(state: *mut ffi::lua_State, err_code: c_int) -> Error {
|
||||
}
|
||||
}
|
||||
|
||||
// Uses 3 stack spaces, does not call checkstack.
|
||||
#[inline]
|
||||
pub unsafe fn push_string<S: AsRef<[u8]> + ?Sized>(
|
||||
state: *mut ffi::lua_State,
|
||||
s: &S,
|
||||
) -> Result<()> {
|
||||
let s = s.as_ref();
|
||||
protect_lua!(state, 0, 1, |state| {
|
||||
// Uses 3 (or 1 if unprotected) stack spaces, does not call checkstack.
|
||||
#[inline(always)]
|
||||
pub unsafe fn push_string(state: *mut ffi::lua_State, s: &[u8], protect: bool) -> Result<()> {
|
||||
// Always use protected mode if the string is too long
|
||||
if protect || s.len() > (1 << 30) {
|
||||
protect_lua!(state, 0, 1, |state| {
|
||||
ffi::lua_pushlstring(state, s.as_ptr() as *const c_char, s.len());
|
||||
})
|
||||
} else {
|
||||
ffi::lua_pushlstring(state, s.as_ptr() as *const c_char, s.len());
|
||||
})
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
// Uses 3 stack spaces, does not call checkstack.
|
||||
#[inline]
|
||||
pub unsafe fn push_table(state: *mut ffi::lua_State, narr: c_int, nrec: c_int) -> Result<()> {
|
||||
protect_lua!(state, 0, 1, |state| ffi::lua_createtable(state, narr, nrec))
|
||||
pub unsafe fn push_table(
|
||||
state: *mut ffi::lua_State,
|
||||
narr: c_int,
|
||||
nrec: c_int,
|
||||
protect: bool,
|
||||
) -> Result<()> {
|
||||
if protect {
|
||||
protect_lua!(state, 0, 1, |state| ffi::lua_createtable(state, narr, nrec))
|
||||
} else {
|
||||
ffi::lua_createtable(state, narr, nrec);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
// Uses 4 stack spaces, does not call checkstack.
|
||||
@@ -281,10 +289,14 @@ where
|
||||
// Internally uses 3 stack spaces, does not call checkstack.
|
||||
#[cfg(not(feature = "luau"))]
|
||||
#[inline]
|
||||
pub unsafe fn push_userdata<T>(state: *mut ffi::lua_State, t: T) -> Result<()> {
|
||||
let ud = protect_lua!(state, 0, 1, |state| {
|
||||
pub unsafe fn push_userdata<T>(state: *mut ffi::lua_State, t: T, protect: bool) -> Result<()> {
|
||||
let ud = if protect {
|
||||
protect_lua!(state, 0, 1, |state| {
|
||||
ffi::lua_newuserdata(state, mem::size_of::<T>()) as *mut T
|
||||
})?
|
||||
} else {
|
||||
ffi::lua_newuserdata(state, mem::size_of::<T>()) as *mut T
|
||||
})?;
|
||||
};
|
||||
ptr::write(ud, t);
|
||||
Ok(())
|
||||
}
|
||||
@@ -292,20 +304,20 @@ pub unsafe fn push_userdata<T>(state: *mut ffi::lua_State, t: T) -> Result<()> {
|
||||
// Internally uses 3 stack spaces, does not call checkstack.
|
||||
#[cfg(feature = "luau")]
|
||||
#[inline]
|
||||
pub unsafe fn push_userdata<T>(state: *mut ffi::lua_State, t: T) -> Result<()> {
|
||||
pub unsafe fn push_userdata<T>(state: *mut ffi::lua_State, t: T, protect: bool) -> Result<()> {
|
||||
unsafe extern "C" fn destructor<T>(ud: *mut c_void) {
|
||||
let ud = ud as *mut T;
|
||||
if *(ud.offset(1) as *mut u8) == 0 {
|
||||
ptr::drop_in_place(ud);
|
||||
}
|
||||
ptr::drop_in_place(ud as *mut T);
|
||||
}
|
||||
|
||||
let ud = protect_lua!(state, 0, 1, |state| {
|
||||
let size = mem::size_of::<T>() + 1;
|
||||
let size = mem::size_of::<T>();
|
||||
let ud = if protect {
|
||||
protect_lua!(state, 0, 1, |state| {
|
||||
ffi::lua_newuserdatadtor(state, size, destructor::<T>) as *mut T
|
||||
})?
|
||||
} else {
|
||||
ffi::lua_newuserdatadtor(state, size, destructor::<T>) as *mut T
|
||||
})?;
|
||||
};
|
||||
ptr::write(ud, t);
|
||||
*(ud.offset(1) as *mut u8) = 0; // Mark as not destructed
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -313,10 +325,19 @@ pub unsafe fn push_userdata<T>(state: *mut ffi::lua_State, t: T) -> Result<()> {
|
||||
// Internally uses 3 stack spaces, does not call checkstack.
|
||||
#[cfg(feature = "lua54")]
|
||||
#[inline]
|
||||
pub unsafe fn push_userdata_uv<T>(state: *mut ffi::lua_State, t: T, nuvalue: c_int) -> Result<()> {
|
||||
let ud = protect_lua!(state, 0, 1, |state| {
|
||||
pub unsafe fn push_userdata_uv<T>(
|
||||
state: *mut ffi::lua_State,
|
||||
t: T,
|
||||
nuvalue: c_int,
|
||||
protect: bool,
|
||||
) -> Result<()> {
|
||||
let ud = if protect {
|
||||
protect_lua!(state, 0, 1, |state| {
|
||||
ffi::lua_newuserdatauv(state, mem::size_of::<T>(), nuvalue) as *mut T
|
||||
})?
|
||||
} else {
|
||||
ffi::lua_newuserdatauv(state, mem::size_of::<T>(), nuvalue) as *mut T
|
||||
})?;
|
||||
};
|
||||
ptr::write(ud, t);
|
||||
Ok(())
|
||||
}
|
||||
@@ -340,33 +361,51 @@ pub unsafe fn take_userdata<T>(state: *mut ffi::lua_State) -> T {
|
||||
get_destructed_userdata_metatable(state);
|
||||
ffi::lua_setmetatable(state, -2);
|
||||
let ud = get_userdata::<T>(state, -1);
|
||||
|
||||
// Update userdata tag to disable destructor and mark as destructed
|
||||
#[cfg(feature = "luau")]
|
||||
ffi::lua_setuserdatatag(state, -1, 1);
|
||||
|
||||
ffi::lua_pop(state, 1);
|
||||
if cfg!(feature = "luau") {
|
||||
*(ud.offset(1) as *mut u8) = 1; // Mark as destructed
|
||||
}
|
||||
ptr::read(ud)
|
||||
}
|
||||
|
||||
// Pushes the userdata and attaches a metatable with __gc method.
|
||||
// Internally uses 3 stack spaces, does not call checkstack.
|
||||
pub unsafe fn push_gc_userdata<T: Any>(state: *mut ffi::lua_State, t: T) -> Result<()> {
|
||||
push_userdata(state, t)?;
|
||||
pub unsafe fn push_gc_userdata<T: Any>(
|
||||
state: *mut ffi::lua_State,
|
||||
t: T,
|
||||
protect: bool,
|
||||
) -> Result<()> {
|
||||
push_userdata(state, t, protect)?;
|
||||
get_gc_metatable::<T>(state);
|
||||
ffi::lua_setmetatable(state, -2);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Uses 2 stack spaces, does not call checkstack
|
||||
pub unsafe fn get_gc_userdata<T: Any>(state: *mut ffi::lua_State, index: c_int) -> *mut T {
|
||||
pub unsafe fn get_gc_userdata<T: Any>(
|
||||
state: *mut ffi::lua_State,
|
||||
index: c_int,
|
||||
mt_ptr: *const c_void,
|
||||
) -> *mut T {
|
||||
let ud = ffi::lua_touserdata(state, index) as *mut T;
|
||||
if ud.is_null() || ffi::lua_getmetatable(state, index) == 0 {
|
||||
return ptr::null_mut();
|
||||
}
|
||||
get_gc_metatable::<T>(state);
|
||||
let res = ffi::lua_rawequal(state, -1, -2);
|
||||
ffi::lua_pop(state, 2);
|
||||
if res == 0 {
|
||||
return ptr::null_mut();
|
||||
if !mt_ptr.is_null() {
|
||||
let ud_mt_ptr = ffi::lua_topointer(state, -1);
|
||||
ffi::lua_pop(state, 1);
|
||||
if !ptr::eq(ud_mt_ptr, mt_ptr) {
|
||||
return ptr::null_mut();
|
||||
}
|
||||
} else {
|
||||
get_gc_metatable::<T>(state);
|
||||
let res = ffi::lua_rawequal(state, -1, -2);
|
||||
ffi::lua_pop(state, 2);
|
||||
if res == 0 {
|
||||
return ptr::null_mut();
|
||||
}
|
||||
}
|
||||
ud
|
||||
}
|
||||
@@ -389,11 +428,17 @@ unsafe fn init_userdata_metatable_index(state: *mut ffi::lua_State) -> Result<()
|
||||
}
|
||||
ffi::lua_pop(state, 1);
|
||||
|
||||
// Create and cache `__index` helper
|
||||
// Create and cache `__index` generator
|
||||
let code = cstr!(
|
||||
r#"
|
||||
local error, isfunction = ...
|
||||
return function (__index, field_getters, methods)
|
||||
-- Fastpath to return methods table for index access
|
||||
if __index == nil and field_getters == nil then
|
||||
return methods
|
||||
end
|
||||
|
||||
-- Alternatively return a function for index access
|
||||
return function (self, key)
|
||||
if field_getters ~= nil then
|
||||
local field_getter = field_getters[key]
|
||||
@@ -443,7 +488,7 @@ pub unsafe fn init_userdata_metatable_newindex(state: *mut ffi::lua_State) -> Re
|
||||
}
|
||||
ffi::lua_pop(state, 1);
|
||||
|
||||
// Create and cache `__newindex` helper
|
||||
// Create and cache `__newindex` generator
|
||||
let code = cstr!(
|
||||
r#"
|
||||
local error, isfunction = ...
|
||||
@@ -505,7 +550,7 @@ pub unsafe fn init_userdata_metatable<T>(
|
||||
// Push `__index` generator function
|
||||
init_userdata_metatable_index(state)?;
|
||||
|
||||
push_string(state, "__index")?;
|
||||
push_string(state, b"__index", true)?;
|
||||
let index_type = ffi::lua_rawget(state, -3);
|
||||
match index_type {
|
||||
ffi::LUA_TNIL | ffi::LUA_TTABLE | ffi::LUA_TFUNCTION => {
|
||||
@@ -530,7 +575,7 @@ pub unsafe fn init_userdata_metatable<T>(
|
||||
// Push `__newindex` generator function
|
||||
init_userdata_metatable_newindex(state)?;
|
||||
|
||||
push_string(state, "__newindex")?;
|
||||
push_string(state, b"__newindex", true)?;
|
||||
let newindex_type = ffi::lua_rawget(state, -3);
|
||||
match newindex_type {
|
||||
ffi::LUA_TNIL | ffi::LUA_TTABLE | ffi::LUA_TFUNCTION => {
|
||||
@@ -642,7 +687,7 @@ pub unsafe extern "C" fn error_traceback(state: *mut ffi::lua_State) -> c_int {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if get_gc_userdata::<WrappedFailure>(state, -1).is_null() {
|
||||
if get_gc_userdata::<WrappedFailure>(state, -1, ptr::null()).is_null() {
|
||||
let s = ffi::luaL_tolstring(state, -1, ptr::null_mut());
|
||||
if ffi::lua_checkstack(state, ffi::LUA_TRACEBACK_STACK) != 0 {
|
||||
ffi::luaL_traceback(state, state, s, 0);
|
||||
@@ -653,6 +698,20 @@ pub unsafe extern "C" fn error_traceback(state: *mut ffi::lua_State) -> c_int {
|
||||
1
|
||||
}
|
||||
|
||||
// A variant of `error_traceback` that can safely inspect another (yielded) thread stack
|
||||
pub unsafe fn error_traceback_thread(state: *mut ffi::lua_State, thread: *mut ffi::lua_State) {
|
||||
// Move error object to the main thread to safely call `__tostring` metamethod if present
|
||||
ffi::lua_xmove(thread, state, 1);
|
||||
|
||||
if get_gc_userdata::<WrappedFailure>(state, -1, ptr::null()).is_null() {
|
||||
let s = ffi::luaL_tolstring(state, -1, ptr::null_mut());
|
||||
if ffi::lua_checkstack(state, ffi::LUA_TRACEBACK_STACK) != 0 {
|
||||
ffi::luaL_traceback(state, thread, s, 0);
|
||||
ffi::lua_remove(state, -2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// A variant of `pcall` that does not allow Lua to catch Rust panics from `callback_error`.
|
||||
pub unsafe extern "C" fn safe_pcall(state: *mut ffi::lua_State) -> c_int {
|
||||
ffi::luaL_checkstack(state, 2, ptr::null());
|
||||
@@ -669,7 +728,7 @@ pub unsafe extern "C" fn safe_pcall(state: *mut ffi::lua_State) -> c_int {
|
||||
ffi::lua_gettop(state)
|
||||
} else {
|
||||
if let Some(WrappedFailure::Panic(_)) =
|
||||
get_gc_userdata::<WrappedFailure>(state, -1).as_ref()
|
||||
get_gc_userdata::<WrappedFailure>(state, -1, ptr::null()).as_ref()
|
||||
{
|
||||
ffi::lua_error(state);
|
||||
}
|
||||
@@ -685,7 +744,7 @@ pub unsafe extern "C" fn safe_xpcall(state: *mut ffi::lua_State) -> c_int {
|
||||
ffi::luaL_checkstack(state, 2, ptr::null());
|
||||
|
||||
if let Some(WrappedFailure::Panic(_)) =
|
||||
get_gc_userdata::<WrappedFailure>(state, -1).as_ref()
|
||||
get_gc_userdata::<WrappedFailure>(state, -1, ptr::null()).as_ref()
|
||||
{
|
||||
1
|
||||
} else {
|
||||
@@ -715,7 +774,7 @@ pub unsafe extern "C" fn safe_xpcall(state: *mut ffi::lua_State) -> c_int {
|
||||
ffi::lua_gettop(state) - 1
|
||||
} else {
|
||||
if let Some(WrappedFailure::Panic(_)) =
|
||||
get_gc_userdata::<WrappedFailure>(state, -1).as_ref()
|
||||
get_gc_userdata::<WrappedFailure>(state, -1, ptr::null()).as_ref()
|
||||
{
|
||||
ffi::lua_error(state);
|
||||
}
|
||||
@@ -758,7 +817,7 @@ pub unsafe fn init_gc_metatable<T: Any>(
|
||||
) -> Result<()> {
|
||||
check_stack(state, 6)?;
|
||||
|
||||
push_table(state, 0, 3)?;
|
||||
push_table(state, 0, 3, true)?;
|
||||
|
||||
#[cfg(not(feature = "luau"))]
|
||||
{
|
||||
@@ -799,7 +858,7 @@ pub unsafe fn init_error_registry(state: *mut ffi::lua_State) -> Result<()> {
|
||||
callback_error(state, |_| {
|
||||
check_stack(state, 3)?;
|
||||
|
||||
let err_buf = match get_gc_userdata::<WrappedFailure>(state, -1).as_ref() {
|
||||
let err_buf = match get_gc_userdata::<WrappedFailure>(state, -1, ptr::null()).as_ref() {
|
||||
Some(WrappedFailure::Error(error)) => {
|
||||
let err_buf_key = &ERROR_PRINT_BUFFER_KEY as *const u8 as *const c_void;
|
||||
ffi::lua_rawgetp(state, ffi::LUA_REGISTRYINDEX, err_buf_key);
|
||||
@@ -836,7 +895,7 @@ pub unsafe fn init_error_registry(state: *mut ffi::lua_State) -> Result<()> {
|
||||
}
|
||||
}?;
|
||||
|
||||
push_string(state, &*err_buf)?;
|
||||
push_string(state, (*err_buf).as_bytes(), true)?;
|
||||
(*err_buf).clear();
|
||||
|
||||
Ok(1)
|
||||
@@ -857,7 +916,7 @@ pub unsafe fn init_error_registry(state: *mut ffi::lua_State) -> Result<()> {
|
||||
callback_error(state, |_| Err(Error::CallbackDestructed))
|
||||
}
|
||||
|
||||
push_table(state, 0, 26)?;
|
||||
push_table(state, 0, 26, true)?;
|
||||
ffi::lua_pushcfunction(state, destructed_error);
|
||||
for &method in &[
|
||||
"__add",
|
||||
@@ -899,6 +958,8 @@ pub unsafe fn init_error_registry(state: *mut ffi::lua_State) -> Result<()> {
|
||||
"__pairs",
|
||||
#[cfg(any(feature = "lua53", feature = "lua52", feature = "luajit52"))]
|
||||
"__ipairs",
|
||||
#[cfg(feature = "luau")]
|
||||
"__iter",
|
||||
#[cfg(feature = "lua54")]
|
||||
"__close",
|
||||
] {
|
||||
@@ -914,7 +975,7 @@ pub unsafe fn init_error_registry(state: *mut ffi::lua_State) -> Result<()> {
|
||||
|
||||
// Create error print buffer
|
||||
init_gc_metatable::<String>(state, None)?;
|
||||
push_gc_userdata(state, String::new())?;
|
||||
push_gc_userdata(state, String::new(), true)?;
|
||||
protect_lua!(state, 1, 0, fn(state) {
|
||||
let err_buf_key = &ERROR_PRINT_BUFFER_KEY as *const u8 as *const c_void;
|
||||
ffi::lua_rawsetp(state, ffi::LUA_REGISTRYINDEX, err_buf_key);
|
||||
|
||||
+59
-8
@@ -1,5 +1,7 @@
|
||||
use std::iter::{self, FromIterator};
|
||||
use std::{slice, str, vec};
|
||||
use std::ops::Index;
|
||||
use std::os::raw::c_void;
|
||||
use std::{ptr, slice, str, vec};
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
use {
|
||||
@@ -9,6 +11,7 @@ use {
|
||||
};
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
use crate::ffi;
|
||||
use crate::function::Function;
|
||||
use crate::lua::Lua;
|
||||
use crate::string::String;
|
||||
@@ -93,6 +96,30 @@ impl<'lua> Value<'lua> {
|
||||
_ => Ok(self == other.as_ref()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Converts the value to a generic C pointer.
|
||||
///
|
||||
/// The value can be a userdata, a table, a thread, a string, or a function; otherwise it returns NULL.
|
||||
/// Different objects will give different pointers.
|
||||
/// There is no way to convert the pointer back to its original value.
|
||||
///
|
||||
/// Typically this function is used only for hashing and debug information.
|
||||
#[inline]
|
||||
pub fn to_pointer(&self) -> *const c_void {
|
||||
unsafe {
|
||||
match self {
|
||||
Value::LightUserData(ud) => ud.0,
|
||||
Value::Table(t) => t.to_pointer(),
|
||||
Value::String(s) => s.to_pointer(),
|
||||
Value::Function(Function(r))
|
||||
| Value::Thread(Thread(r))
|
||||
| Value::UserData(AnyUserData(r)) => {
|
||||
ffi::lua_topointer(r.lua.ref_thread(), r.index)
|
||||
}
|
||||
_ => ptr::null(),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> PartialEq for Value<'lua> {
|
||||
@@ -170,8 +197,7 @@ pub struct MultiValue<'lua>(Vec<Value<'lua>>);
|
||||
|
||||
impl<'lua> MultiValue<'lua> {
|
||||
/// Creates an empty `MultiValue` containing no values.
|
||||
#[inline]
|
||||
pub fn new() -> MultiValue<'lua> {
|
||||
pub const fn new() -> MultiValue<'lua> {
|
||||
MultiValue(Vec::new())
|
||||
}
|
||||
|
||||
@@ -212,7 +238,24 @@ impl<'a, 'lua> IntoIterator for &'a MultiValue<'lua> {
|
||||
|
||||
#[inline]
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
(&self.0).iter().rev()
|
||||
self.0.iter().rev()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> Index<usize> for MultiValue<'lua> {
|
||||
type Output = Value<'lua>;
|
||||
|
||||
#[inline]
|
||||
fn index(&self, index: usize) -> &Self::Output {
|
||||
if let Some(result) = self.get(index) {
|
||||
result
|
||||
} else {
|
||||
panic!(
|
||||
"index out of bounds: the len is {} but the index is {}",
|
||||
self.len(),
|
||||
index
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -230,19 +273,27 @@ impl<'lua> MultiValue<'lua> {
|
||||
v
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get(&self, index: usize) -> Option<&Value<'lua>> {
|
||||
if index < self.0.len() {
|
||||
return self.0.get(self.0.len() - index - 1);
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn reserve(&mut self, size: usize) {
|
||||
self.0.reserve(size);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn push_front(&mut self, value: Value<'lua>) {
|
||||
self.0.push(value);
|
||||
pub fn pop_front(&mut self) -> Option<Value<'lua>> {
|
||||
self.0.pop()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn pop_front(&mut self) -> Option<Value<'lua>> {
|
||||
self.0.pop()
|
||||
pub fn push_front(&mut self, value: Value<'lua>) {
|
||||
self.0.push(value);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
||||
+82
-1
@@ -75,7 +75,10 @@ async fn test_async_call() -> Result<()> {
|
||||
async fn test_async_bind_call() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let sum = lua.create_async_function(|_lua, (a, b): (i64, i64)| async move { Ok(a + b) })?;
|
||||
let sum = lua.create_async_function(|_lua, (a, b): (i64, i64)| async move {
|
||||
tokio::task::yield_now().await;
|
||||
Ok(a + b)
|
||||
})?;
|
||||
|
||||
let plus_10 = sum.bind(10)?;
|
||||
lua.globals().set("plus_10", plus_10)?;
|
||||
@@ -171,6 +174,38 @@ async fn test_async_return_async_closure() -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(feature = "lua54")]
|
||||
#[tokio::test]
|
||||
async fn test_async_lua54_to_be_closed() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let globals = lua.globals();
|
||||
globals.set("close_count", 0)?;
|
||||
|
||||
let code = r#"
|
||||
local t <close> = setmetatable({}, {
|
||||
__close = function()
|
||||
close_count = close_count + 1
|
||||
end
|
||||
})
|
||||
error "test"
|
||||
"#;
|
||||
let f = lua.load(code).into_function()?;
|
||||
|
||||
// Test close using call_async
|
||||
let _ = f.call_async::<_, ()>(()).await;
|
||||
assert_eq!(globals.get::<_, usize>("close_count")?, 1);
|
||||
|
||||
// Don't close by default when awaiting async threads
|
||||
let co = lua.create_thread(f.clone())?;
|
||||
let _ = co.clone().into_async::<_, ()>(()).await;
|
||||
assert_eq!(globals.get::<_, usize>("close_count")?, 1);
|
||||
let _ = co.reset(f);
|
||||
assert_eq!(globals.get::<_, usize>("close_count")?, 2);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_async_thread_stream() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
@@ -275,6 +310,28 @@ async fn test_async_table() -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_async_thread_cache() -> Result<()> {
|
||||
let options = LuaOptions::new().thread_cache_size(4);
|
||||
let lua = Lua::new_with(StdLib::ALL_SAFE, options)?;
|
||||
|
||||
let error_f = lua.create_async_function(|_, ()| async move {
|
||||
Delay::new(Duration::from_millis(10)).await;
|
||||
Err::<(), _>(Error::RuntimeError("test".to_string()))
|
||||
})?;
|
||||
|
||||
let sleep = lua.create_async_function(|_, n| async move {
|
||||
Delay::new(Duration::from_millis(n)).await;
|
||||
Ok(format!("elapsed:{}ms", n))
|
||||
})?;
|
||||
|
||||
assert!(error_f.call_async::<_, ()>(()).await.is_err());
|
||||
// Next call should use cached thread
|
||||
assert_eq!(sleep.call_async::<_, String>(3).await?, "elapsed:3ms");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_async_userdata() -> Result<()> {
|
||||
#[derive(Clone)]
|
||||
@@ -369,6 +426,30 @@ async fn test_async_userdata() -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_async_thread_error() -> Result<()> {
|
||||
struct MyUserData;
|
||||
|
||||
impl UserData for MyUserData {
|
||||
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
|
||||
methods.add_meta_method("__tostring", |_, _this, ()| Ok("myuserdata error"))
|
||||
}
|
||||
}
|
||||
|
||||
let lua = Lua::new();
|
||||
let result = lua
|
||||
.load("function x(...) error(...) end x(...)")
|
||||
.set_name("chunk")?
|
||||
.call_async::<_, ()>(MyUserData)
|
||||
.await;
|
||||
assert!(
|
||||
matches!(result, Err(Error::RuntimeError(cause)) if cause.contains("myuserdata error")),
|
||||
"improper error traceback from dead thread"
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_async_scope() -> Result<()> {
|
||||
let ref lua = Lua::new();
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
use mlua::{Lua, UserData, UserDataMethods};
|
||||
use mlua::{UserData, UserDataMethods};
|
||||
|
||||
fn main() {
|
||||
let ref lua = Lua::new();
|
||||
|
||||
#[derive(Clone)]
|
||||
struct MyUserData<'a>(&'a i64);
|
||||
|
||||
|
||||
@@ -1,41 +1,14 @@
|
||||
error[E0495]: cannot infer an appropriate lifetime due to conflicting requirements
|
||||
--> tests/compile/async_nonstatic_userdata.rs:11:72
|
||||
|
|
||||
11 | methods.add_async_method("print", |_, data, ()| async move {
|
||||
| ________________________________________________________________________^
|
||||
12 | | println!("{}", data.0);
|
||||
13 | | Ok(())
|
||||
14 | | });
|
||||
| |_____________^
|
||||
|
|
||||
note: first, the lifetime cannot outlive the lifetime `'a` as defined here...
|
||||
--> tests/compile/async_nonstatic_userdata.rs:9:10
|
||||
|
|
||||
9 | impl<'a> UserData for MyUserData<'a> {
|
||||
| ^^
|
||||
note: ...so that the types are compatible
|
||||
--> tests/compile/async_nonstatic_userdata.rs:11:72
|
||||
|
|
||||
11 | methods.add_async_method("print", |_, data, ()| async move {
|
||||
| ________________________________________________________________________^
|
||||
12 | | println!("{}", data.0);
|
||||
13 | | Ok(())
|
||||
14 | | });
|
||||
| |_____________^
|
||||
= note: expected `(MyUserData<'_>,)`
|
||||
found `(MyUserData<'a>,)`
|
||||
note: but, the lifetime must be valid for the lifetime `'lua` as defined here...
|
||||
--> tests/compile/async_nonstatic_userdata.rs:10:24
|
||||
|
|
||||
10 | fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
|
||||
| ^^^^
|
||||
note: ...so that the type `impl Future<Output = [async output]>` will meet its required lifetime bounds...
|
||||
--> tests/compile/async_nonstatic_userdata.rs:11:21
|
||||
|
|
||||
11 | methods.add_async_method("print", |_, data, ()| async move {
|
||||
| ^^^^^^^^^^^^^^^^
|
||||
note: ...that is required by this bound
|
||||
--> src/userdata.rs
|
||||
|
|
||||
| MR: 'lua + Future<Output = Result<R>>;
|
||||
| ^^^^
|
||||
error: lifetime may not live long enough
|
||||
--> tests/compile/async_nonstatic_userdata.rs:9:13
|
||||
|
|
||||
7 | impl<'a> UserData for MyUserData<'a> {
|
||||
| -- lifetime `'a` defined here
|
||||
8 | fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
|
||||
| ---- lifetime `'lua` defined here
|
||||
9 | / methods.add_async_method("print", |_, data, ()| async move {
|
||||
10 | | println!("{}", data.0);
|
||||
11 | | Ok(())
|
||||
12 | | });
|
||||
| |______________^ argument requires that `'a` must outlive `'lua`
|
||||
|
|
||||
= help: consider adding the following bound: `'a: 'lua`
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
error[E0373]: closure may outlive the current function, but it borrows `test`, which is owned by the current function
|
||||
error[E0373]: closure may outlive the current function, but it borrows `test.0`, which is owned by the current function
|
||||
--> tests/compile/function_borrow.rs:9:33
|
||||
|
|
||||
9 | let _ = lua.create_function(|_, ()| -> Result<i32> {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^ may outlive borrowed value `test`
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^ may outlive borrowed value `test.0`
|
||||
10 | Ok(test.0)
|
||||
| ------ `test` is borrowed here
|
||||
| ------ `test.0` is borrowed here
|
||||
|
|
||||
note: function requires argument type to outlive `'static`
|
||||
--> tests/compile/function_borrow.rs:9:13
|
||||
@@ -14,7 +14,7 @@ note: function requires argument type to outlive `'static`
|
||||
10 | | Ok(test.0)
|
||||
11 | | });
|
||||
| |______^
|
||||
help: to force the closure to take ownership of `test` (and any other referenced variables), use the `move` keyword
|
||||
help: to force the closure to take ownership of `test.0` (and any other referenced variables), use the `move` keyword
|
||||
|
|
||||
9 | let _ = lua.create_function(move |_, ()| -> Result<i32> {
|
||||
| ++++
|
||||
|
||||
@@ -10,7 +10,11 @@ error[E0277]: the type `UnsafeCell<mlua::lua::LuaInner>` may contain interior mu
|
||||
= note: required because it appears within the type `Arc<UnsafeCell<mlua::lua::LuaInner>>`
|
||||
= note: required because it appears within the type `Lua`
|
||||
= note: required because of the requirements on the impl of `UnwindSafe` for `&Lua`
|
||||
= note: required because it appears within the type `[closure@$DIR/tests/compile/lua_norefunwindsafe.rs:7:18: 7:48]`
|
||||
note: required because it's used within this closure
|
||||
--> tests/compile/lua_norefunwindsafe.rs:7:18
|
||||
|
|
||||
7 | catch_unwind(|| lua.create_table().unwrap());
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
note: required by a bound in `catch_unwind`
|
||||
--> $RUST/std/src/panic.rs
|
||||
|
|
||||
|
||||
@@ -10,7 +10,14 @@ error[E0277]: `Rc<Cell<i32>>` cannot be sent between threads safely
|
||||
| |_____- within this `[closure@$DIR/tests/compile/non_send.rs:11:25: 13:6]`
|
||||
|
|
||||
= help: within `[closure@$DIR/tests/compile/non_send.rs:11:25: 13:6]`, the trait `Send` is not implemented for `Rc<Cell<i32>>`
|
||||
= note: required because it appears within the type `[closure@$DIR/tests/compile/non_send.rs:11:25: 13:6]`
|
||||
note: required because it's used within this closure
|
||||
--> tests/compile/non_send.rs:11:25
|
||||
|
|
||||
11 | lua.create_function(move |_, ()| {
|
||||
| _________________________^
|
||||
12 | | Ok(data.get())
|
||||
13 | | })?
|
||||
| |_____^
|
||||
= note: required because of the requirements on the impl of `mlua::types::MaybeSend` for `[closure@$DIR/tests/compile/non_send.rs:11:25: 13:6]`
|
||||
note: required by a bound in `Lua::create_function`
|
||||
--> src/lua.rs
|
||||
|
||||
@@ -12,7 +12,11 @@ error[E0277]: the type `UnsafeCell<mlua::lua::LuaInner>` may contain interior mu
|
||||
= 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]`
|
||||
note: required because it's used within this closure
|
||||
--> tests/compile/ref_nounwindsafe.rs:8:18
|
||||
|
|
||||
8 | catch_unwind(move || table.set("a", "b").unwrap());
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
note: required by a bound in `catch_unwind`
|
||||
--> $RUST/std/src/panic.rs
|
||||
|
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
error[E0373]: closure may outlive the current function, but it borrows `test`, which is owned by the current function
|
||||
error[E0373]: closure may outlive the current function, but it borrows `test.field`, which is owned by the current function
|
||||
--> tests/compile/scope_invariance.rs:14:38
|
||||
|
|
||||
9 | lua.scope(|scope| {
|
||||
| ----- has type `&mlua::Scope<'_, '1>`
|
||||
...
|
||||
14 | .create_function_mut(|_, ()| {
|
||||
| ^^^^^^^ may outlive borrowed value `test`
|
||||
| ^^^^^^^ may outlive borrowed value `test.field`
|
||||
15 | test.field = 42;
|
||||
| ---------- `test` is borrowed here
|
||||
| ---------- `test.field` is borrowed here
|
||||
|
|
||||
note: function requires argument type to outlive `'1`
|
||||
--> tests/compile/scope_invariance.rs:13:13
|
||||
@@ -19,7 +19,7 @@ note: function requires argument type to outlive `'1`
|
||||
17 | | Ok(())
|
||||
18 | | })?
|
||||
| |__________________^
|
||||
help: to force the closure to take ownership of `test` (and any other referenced variables), use the `move` keyword
|
||||
help: to force the closure to take ownership of `test.field` (and any other referenced variables), use the `move` keyword
|
||||
|
|
||||
14 | .create_function_mut(move |_, ()| {
|
||||
| ++++
|
||||
|
||||
@@ -1,33 +1,31 @@
|
||||
error[E0597]: `lua` does not live long enough
|
||||
--> tests/compile/static_callback_args.rs:12:5
|
||||
|
|
||||
12 | / lua.create_function(|_, table: Table| {
|
||||
13 | | BAD_TIME.with(|bt| {
|
||||
14 | | *bt.borrow_mut() = Some(table);
|
||||
15 | | });
|
||||
16 | | Ok(())
|
||||
17 | | })?
|
||||
| | ^
|
||||
| | |
|
||||
| |______borrowed value does not live long enough
|
||||
| argument requires that `lua` is borrowed for `'static`
|
||||
12 | / lua.create_function(|_, table: Table| {
|
||||
13 | | BAD_TIME.with(|bt| {
|
||||
| |_________-
|
||||
14 | || *bt.borrow_mut() = Some(table);
|
||||
15 | || });
|
||||
| ||__________- argument requires that `lua` is borrowed for `'static`
|
||||
16 | | Ok(())
|
||||
17 | | })?
|
||||
| |______^ borrowed value does not live long enough
|
||||
...
|
||||
32 | }
|
||||
| - `lua` dropped here while still borrowed
|
||||
32 | }
|
||||
| - `lua` dropped here while still borrowed
|
||||
|
||||
error[E0505]: cannot move out of `lua` because it is borrowed
|
||||
--> tests/compile/static_callback_args.rs:22:10
|
||||
|
|
||||
12 | / lua.create_function(|_, table: Table| {
|
||||
13 | | BAD_TIME.with(|bt| {
|
||||
14 | | *bt.borrow_mut() = Some(table);
|
||||
15 | | });
|
||||
16 | | Ok(())
|
||||
17 | | })?
|
||||
| | -
|
||||
| | |
|
||||
| |______borrow of `lua` occurs here
|
||||
| argument requires that `lua` is borrowed for `'static`
|
||||
12 | / lua.create_function(|_, table: Table| {
|
||||
13 | | BAD_TIME.with(|bt| {
|
||||
| |_________-
|
||||
14 | || *bt.borrow_mut() = Some(table);
|
||||
15 | || });
|
||||
| ||__________- argument requires that `lua` is borrowed for `'static`
|
||||
16 | | Ok(())
|
||||
17 | | })?
|
||||
| |______- borrow of `lua` occurs here
|
||||
...
|
||||
22 | drop(lua);
|
||||
| ^^^ move out of `lua` occurs here
|
||||
22 | drop(lua);
|
||||
| ^^^ move out of `lua` occurs here
|
||||
|
||||
@@ -42,11 +42,17 @@ fn test_bind() -> Result<()> {
|
||||
concat = concat.bind("foo")?;
|
||||
concat = concat.bind("bar")?;
|
||||
concat = concat.bind(("baz", "baf"))?;
|
||||
assert_eq!(concat.call::<_, String>(())?, "foobarbazbaf");
|
||||
assert_eq!(
|
||||
concat.call::<_, String>(("hi", "wut"))?,
|
||||
"foobarbazbafhiwut"
|
||||
);
|
||||
|
||||
let mut concat2 = globals.get::<_, Function>("concat")?;
|
||||
concat2 = concat2.bind(())?;
|
||||
assert_eq!(concat2.call::<_, String>(())?, "");
|
||||
assert_eq!(concat2.call::<_, String>(("ab", "cd"))?, "abcd");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
+109
-13
@@ -1,11 +1,13 @@
|
||||
#![cfg(feature = "luau")]
|
||||
|
||||
use std::env;
|
||||
use std::fmt::Debug;
|
||||
use std::fs;
|
||||
use std::panic::{catch_unwind, AssertUnwindSafe};
|
||||
use std::sync::atomic::{AtomicU64, Ordering};
|
||||
use std::sync::Arc;
|
||||
|
||||
use mlua::{Compiler, Error, Lua, Result, Table, ThreadStatus, Value, VmState};
|
||||
use mlua::{Compiler, CoverageInfo, Error, Lua, Result, Table, ThreadStatus, Value, VmState};
|
||||
|
||||
#[test]
|
||||
fn test_require() -> Result<()> {
|
||||
@@ -15,8 +17,11 @@ fn test_require() -> Result<()> {
|
||||
fs::write(
|
||||
temp_dir.path().join("module.luau"),
|
||||
r#"
|
||||
counter = counter or 0
|
||||
return counter + 1
|
||||
counter = (counter or 0) + 1
|
||||
return {
|
||||
counter = counter,
|
||||
error = function() error("test") end,
|
||||
}
|
||||
"#,
|
||||
)?;
|
||||
|
||||
@@ -24,9 +29,12 @@ fn test_require() -> Result<()> {
|
||||
lua.load(
|
||||
r#"
|
||||
local module = require("module")
|
||||
assert(module == 1)
|
||||
assert(module.counter == 1)
|
||||
module = require("module")
|
||||
assert(module == 1)
|
||||
assert(module.counter == 1)
|
||||
|
||||
local ok, err = pcall(module.error)
|
||||
assert(not ok and string.find(err, "module.luau") ~= nil)
|
||||
"#,
|
||||
)
|
||||
.exec()
|
||||
@@ -69,18 +77,33 @@ fn test_vectors() -> Result<()> {
|
||||
fn test_readonly_table() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let t = lua.create_table()?;
|
||||
let t = lua.create_sequence_from([1])?;
|
||||
assert!(!t.is_readonly());
|
||||
t.set_readonly(true);
|
||||
assert!(t.is_readonly());
|
||||
|
||||
match t.set("key", "value") {
|
||||
Err(Error::RuntimeError(err)) if err.contains("Attempt to modify a readonly table") => {}
|
||||
r => panic!(
|
||||
"expected RuntimeError(...) with a specific message, got {:?}",
|
||||
r
|
||||
),
|
||||
};
|
||||
#[track_caller]
|
||||
fn check_readonly_error<T: Debug>(res: Result<T>) {
|
||||
match res {
|
||||
Err(Error::RuntimeError(e)) if e.contains("attempt to modify a readonly table") => {}
|
||||
r => panic!("expected RuntimeError(...) with a specific message, got {r:?}"),
|
||||
}
|
||||
}
|
||||
|
||||
check_readonly_error(t.set("key", "value"));
|
||||
check_readonly_error(t.raw_set("key", "value"));
|
||||
check_readonly_error(t.raw_insert(1, "value"));
|
||||
check_readonly_error(t.raw_remove(1));
|
||||
check_readonly_error(t.push("value"));
|
||||
check_readonly_error(t.pop::<Value>());
|
||||
check_readonly_error(t.raw_push("value"));
|
||||
check_readonly_error(t.raw_pop::<Value>());
|
||||
|
||||
// Special case
|
||||
match catch_unwind(AssertUnwindSafe(|| t.set_metatable(None))) {
|
||||
Ok(_) => panic!("expected panic, got nothing"),
|
||||
Err(_) => {}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -212,3 +235,76 @@ fn test_interrupts() -> Result<()> {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_coverage() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
lua.set_compiler(Compiler::default().set_coverage_level(1));
|
||||
|
||||
let f = lua
|
||||
.load(
|
||||
r#"local v = vector(1, 2, 3)
|
||||
assert(v.x == 1 and v.y == 2 and v.z == 3)
|
||||
|
||||
function abc(i)
|
||||
if i < 5 then
|
||||
return 0
|
||||
else
|
||||
return 1
|
||||
end
|
||||
end
|
||||
|
||||
(function()
|
||||
(function() abc(10) end)()
|
||||
end)()
|
||||
"#,
|
||||
)
|
||||
.into_function()?;
|
||||
|
||||
f.call(())?;
|
||||
|
||||
let mut report = Vec::new();
|
||||
f.coverage(|cov| {
|
||||
report.push(cov);
|
||||
});
|
||||
|
||||
assert_eq!(
|
||||
report[0],
|
||||
CoverageInfo {
|
||||
function: None,
|
||||
line_defined: 1,
|
||||
depth: 0,
|
||||
hits: vec![-1, 1, 1, -1, 1, -1, -1, -1, -1, -1, -1, -1, 1, -1, -1, -1],
|
||||
}
|
||||
);
|
||||
assert_eq!(
|
||||
report[1],
|
||||
CoverageInfo {
|
||||
function: Some("abc".into()),
|
||||
line_defined: 4,
|
||||
depth: 1,
|
||||
hits: vec![-1, -1, -1, -1, -1, 1, 0, -1, 1, -1, -1, -1, -1, -1, -1, -1],
|
||||
}
|
||||
);
|
||||
assert_eq!(
|
||||
report[2],
|
||||
CoverageInfo {
|
||||
function: None,
|
||||
line_defined: 12,
|
||||
depth: 1,
|
||||
hits: vec![-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, -1, -1],
|
||||
}
|
||||
);
|
||||
assert_eq!(
|
||||
report[3],
|
||||
CoverageInfo {
|
||||
function: None,
|
||||
line_defined: 13,
|
||||
depth: 2,
|
||||
hits: vec![-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, -1, -1],
|
||||
}
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
[target.x86_64-apple-darwin]
|
||||
rustflags = ["-C", "link-args=-rdynamic"]
|
||||
rustflags = [
|
||||
"-C", "link-arg=-undefined",
|
||||
"-C", "link-arg=dynamic_lookup",
|
||||
]
|
||||
|
||||
[target.aarch64-apple-darwin]
|
||||
rustflags = ["-C", "link-args=-rdynamic"]
|
||||
|
||||
[target.x86_64-unknown-linux-gnu]
|
||||
rustflags = ["-C", "link-args=-rdynamic"]
|
||||
rustflags = [
|
||||
"-C", "link-arg=-undefined",
|
||||
"-C", "link-arg=dynamic_lookup",
|
||||
]
|
||||
|
||||
+10
-3
@@ -1,16 +1,23 @@
|
||||
[package]
|
||||
name = "module_runner"
|
||||
name = "rust_module"
|
||||
version = "0.0.0"
|
||||
authors = ["Aleksandr Orlenko <zxteam@pm.me>"]
|
||||
edition = "2018"
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib"]
|
||||
|
||||
[workspace]
|
||||
members = [
|
||||
"loader",
|
||||
]
|
||||
|
||||
[features]
|
||||
lua54 = ["mlua/lua54"]
|
||||
lua53 = ["mlua/lua53"]
|
||||
lua52 = ["mlua/lua52"]
|
||||
lua51 = ["mlua/lua51"]
|
||||
luajit = ["mlua/luajit"]
|
||||
vendored = ["mlua/vendored"]
|
||||
|
||||
[dependencies]
|
||||
mlua = { path = "../.." }
|
||||
mlua = { path = "../..", features = ["module"] }
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
[target.x86_64-apple-darwin]
|
||||
rustflags = ["-C", "link-args=-rdynamic"]
|
||||
|
||||
[target.aarch64-apple-darwin]
|
||||
rustflags = ["-C", "link-args=-rdynamic"]
|
||||
|
||||
[target.x86_64-unknown-linux-gnu]
|
||||
rustflags = ["-C", "link-args=-rdynamic"]
|
||||
@@ -0,0 +1,16 @@
|
||||
[package]
|
||||
name = "module_loader"
|
||||
version = "0.0.0"
|
||||
authors = ["Aleksandr Orlenko <zxteam@pm.me>"]
|
||||
edition = "2018"
|
||||
|
||||
[features]
|
||||
lua54 = ["mlua/lua54"]
|
||||
lua53 = ["mlua/lua53"]
|
||||
lua52 = ["mlua/lua52"]
|
||||
lua51 = ["mlua/lua51"]
|
||||
luajit = ["mlua/luajit"]
|
||||
vendored = ["mlua/vendored"]
|
||||
|
||||
[dependencies]
|
||||
mlua = { path = "../../.." }
|
||||
@@ -0,0 +1,39 @@
|
||||
use mlua::prelude::*;
|
||||
|
||||
fn sum(_: &Lua, (a, b): (i64, i64)) -> LuaResult<i64> {
|
||||
Ok(a + b)
|
||||
}
|
||||
|
||||
fn used_memory(lua: &Lua, _: ()) -> LuaResult<usize> {
|
||||
Ok(lua.used_memory())
|
||||
}
|
||||
|
||||
fn check_userdata(_: &Lua, ud: MyUserData) -> LuaResult<i32> {
|
||||
Ok(ud.0)
|
||||
}
|
||||
|
||||
#[mlua::lua_module]
|
||||
fn rust_module(lua: &Lua) -> LuaResult<LuaTable> {
|
||||
let exports = lua.create_table()?;
|
||||
exports.set("sum", lua.create_function(sum)?)?;
|
||||
exports.set("used_memory", lua.create_function(used_memory)?)?;
|
||||
exports.set("check_userdata", lua.create_function(check_userdata)?)?;
|
||||
Ok(exports)
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
struct MyUserData(i32);
|
||||
|
||||
impl LuaUserData for MyUserData {}
|
||||
|
||||
#[mlua::lua_module]
|
||||
fn rust_module_second(lua: &Lua) -> LuaResult<LuaTable> {
|
||||
let exports = lua.create_table()?;
|
||||
exports.set("userdata", lua.create_userdata(MyUserData(123))?)?;
|
||||
Ok(exports)
|
||||
}
|
||||
|
||||
#[mlua::lua_module]
|
||||
fn rust_module_error(_: &Lua) -> LuaResult<LuaTable> {
|
||||
Err("custom module error".to_lua_err())
|
||||
}
|
||||
@@ -375,6 +375,19 @@ fn test_from_value_struct() -> Result<(), Box<dyn std::error::Error>> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_from_value_newtype_struct() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let lua = Lua::new();
|
||||
|
||||
#[derive(Deserialize, PartialEq, Debug)]
|
||||
struct Test(f64);
|
||||
|
||||
let got = lua.from_value(Value::Number(123.456))?;
|
||||
assert_eq!(Test(123.456), got);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_from_value_enum() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let lua = Lua::new();
|
||||
|
||||
@@ -109,6 +109,49 @@ fn test_table() -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_table_push_pop() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
// Test raw access
|
||||
let table1 = lua.create_sequence_from(vec![123])?;
|
||||
table1.raw_push(321)?;
|
||||
assert_eq!(
|
||||
table1
|
||||
.clone()
|
||||
.raw_sequence_values::<i64>()
|
||||
.collect::<Result<Vec<_>>>()?,
|
||||
vec![123, 321]
|
||||
);
|
||||
assert_eq!(table1.raw_pop::<i64>()?, 321);
|
||||
assert_eq!(table1.raw_pop::<i64>()?, 123);
|
||||
assert_eq!(table1.raw_pop::<Value>()?, Value::Nil); // An extra pop should do nothing
|
||||
assert_eq!(table1.raw_len(), 0);
|
||||
|
||||
// Test access through metamethods
|
||||
let table2 = lua
|
||||
.load(
|
||||
r#"
|
||||
local proxy_table = {234}
|
||||
table2 = setmetatable({}, {
|
||||
__len = function() return #proxy_table end,
|
||||
__index = proxy_table,
|
||||
__newindex = proxy_table,
|
||||
})
|
||||
return table2
|
||||
"#,
|
||||
)
|
||||
.eval::<Table>()?;
|
||||
table2.push(345)?;
|
||||
assert_eq!(table2.len()?, 2);
|
||||
assert_eq!(table2.pop::<i64>()?, 345);
|
||||
assert_eq!(table2.pop::<i64>()?, 234);
|
||||
assert_eq!(table2.pop::<Value>()?, Value::Nil);
|
||||
assert_eq!(table2.len()?, 0);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_table_sequence_from() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
+55
-2
@@ -73,6 +73,7 @@ fn test_load() -> Result<()> {
|
||||
let result: i32 = func.call(())?;
|
||||
assert_eq!(result, 3);
|
||||
|
||||
assert!(lua.load("").exec().is_ok());
|
||||
assert!(lua.load("§$%§&$%&").exec().is_err());
|
||||
|
||||
Ok(())
|
||||
@@ -275,7 +276,10 @@ fn test_error() -> Result<()> {
|
||||
end, 3)
|
||||
|
||||
local function handler(err)
|
||||
if string.match(_VERSION, ' 5%.1$') or string.match(_VERSION, ' 5%.2$') or _VERSION == "Luau" then
|
||||
if string.match(_VERSION, " 5%.1$")
|
||||
or string.match(_VERSION, " 5%.2$")
|
||||
or string.match(_VERSION, "Luau")
|
||||
then
|
||||
-- Special case for Lua 5.1/5.2 and Luau
|
||||
local caps = string.match(err, ': (%d+)$')
|
||||
if caps then
|
||||
@@ -560,7 +564,12 @@ fn test_num_conversion() -> Result<()> {
|
||||
assert_eq!(lua.load("1.0").eval::<f64>()?, 1.0);
|
||||
#[cfg(any(feature = "lua54", feature = "lua53"))]
|
||||
assert_eq!(lua.load("1.0").eval::<String>()?, "1.0");
|
||||
#[cfg(any(feature = "lua52", feature = "lua51", feature = "luajit"))]
|
||||
#[cfg(any(
|
||||
feature = "lua52",
|
||||
feature = "lua51",
|
||||
feature = "luajit",
|
||||
feature = "luau"
|
||||
))]
|
||||
assert_eq!(lua.load("1.0").eval::<String>()?, "1");
|
||||
|
||||
assert_eq!(lua.load("1.5").eval::<i64>()?, 1);
|
||||
@@ -787,6 +796,17 @@ fn test_replace_registry_value() -> Result<()> {
|
||||
let key = lua.create_registry_value::<i32>(42)?;
|
||||
lua.replace_registry_value(&key, "new value")?;
|
||||
assert_eq!(lua.registry_value::<String>(&key)?, "new value");
|
||||
lua.replace_registry_value(&key, Value::Nil)?;
|
||||
assert_eq!(lua.registry_value::<Value>(&key)?, Value::Nil);
|
||||
lua.replace_registry_value(&key, 123)?;
|
||||
assert_eq!(lua.registry_value::<i32>(&key)?, 123);
|
||||
|
||||
// It should be impossible to replace (initial) nil value with non-nil
|
||||
let key2 = lua.create_registry_value(Value::Nil)?;
|
||||
match lua.replace_registry_value(&key2, "abc") {
|
||||
Err(Error::RuntimeError(_)) => {}
|
||||
r => panic!("expected RuntimeError, got {r:?}"),
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -838,6 +858,28 @@ fn test_mismatched_registry_key() -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_registry_value_reuse() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let r1 = lua.create_registry_value("value1")?;
|
||||
let r1_slot = format!("{r1:?}");
|
||||
drop(r1);
|
||||
|
||||
// Previous slot must not be reused by nil value
|
||||
let r2 = lua.create_registry_value(Value::Nil)?;
|
||||
let r2_slot = format!("{r2:?}");
|
||||
assert_ne!(r1_slot, r2_slot);
|
||||
drop(r2);
|
||||
|
||||
// But should be reused by non-nil value
|
||||
let r3 = lua.create_registry_value("value3")?;
|
||||
let r3_slot = format!("{r3:?}");
|
||||
assert_eq!(r1_slot, r3_slot);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_application_data() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
@@ -1272,3 +1314,14 @@ fn test_luajit_cdata() {
|
||||
)
|
||||
.eval();
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "send")]
|
||||
fn test_send() {
|
||||
let lua = Lua::new();
|
||||
std::thread::spawn(move || {
|
||||
let _lua = lua;
|
||||
})
|
||||
.join()
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
@@ -656,3 +656,44 @@ fn test_userdata_wrapped() -> Result<()> {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_userdata_proxy() -> Result<()> {
|
||||
struct MyUserData(i64);
|
||||
|
||||
impl UserData for MyUserData {
|
||||
fn add_fields<'lua, F: UserDataFields<'lua, Self>>(fields: &mut F) {
|
||||
fields.add_field_function_get("static_field", |_, _| Ok(123));
|
||||
fields.add_field_method_get("n", |_, this| Ok(this.0));
|
||||
}
|
||||
|
||||
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
|
||||
methods.add_function("new", |_, n| Ok(Self(n)));
|
||||
|
||||
methods.add_method("plus", |_, this, n: i64| Ok(this.0 + n));
|
||||
}
|
||||
}
|
||||
|
||||
let lua = Lua::new();
|
||||
let globals = lua.globals();
|
||||
globals.set("MyUserData", lua.create_proxy::<MyUserData>()?)?;
|
||||
|
||||
lua.load(
|
||||
r#"
|
||||
assert(MyUserData.static_field == 123)
|
||||
local data = MyUserData.new(321)
|
||||
assert(data.static_field == 123)
|
||||
assert(data.n == 321)
|
||||
assert(data:plus(1) == 322)
|
||||
|
||||
-- Error when accessing the proxy object fields and methods that require instance
|
||||
|
||||
local ok = pcall(function() return MyUserData.n end)
|
||||
assert(not ok)
|
||||
|
||||
ok = pcall(function() return MyUserData:plus(1) end)
|
||||
assert(not ok)
|
||||
"#,
|
||||
)
|
||||
.exec()
|
||||
}
|
||||
|
||||
+31
-5
@@ -1,4 +1,6 @@
|
||||
use mlua::{Lua, Result, Value};
|
||||
use std::ptr;
|
||||
|
||||
use mlua::{Lua, MultiValue, Result, Value};
|
||||
|
||||
#[test]
|
||||
fn test_value_eq() -> Result<()> {
|
||||
@@ -41,17 +43,41 @@ fn test_value_eq() -> Result<()> {
|
||||
let thread2: Value = globals.get("thread2")?;
|
||||
|
||||
assert!(table1 != table2);
|
||||
assert!(table1.equals(table2)?);
|
||||
assert!(table1.equals(&table2)?);
|
||||
assert!(string1 == string2);
|
||||
assert!(string1.equals(string2)?);
|
||||
assert!(string1.equals(&string2)?);
|
||||
assert!(num1 == num2);
|
||||
assert!(num1.equals(num2)?);
|
||||
assert!(num1 != num3);
|
||||
assert!(func1 == func2);
|
||||
assert!(func1 != func3);
|
||||
assert!(!func1.equals(func3)?);
|
||||
assert!(!func1.equals(&func3)?);
|
||||
assert!(thread1 == thread2);
|
||||
assert!(thread1.equals(thread2)?);
|
||||
assert!(thread1.equals(&thread2)?);
|
||||
|
||||
assert!(!table1.to_pointer().is_null());
|
||||
assert!(!ptr::eq(table1.to_pointer(), table2.to_pointer()));
|
||||
assert!(ptr::eq(string1.to_pointer(), string2.to_pointer()));
|
||||
assert!(ptr::eq(func1.to_pointer(), func2.to_pointer()));
|
||||
assert!(num1.to_pointer().is_null());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_multi_value() {
|
||||
let mut multi_value = MultiValue::new();
|
||||
assert_eq!(multi_value.len(), 0);
|
||||
assert_eq!(multi_value.get(0), None);
|
||||
|
||||
multi_value.push_front(Value::Number(2.));
|
||||
multi_value.push_front(Value::Number(1.));
|
||||
assert_eq!(multi_value.get(0), Some(&Value::Number(1.)));
|
||||
assert_eq!(multi_value.get(1), Some(&Value::Number(2.)));
|
||||
|
||||
assert_eq!(multi_value.pop_front(), Some(Value::Number(1.)));
|
||||
assert_eq!(multi_value[0], Value::Number(2.));
|
||||
|
||||
multi_value.clear();
|
||||
assert!(multi_value.is_empty());
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user