mirror of
https://github.com/mlua-rs/mlua
synced 2026-06-08 16:05:43 +00:00
Compare commits
325 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 3516f4c6ca | |||
| ca73583714 | |||
| 36560435f7 | |||
| 763c2b2564 | |||
| bafdb6138c | |||
| c9d6a610e1 | |||
| bd63f63bc9 | |||
| c035c23a15 | |||
| cb153a52b2 | |||
| b1c69d3005 | |||
| 841bd332e4 | |||
| 815d1bd7c9 | |||
| 78331ceebe | |||
| f945a35cbd | |||
| 459edb6816 | |||
| 00328b0b64 | |||
| 928d94d255 | |||
| 583c35a172 | |||
| 1791c599f4 | |||
| 1e48817a64 | |||
| 8d219503dd | |||
| 95367855c1 | |||
| 49389c4aa4 | |||
| 13dc2b5352 | |||
| 7afbf74128 | |||
| 06c3bd9d69 | |||
| 1ddaea60ce | |||
| a653d08768 | |||
| 2b6b0144a1 | |||
| 4cfe0be945 | |||
| a9a4814c3c | |||
| b1f73ec29d | |||
| 61a2141151 | |||
| dea38f27a5 | |||
| 04aaa18dc8 | |||
| d8455c038a | |||
| ca22ea3be7 | |||
| cf05593d66 | |||
| d011a1f851 | |||
| 1ec4661bf9 | |||
| d3b2999d2f | |||
| 72f6536efb | |||
| 646827a6bb | |||
| 1882931cd9 | |||
| c90cac5189 | |||
| c0d839d8d2 | |||
| 80471c6dad | |||
| 4b9d1cf271 | |||
| 55c07f3b28 | |||
| ef4eabd327 | |||
| a3302afdc1 | |||
| dfb4e9a668 | |||
| 92db0f6d3a | |||
| a3697ab1db | |||
| 052740db15 | |||
| faf547c154 | |||
| 0de7cd1c7d | |||
| 58953e5635 | |||
| 6406de405d | |||
| 727096dd3b | |||
| 2445230759 | |||
| 1cd2bdc808 | |||
| f8ed33a2aa | |||
| 25955893e0 | |||
| 3f0c69b70b | |||
| 9da98d42c7 | |||
| f539f60987 | |||
| aa187e6663 | |||
| ec10bf2a39 | |||
| 3ea80b763d | |||
| 2e0e86dab2 | |||
| 0ac7aebb27 | |||
| 634e5d4550 | |||
| 05d6c20520 | |||
| 63e7cfd31b | |||
| 9c24c99cbe | |||
| 62f84828f2 | |||
| 7bc72be7d3 | |||
| 2fbbbe4238 | |||
| f00208373e | |||
| a2dc662a92 | |||
| caeac2e9a3 | |||
| 3d5261640d | |||
| b57a6239a6 | |||
| 031424f6ce | |||
| c4c9609ac6 | |||
| 6fcd18e434 | |||
| 39cac5699a | |||
| e6e1ef014f | |||
| 942a443592 | |||
| 65e292dac4 | |||
| 1a82f83644 | |||
| 38fbd08c72 | |||
| b3854d2f1d | |||
| 00a56b115b | |||
| 2fefaafaa6 | |||
| 76a8f8cc71 | |||
| 13395e9c3d | |||
| c61219dd93 | |||
| 2e7c654cfe | |||
| f36aaa5ce1 | |||
| df38878278 | |||
| c5c1fe3b85 | |||
| 0cc4b15f6b | |||
| f16aca687d | |||
| 9b45663afd | |||
| c0c802262e | |||
| dda344bfc6 | |||
| 93adfa42d1 | |||
| 5dc6c3214b | |||
| da08f7210f | |||
| 8b7a85076a | |||
| a4ff68a120 | |||
| 5454cfa5ed | |||
| f9d9dc74a7 | |||
| ed796fff51 | |||
| 65e5be81ba | |||
| 4fa8e645b7 | |||
| 05a0abb8c2 | |||
| f0c0527c2b | |||
| 2490dfeb38 | |||
| 4a3dbafb6e | |||
| 97ca3e2c08 | |||
| d6fb328660 | |||
| edd508ed1f | |||
| a30a736291 | |||
| 8e1df48e81 | |||
| efd192b816 | |||
| 5e1451a465 | |||
| 64b1d152b9 | |||
| a8a4aa8c93 | |||
| 9fda2ecfcc | |||
| 21fc924457 | |||
| 30d5e08a8a | |||
| 560a30ca02 | |||
| 117f8377e2 | |||
| 8173ef2fa8 | |||
| 4e5677ae54 | |||
| 49958f4827 | |||
| 39b3af2ff2 | |||
| b805939320 | |||
| bd13300288 | |||
| 12e61e01f6 | |||
| 53ff494ab9 | |||
| 1cb081d20a | |||
| c1a018a60d | |||
| 4d2ff45a8b | |||
| 72682198eb | |||
| 87fa663ac1 | |||
| 1f970824b4 | |||
| 8d864e2687 | |||
| b0140cdb9e | |||
| 806817212e | |||
| 95c623e94d | |||
| 0a1a1fe0b6 | |||
| 4fe7d151a1 | |||
| edbf1e9150 | |||
| 69011a89d2 | |||
| 0ed11e4134 | |||
| 0ce599aff2 | |||
| 1ebb4b468a | |||
| 5ab97666d7 | |||
| e706ae4fcb | |||
| ae7cdcb934 | |||
| dacddfa967 | |||
| a01b032c35 | |||
| 18497f1528 | |||
| 28e8f56989 | |||
| c1f8abba9e | |||
| d1a587f49a | |||
| 5bbd23ed1a | |||
| 74b4601b48 | |||
| a89800b949 | |||
| 2b6172ef38 | |||
| 20f7ce097d | |||
| 6882e0434e | |||
| fca38a6637 | |||
| cdd6a99136 | |||
| 21d39a069d | |||
| 58965c6255 | |||
| 8574682ffc | |||
| 47bc372096 | |||
| d376cb9403 | |||
| ea5ecccf02 | |||
| cf71edc492 | |||
| 9caf3542d9 | |||
| cb45db05fa | |||
| d1cb2a9a96 | |||
| aa3f6ba46c | |||
| cc57bed4c8 | |||
| b5d38ab2e3 | |||
| cd4091f64d | |||
| 91e069a77e | |||
| cacd3dc70f | |||
| 6f6cda0099 | |||
| d51ce86142 | |||
| aa061bce6f | |||
| 1c6b6ad801 | |||
| 031854fa2a | |||
| 5fd96c7908 | |||
| 7a3f19b857 | |||
| 55a5d7ef10 | |||
| af31dbd180 | |||
| 9ae3cb0a7c | |||
| fc1c80c142 | |||
| ee7ced6334 | |||
| d8307d0e4c | |||
| bf9fcc5aca | |||
| 7ce6b97da9 | |||
| 4891a6ac10 | |||
| 4ef0d583fc | |||
| 30b0122f5d | |||
| c31c72076f | |||
| 3bfaee4ecc | |||
| 89b68e2a24 | |||
| cbf805f492 | |||
| c926327a6a | |||
| 7c099500d0 | |||
| 8c889cc353 | |||
| 958abd050e | |||
| 92a8203e1c | |||
| b34b90eca3 | |||
| 7aad0adcb4 | |||
| a3cd25db7a | |||
| a4bfeb7752 | |||
| 0fda512938 | |||
| 58e0661086 | |||
| c7094d470f | |||
| a7d0691e10 | |||
| 05778fbe6f | |||
| b34d67ec41 | |||
| 46ee7ea772 | |||
| 15738dda1f | |||
| 1f32754f05 | |||
| c2eab173c5 | |||
| bb311349ec | |||
| 928e1d9221 | |||
| 5b8681dcf2 | |||
| 4e9a17707b | |||
| 6066089cc1 | |||
| a8d5f23818 | |||
| 5ec4e0338a | |||
| d27d1365b5 | |||
| 76b896edcc | |||
| ddebf56b41 | |||
| 4f56575e05 | |||
| 35fa76263e | |||
| 446d63a77e | |||
| 8d8d521721 | |||
| 0d31a1caa6 | |||
| 3dc58cdfc9 | |||
| 5724b5f112 | |||
| d64d9719c6 | |||
| 75475fc9a8 | |||
| f8fe9246bb | |||
| 5c54361236 | |||
| ec227f9056 | |||
| 93a1a55aaa | |||
| a020b2b5b2 | |||
| 2c756e5958 | |||
| c702077028 | |||
| 08545224f4 | |||
| e122f90837 | |||
| c638d90b02 | |||
| 930fd9c00f | |||
| 2a8db87132 | |||
| cbae4fe59c | |||
| c68e3c4f41 | |||
| 02d4ceff34 | |||
| 98339c57e6 | |||
| 2331995e28 | |||
| c07bdce250 | |||
| 084a85c3d8 | |||
| 735aa22be9 | |||
| 5479546b27 | |||
| 179c54f297 | |||
| f9ae4bf05f | |||
| 9e16e18132 | |||
| 7535a23fa2 | |||
| 3787ff9e8c | |||
| 0a2a70c15a | |||
| 81d7c81532 | |||
| 0453029765 | |||
| 7b777d074e | |||
| c6cd1c53c3 | |||
| 669349d704 | |||
| c086c144d0 | |||
| 640cb2c182 | |||
| 03a4068d55 | |||
| 9f6c78532f | |||
| 8aecc83f53 | |||
| 4891b6535c | |||
| fa343c2c69 | |||
| 6d5e735bed | |||
| ac315fd80b | |||
| f95161c6e0 | |||
| 4bc846a119 | |||
| a3ca95fc8f | |||
| ae4897ab2e | |||
| 4ac87c7208 | |||
| 04d8106676 | |||
| b6cdf32f16 | |||
| ad9bc36764 | |||
| 4b8c26e682 | |||
| 7839c4438c | |||
| 529361fcbc | |||
| 4dddf3c18d | |||
| fb0c0d9ee9 | |||
| 235c32006c | |||
| b65901e444 | |||
| 91fe02da45 | |||
| 8274b5fa88 | |||
| 762e677a70 | |||
| 5b5f1e4669 | |||
| e582e7c57f | |||
| 3714da5ec8 | |||
| ca69be07ff | |||
| 16951e3628 | |||
| 8bb2b444ab | |||
| fc1570d2d7 | |||
| fce85381c6 | |||
| 3088516851 | |||
| 5162a0f46e | |||
| 640d27697d | |||
| da4404baa5 |
@@ -6,18 +6,18 @@ jobs:
|
||||
name: coverage
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: xd009642/tarpaulin
|
||||
image: xd009642/tarpaulin:develop-nightly
|
||||
options: --security-opt seccomp=unconfined
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@main
|
||||
|
||||
- name: Generate coverage report
|
||||
run: |
|
||||
cargo tarpaulin --out xml --tests --exclude-files benches/* --exclude-files mlua-sys/src/*/*
|
||||
cargo +nightly tarpaulin --verbose --out xml --tests --exclude-files benches/* --exclude-files mlua-sys/src/*/*
|
||||
|
||||
- name: Upload report to codecov.io
|
||||
uses: codecov/codecov-action@v3
|
||||
uses: codecov/codecov-action@v4
|
||||
with:
|
||||
token: ${{secrets.CODECOV_TOKEN}}
|
||||
fail_ci_if_error: false
|
||||
|
||||
+65
-39
@@ -7,18 +7,18 @@ jobs:
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-22.04, macos-latest, windows-latest]
|
||||
os: [ubuntu-latest, macos-latest, windows-latest]
|
||||
rust: [stable]
|
||||
lua: [lua54, lua53, lua52, lua51, luajit, luau, luau-jit, luau-vector4]
|
||||
include:
|
||||
- os: ubuntu-22.04
|
||||
- os: ubuntu-latest
|
||||
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@v4
|
||||
- uses: actions/checkout@main
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
toolchain: ${{ matrix.rust }}
|
||||
@@ -27,11 +27,11 @@ jobs:
|
||||
- name: Build ${{ matrix.lua }} vendored
|
||||
run: |
|
||||
cargo build --features "${{ matrix.lua }},vendored"
|
||||
cargo build --features "${{ matrix.lua }},vendored,async,serialize,macros"
|
||||
cargo build --features "${{ matrix.lua }},vendored,async,serialize,macros,send"
|
||||
cargo build --features "${{ matrix.lua }},vendored,async,serde,macros,anyhow,userdata-wrappers"
|
||||
cargo build --features "${{ matrix.lua }},vendored,async,serde,macros,anyhow,userdata-wrappers,send"
|
||||
shell: bash
|
||||
- name: Build ${{ matrix.lua }} pkg-config
|
||||
if: ${{ matrix.os == 'ubuntu-22.04' }}
|
||||
if: ${{ matrix.os == 'ubuntu-latest' }}
|
||||
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
|
||||
@@ -45,23 +45,23 @@ jobs:
|
||||
matrix:
|
||||
lua: [lua54, lua53, lua52, lua51, luajit]
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@main
|
||||
- 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"
|
||||
run: cargo build --target aarch64-apple-darwin --features "${{ matrix.lua }},vendored,async,send,serde,macros,anyhow,userdata-wrappers"
|
||||
|
||||
build_aarch64_cross_ubuntu:
|
||||
name: Cross-compile to aarch64-unknown-linux-gnu
|
||||
runs-on: ubuntu-22.04
|
||||
runs-on: ubuntu-latest
|
||||
needs: build
|
||||
strategy:
|
||||
matrix:
|
||||
lua: [lua54, lua53, lua52, lua51, luajit]
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@main
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
toolchain: stable
|
||||
@@ -72,18 +72,18 @@ jobs:
|
||||
sudo apt-get install -y --no-install-recommends gcc-aarch64-linux-gnu libc6-dev-arm64-cross
|
||||
shell: bash
|
||||
- name: Cross-compile
|
||||
run: cargo build --target aarch64-unknown-linux-gnu --features "${{ matrix.lua }},vendored,async,send,serialize,macros"
|
||||
run: cargo build --target aarch64-unknown-linux-gnu --features "${{ matrix.lua }},vendored,async,send,serde,macros,anyhow,userdata-wrappers"
|
||||
shell: bash
|
||||
|
||||
build_armv7_cross_ubuntu:
|
||||
name: Cross-compile to armv7-unknown-linux-gnueabihf
|
||||
runs-on: ubuntu-22.04
|
||||
runs-on: ubuntu-latest
|
||||
needs: build
|
||||
strategy:
|
||||
matrix:
|
||||
lua: [lua54, lua53, lua52, lua51]
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@main
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
toolchain: stable
|
||||
@@ -94,7 +94,7 @@ jobs:
|
||||
sudo apt-get install -y --no-install-recommends gcc-arm-linux-gnueabihf libc-dev-armhf-cross
|
||||
shell: bash
|
||||
- name: Cross-compile
|
||||
run: cargo build --target armv7-unknown-linux-gnueabihf --features "${{ matrix.lua }},vendored,async,send,serialize,macros"
|
||||
run: cargo build --target armv7-unknown-linux-gnueabihf --features "${{ matrix.lua }},vendored,async,send,serde,macros,anyhow,userdata-wrappers"
|
||||
shell: bash
|
||||
|
||||
test:
|
||||
@@ -103,18 +103,18 @@ jobs:
|
||||
needs: build
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-22.04, macos-latest, windows-latest]
|
||||
os: [ubuntu-latest, macos-latest, windows-latest]
|
||||
rust: [stable, nightly]
|
||||
lua: [lua54, lua53, lua52, lua51, luajit, luajit52, luau, luau-jit, luau-vector4]
|
||||
include:
|
||||
- os: ubuntu-22.04
|
||||
- os: ubuntu-latest
|
||||
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@v4
|
||||
- uses: actions/checkout@main
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
toolchain: ${{ matrix.rust }}
|
||||
@@ -123,14 +123,14 @@ jobs:
|
||||
- name: Run ${{ matrix.lua }} tests
|
||||
run: |
|
||||
cargo test --features "${{ matrix.lua }},vendored"
|
||||
cargo test --features "${{ matrix.lua }},vendored,async,serialize,macros"
|
||||
cargo test --features "${{ matrix.lua }},vendored,async,serialize,macros,send"
|
||||
cargo test --features "${{ matrix.lua }},vendored,async,serde,macros,anyhow,userdata-wrappers"
|
||||
cargo test --features "${{ matrix.lua }},vendored,async,serde,macros,anyhow,userdata-wrappers,send"
|
||||
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" -- --ignored
|
||||
TRYBUILD=overwrite cargo test --features "${{ matrix.lua }},vendored" --tests -- --ignored
|
||||
TRYBUILD=overwrite cargo test --features "${{ matrix.lua }},vendored,async,send,serde,macros" --tests -- --ignored
|
||||
shell: bash
|
||||
|
||||
test_with_sanitizer:
|
||||
@@ -139,14 +139,14 @@ jobs:
|
||||
needs: build
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-22.04]
|
||||
os: [ubuntu-latest]
|
||||
rust: [nightly]
|
||||
lua: [lua54, lua53, lua52, lua51, luajit, luau, luau-jit, luau-vector4]
|
||||
include:
|
||||
- os: ubuntu-22.04
|
||||
- os: ubuntu-latest
|
||||
target: x86_64-unknown-linux-gnu
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@main
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
toolchain: ${{ matrix.rust }}
|
||||
@@ -154,28 +154,54 @@ jobs:
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
- name: Run ${{ matrix.lua }} tests with address sanitizer
|
||||
run: |
|
||||
cargo test --tests --features "${{ matrix.lua }},vendored,async,serialize,macros" --target x86_64-unknown-linux-gnu -- --skip test_too_many_recursions
|
||||
cargo test --tests --features "${{ matrix.lua }},vendored,async,serialize,macros,send" --target x86_64-unknown-linux-gnu -- --skip test_too_many_recursions
|
||||
cargo test --tests --features "${{ matrix.lua }},vendored,async,serde,macros,anyhow" --target x86_64-unknown-linux-gnu -- --skip test_too_many_recursions
|
||||
cargo test --tests --features "${{ matrix.lua }},vendored,async,serde,macros,anyhow,userdata-wrappers,send" --target x86_64-unknown-linux-gnu -- --skip test_too_many_recursions
|
||||
shell: bash
|
||||
env:
|
||||
RUSTFLAGS: -Z sanitizer=address
|
||||
|
||||
test_with_memory_limit:
|
||||
name: Test with memory limit
|
||||
runs-on: ${{ matrix.os }}
|
||||
needs: build
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest]
|
||||
rust: [nightly]
|
||||
lua: [lua54, lua53, lua52, lua51, luajit, luau, luau-jit, luau-vector4]
|
||||
include:
|
||||
- os: ubuntu-latest
|
||||
target: x86_64-unknown-linux-gnu
|
||||
steps:
|
||||
- uses: actions/checkout@main
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
toolchain: ${{ matrix.rust }}
|
||||
target: ${{ matrix.target }}
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
- name: Run ${{ matrix.lua }} tests with forced memory limit
|
||||
run: |
|
||||
cargo test --tests --features "${{ matrix.lua }},vendored,async,send,serde,macros,anyhow,userdata-wrappers"
|
||||
shell: bash
|
||||
env:
|
||||
RUSTFLAGS: --cfg=force_memory_limit
|
||||
|
||||
test_modules:
|
||||
name: Test modules
|
||||
runs-on: ${{ matrix.os }}
|
||||
needs: build
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-22.04, macos-latest]
|
||||
os: [ubuntu-latest, macos-latest]
|
||||
rust: [stable]
|
||||
lua: [lua54, lua53, lua52, lua51, luajit, luau]
|
||||
lua: [lua54, lua53, lua52, lua51, luajit]
|
||||
include:
|
||||
- os: ubuntu-22.04
|
||||
- os: ubuntu-latest
|
||||
target: x86_64-unknown-linux-gnu
|
||||
- os: macos-latest
|
||||
target: x86_64-apple-darwin
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@main
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
toolchain: ${{ matrix.rust }}
|
||||
@@ -199,7 +225,7 @@ jobs:
|
||||
shell: msys2 {0}
|
||||
steps:
|
||||
- uses: msys2/setup-msys2@v2
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@main
|
||||
- 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
|
||||
@@ -210,13 +236,13 @@ jobs:
|
||||
|
||||
test_wasm32_emscripten:
|
||||
name: Test on wasm32-unknown-emscripten
|
||||
runs-on: ubuntu-22.04
|
||||
runs-on: ubuntu-latest
|
||||
needs: build
|
||||
strategy:
|
||||
matrix:
|
||||
lua: [lua54, lua53, lua52, lua51, luau]
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@main
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
toolchain: stable
|
||||
@@ -228,13 +254,13 @@ jobs:
|
||||
- name: Run ${{ matrix.lua }} tests
|
||||
run: |
|
||||
cargo test --tests --features "${{ matrix.lua }},vendored"
|
||||
cargo test --tests --features "${{ matrix.lua }},vendored,async,serialize,macros"
|
||||
cargo test --tests --features "${{ matrix.lua }},vendored,async,serde,macros,anyhow,userdata-wrappers"
|
||||
|
||||
rustfmt:
|
||||
name: Rustfmt
|
||||
runs-on: ubuntu-22.04
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@main
|
||||
- uses: dtolnay/rust-toolchain@nightly
|
||||
with:
|
||||
components: rustfmt
|
||||
@@ -242,12 +268,12 @@ jobs:
|
||||
|
||||
clippy:
|
||||
name: Clippy
|
||||
runs-on: ubuntu-22.04
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
lua: [lua54, lua53, lua52, lua51, luajit, luau, luau-jit, luau-vector4]
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@main
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
toolchain: nightly
|
||||
@@ -255,4 +281,4 @@ jobs:
|
||||
- uses: giraffate/clippy-action@v1
|
||||
with:
|
||||
reporter: 'github-pr-review'
|
||||
clippy_flags: --features "${{ matrix.lua }},vendored,async,send,serialize,macros"
|
||||
clippy_flags: --features "${{ matrix.lua }},vendored,async,send,serde,macros,anyhow,userdata-wrappers"
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
name: Typos Check
|
||||
on:
|
||||
pull_request:
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
run:
|
||||
name: Spell Check with Typos
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout Actions Repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Check spelling
|
||||
uses: crate-ci/typos@master
|
||||
with:
|
||||
config: ./typos.toml
|
||||
+134
-4
@@ -1,3 +1,133 @@
|
||||
## v0.11.2 (Aug 10, 2025)
|
||||
|
||||
- Faster stack push for `Variadic<T>`
|
||||
- Fix handling Windows paths with drive letter in Luau require (#623)
|
||||
- Make Luau registered aliases ascii case-insensitive (#620)
|
||||
- Fix deserializing negative zeros `-0.0` (#618)
|
||||
|
||||
## v0.11.1 (Jul 15, 2025)
|
||||
|
||||
- Fixed bug exhausting Lua auxiliary stack and leaving it without reserve (#615)
|
||||
- `Lua::push_c_function` now correctly handles OOM for Lua 5.1 and Luau
|
||||
|
||||
## v0.11.0 (Jul 14, 2025)
|
||||
|
||||
Changes since v0.11.0-beta.3
|
||||
|
||||
- Allow linking external Lua libraries in a build script (e.g. pluto) using `external` mlua-sys feature flag
|
||||
- `Lua::inspect_stack` takes a callback with `&Debug` argument, instead of returning `Debug` directly
|
||||
- Added `Debug::function` method to get function running at a given level
|
||||
- `Debug::curr_line` is deprecated in favour of `Debug::current_line` that returns `Option<usize>`
|
||||
- Added `Lua::set_globals` method to replace global environment
|
||||
- `Table::set_metatable` now returns `Result<()>` (this operation can fail in sandboxed Luau mode)
|
||||
- `impl ToString` replaced with `Into<StdString>` in `UserData` registration
|
||||
- `Value::as_str` and `Value::as_string_lossy` methods are deprecated (as they are non-idiomatic)
|
||||
- Bugfixes and improvements
|
||||
|
||||
## v0.11.0-beta.3 (Jun 23, 2025)
|
||||
|
||||
- Luau in sandboxed mode has reduced options in `collectgarbage` function (to follow the official doc)
|
||||
- `Function::deep_clone` now returns `Result<Function>` as this operation can trigger memory errors
|
||||
- Luau "Require" resolves included Lua files relative to the current directory (#605)
|
||||
- Fixed bug when finalizing `AsyncThread` on drop (`call_async` methods family)
|
||||
|
||||
## v0.11.0-beta.2 (Jun 12, 2025)
|
||||
|
||||
- Lua 5.4 updated to 5.4.8
|
||||
- Terminate Rust `Future` when `AsyncThread` is dropped (without relying on Lua GC)
|
||||
- Added `loadstring` function to Luau
|
||||
- Make `AsChunk` trait dyn-friendly
|
||||
- Luau `Require` trait synced with Luau 0.674
|
||||
- Luau `Require` trait methods now can return `Error` variant (in `NavigateError` enum)
|
||||
- Added `__type` to `Error`'s userdata metatable (for `typeof` function)
|
||||
- `parking_log/send_guard` is moved to `userdata-wrappers` feature flag
|
||||
- New `serde` feature flag to replace `serialize` (the old one is still available)
|
||||
|
||||
## v0.11.0-beta.1 (May 7th, 2025)
|
||||
|
||||
- New "require-by-string" for Luau (with `Require` trait and async support)
|
||||
- Added `Thread::resume_error` support for Luau
|
||||
- 52 bit integers support for Luau (this is a breaking change)
|
||||
- New features for Luau compiler (constants, disabled builtins, known members)
|
||||
- `AsyncThread<A, R>` changed to `AsyncThread<R>` (`A` pushed to stack immediately)
|
||||
- Lifetime `'a` moved from `AsChunk<'a>` to `AsChunk::source where Self: 'a`
|
||||
- `Lua::scope` pass `&Scope` instead of `&mut Scope` to closure
|
||||
- Added global hooks support (Lua 5.1+)
|
||||
- Added per-thread hooks support (Lua 5.1+)
|
||||
- `Lua::init_from_ptr` renamed to `Lua::get_or_init_from_ptr` and returns `&Lua`
|
||||
- `Lua:load_from_function` is deprecated (this is `register_module` now)
|
||||
- Added `Lua::register_module` and `Lua::preload_module`
|
||||
|
||||
## v0.10.4 (May 5th, 2025)
|
||||
|
||||
- Luau updated to 0.672
|
||||
- New serde option `encode_empty_tables_as_array` to serialize empty tables as arrays
|
||||
- Added `WeakLua` and `Lua::weak()` to create weak references to Lua state
|
||||
- Trigger abort when Luau userdata destructors are panic (Luau GC does not support it)
|
||||
- Added `AnyUserData::type_id()` method to get the type id of the userdata
|
||||
- Added `Chunk::name()`, `Chunk::environment()` and `Chunk::mode()` functions
|
||||
- Support borrowing underlying wrapped types for `UserDataRef` and `UserDataRefMut` (under `userdata-wrappers` feature)
|
||||
- Added large (52bit) integers support for Luau
|
||||
- Enable `serde` for `bstr` if `serialize` feature flag is enabled
|
||||
- Recursive warnings (Lua 5.4) are no longer allowed
|
||||
- Implemented `IntoLua`/`FromLua` for `BorrowedString` and `BorrowedBytes`
|
||||
- Implemented `IntoLua`/`FromLua` for `char`
|
||||
- Enable `Thread::reset()` for all Lua versions (limited support for 5.1-5.3)
|
||||
- Bugfixes and improvements
|
||||
|
||||
## v0.10.3 (Jan 27th, 2025)
|
||||
|
||||
- Set `Default` for `Value` to be `Nil`
|
||||
- Allow exhaustive match on `Value` (#502)
|
||||
- Add `Table::set_safeenv` method (Luau)
|
||||
|
||||
## v0.10.2 (Dec 1st, 2024)
|
||||
|
||||
- Switch proc-macro-error to proc-macro-error2 (#493)
|
||||
- Do not allow Lua to run GC finalizers on ref thread (#491)
|
||||
- Fix chunks loading in Luau when memory limit is enforced (#488)
|
||||
- Added `String::wrap` method to wrap arbitrary `AsRef<[u8]>` into `impl IntoLua`
|
||||
- Better FreeBSD/OpenBSD support (thanks to cos)
|
||||
- Delay "any" userdata metatable creation until first instance is created (#482)
|
||||
- Reduce amount of generated code for `UserData` (less generics)
|
||||
|
||||
## v0.10.1 (Nov 9th, 2024)
|
||||
|
||||
- Minimal Luau updated to 0.650
|
||||
- Added Luau native vector library support (this can change behavior if you use `vector` function!)
|
||||
- Added Lua `String::display` method
|
||||
- Improved pretty-printing for Lua tables (#478)
|
||||
- Added `Scope::create_any_userdata` to create Lua objects from any non-`'static` Rust types
|
||||
- Added `AnyUserData::destroy` method
|
||||
- New `userdata-wrappers` feature to `impl UserData` for `Rc<T>`/`Arc<T>`/`Rc<RefCell<T>>`/`Arc<Mutex<T>>` (similar to v0.9)
|
||||
- `UserDataRef` in `send` mode now uses shared lock if `T: Sync` (and exclusive lock otherwise)
|
||||
- Added `Scope::add_destructor` to attach custom destructors
|
||||
- Added `Lua::try_app_data_ref` and `Lua::try_app_data_mut` methods
|
||||
- Added `From<Vec>` and `Into<Vec>` support to `MultiValue` and `Variadic` types
|
||||
- Bug fixes and improvements (#477 #479)
|
||||
|
||||
## v0.10.0 (Oct 25th, 2024)
|
||||
|
||||
Changes since v0.10.0-rc.1
|
||||
|
||||
- Added `error-send` feature flag (disabled by default) to require `Send + Sync` for `Error`
|
||||
- Some performance improvements
|
||||
|
||||
## v0.10.0-rc.1
|
||||
|
||||
- `Lua::scope` is back
|
||||
- Support yielding from hooks for Lua 5.3+
|
||||
- Support setting metatable for Lua builtin types (number/string/function/etc)
|
||||
- Added `LuaNativeFn`/`LuaNativeFnMut`/`LuaNativeAsyncFn` traits for using in `Function::wrap`
|
||||
- Added `Error::chain` method to return iterator over nested errors
|
||||
- Added `Lua::exec_raw` helper to execute low-level Lua C API code
|
||||
- Added `Either<L, R>` enum to combine two types into a single one
|
||||
- Added a new `Buffer` type for Luau
|
||||
- Added `Value::is_error` and `Value::as_error` helpers
|
||||
- Added `Value::Other` variant to represent unknown Lua types (eg LuaJIT CDATA)
|
||||
- Added (optional) `anyhow` feature to implement `IntoLua` for `anyhow::Error`
|
||||
- Added `IntoLua`/`FromLua` for `OsString`/`OsStr` and `PathBuf`/`Path`
|
||||
|
||||
## v0.10.0-beta.2
|
||||
|
||||
- Updated `ThreadStatus` enum to include `Running` and `Finished` variants.
|
||||
@@ -251,7 +381,7 @@ Other:
|
||||
|
||||
## v0.8.0
|
||||
Changes since 0.7.4
|
||||
- Roblox Luau support
|
||||
- Luau support
|
||||
- Removed C glue
|
||||
- Added async support to `__index` and `__newindex` metamethods
|
||||
- Added `Function::info()` to get information about functions (#149).
|
||||
@@ -301,7 +431,7 @@ Breaking changes:
|
||||
|
||||
## v0.8.0-beta.1
|
||||
|
||||
- Roblox Luau support
|
||||
- Luau support
|
||||
- Refactored ffi module. C glue is no longer required
|
||||
- Added async support to `__index` and `__newindex` metamethods
|
||||
|
||||
@@ -414,7 +544,7 @@ Breaking changes:
|
||||
- [**Breaking**] Removed `AnyUserData::has_metamethod()`
|
||||
- Added `Thread::reset()` for luajit/lua54 to recycle threads.
|
||||
It's possible to attach a new function to a thread (coroutine).
|
||||
- Added `chunk!` macro support to load chunks of Lua code using the Rust tokenizer and optinally capturing Rust variables.
|
||||
- Added `chunk!` macro support to load chunks of Lua code using the Rust tokenizer and optionally capturing Rust variables.
|
||||
- Improved error reporting (`Error`'s `__tostring` method formats full stacktraces). This is useful in the module mode.
|
||||
|
||||
## v0.6.0-beta.1
|
||||
@@ -470,7 +600,7 @@ Breaking changes:
|
||||
|
||||
- Lua 5.4 support with `MetaMethod::Close`.
|
||||
- `lua53` feature is disabled by default. Now preferred Lua version have to be chosen explicitly.
|
||||
- Provide safety guaraness for Lua state, which means that potenially unsafe operations, like loading C modules (using `require` or `package.loadlib`) are disabled. Equalient for the previous `Lua::new()` function is `Lua::unsafe_new()`.
|
||||
- Provide safety guarantees for Lua state, which means that potentially unsafe operations, like loading C modules (using `require` or `package.loadlib`) are disabled. Equivalent to the previous `Lua::new()` function is `Lua::unsafe_new()`.
|
||||
- New `send` feature to require `Send`.
|
||||
- New `module` feature, that disables linking to Lua Core Libraries. Required for modules.
|
||||
- Don't allow `'callback` outlive `'lua` in `Lua::create_function()` to fix [the unsoundness](tests/compile/static_callback_args.rs).
|
||||
|
||||
+25
-19
@@ -1,22 +1,22 @@
|
||||
[package]
|
||||
name = "mlua"
|
||||
version = "0.10.0-beta.2" # remember to update mlua_derive
|
||||
version = "0.11.2" # remember to update mlua_derive
|
||||
authors = ["Aleksandr Orlenko <zxteam@pm.me>", "kyren <catherine@kyju.org>"]
|
||||
rust-version = "1.79.0"
|
||||
edition = "2021"
|
||||
repository = "https://github.com/khvzak/mlua"
|
||||
repository = "https://github.com/mlua-rs/mlua"
|
||||
documentation = "https://docs.rs/mlua"
|
||||
readme = "README.md"
|
||||
keywords = ["lua", "luajit", "luau", "async", "scripting"]
|
||||
categories = ["api-bindings", "asynchronous"]
|
||||
license = "MIT"
|
||||
description = """
|
||||
High level bindings to Lua 5.4/5.3/5.2/5.1 (including LuaJIT) and Roblox Luau
|
||||
High level bindings to Lua 5.4/5.3/5.2/5.1 (including LuaJIT) and Luau
|
||||
with async/await features and support of writing native Lua modules in Rust.
|
||||
"""
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
features = ["lua54", "vendored", "async", "send", "serialize", "macros"]
|
||||
features = ["lua54", "vendored", "async", "send", "serde", "macros"]
|
||||
rustdoc-args = ["--cfg", "docsrs"]
|
||||
|
||||
[workspace]
|
||||
@@ -32,19 +32,26 @@ lua52 = ["ffi/lua52"]
|
||||
lua51 = ["ffi/lua51"]
|
||||
luajit = ["ffi/luajit"]
|
||||
luajit52 = ["luajit", "ffi/luajit52"]
|
||||
luau = ["ffi/luau", "dep:libloading"]
|
||||
luau = ["ffi/luau"]
|
||||
luau-jit = ["luau", "ffi/luau-codegen"]
|
||||
luau-vector4 = ["luau", "ffi/luau-vector4"]
|
||||
vendored = ["ffi/vendored"]
|
||||
module = ["dep:mlua_derive", "ffi/module"]
|
||||
module = ["mlua_derive", "ffi/module"]
|
||||
async = ["dep:futures-util"]
|
||||
send = ["parking_lot/send_guard"]
|
||||
serialize = ["dep:serde", "dep:erased-serde", "dep:serde-value"]
|
||||
send = ["error-send"]
|
||||
error-send = []
|
||||
serde = ["dep:serde", "dep:erased-serde", "dep:serde-value", "bstr/serde"]
|
||||
macros = ["mlua_derive/macros"]
|
||||
anyhow = ["dep:anyhow", "error-send"]
|
||||
userdata-wrappers = ["parking_lot/send_guard"]
|
||||
|
||||
# deprecated features
|
||||
serialize = ["serde"]
|
||||
|
||||
[dependencies]
|
||||
mlua_derive = { version = "=0.10.0-beta.1", optional = true, path = "mlua_derive" }
|
||||
mlua_derive = { version = "=0.11.0", optional = true, path = "mlua_derive" }
|
||||
bstr = { version = "1.0", features = ["std"], default-features = false }
|
||||
either = "1.0"
|
||||
num-traits = { version = "0.2.14" }
|
||||
rustc-hash = "2.0"
|
||||
futures-util = { version = "0.3", optional = true, default-features = false, features = ["std"] }
|
||||
@@ -52,11 +59,10 @@ serde = { version = "1.0", optional = true }
|
||||
erased-serde = { version = "0.4", optional = true }
|
||||
serde-value = { version = "0.7", optional = true }
|
||||
parking_lot = { version = "0.12", features = ["arc_lock"] }
|
||||
anyhow = { version = "1.0", optional = true }
|
||||
rustversion = "1.0"
|
||||
|
||||
ffi = { package = "mlua-sys", version = "0.6.3", path = "mlua-sys" }
|
||||
|
||||
[target.'cfg(unix)'.dependencies]
|
||||
libloading = { version = "0.8", optional = true }
|
||||
ffi = { package = "mlua-sys", version = "0.8.3", path = "mlua-sys" }
|
||||
|
||||
[dev-dependencies]
|
||||
trybuild = "1.0"
|
||||
@@ -72,8 +78,8 @@ tempfile = "3"
|
||||
static_assertions = "1.0"
|
||||
|
||||
[target.'cfg(not(target_arch = "wasm32"))'.dev-dependencies]
|
||||
criterion = { version = "0.5", features = ["async_tokio"] }
|
||||
rustyline = "14.0"
|
||||
criterion = { version = "0.7", features = ["async_tokio"] }
|
||||
rustyline = "17.0"
|
||||
tokio = { version = "1.0", features = ["full"] }
|
||||
|
||||
[lints.rust]
|
||||
@@ -87,7 +93,7 @@ required-features = ["async"]
|
||||
[[bench]]
|
||||
name = "serde"
|
||||
harness = false
|
||||
required-features = ["serialize"]
|
||||
required-features = ["serde"]
|
||||
|
||||
[[example]]
|
||||
name = "async_http_client"
|
||||
@@ -95,7 +101,7 @@ required-features = ["async", "macros"]
|
||||
|
||||
[[example]]
|
||||
name = "async_http_reqwest"
|
||||
required-features = ["async", "serialize", "macros"]
|
||||
required-features = ["async", "serde", "macros"]
|
||||
|
||||
[[example]]
|
||||
name = "async_http_server"
|
||||
@@ -110,8 +116,8 @@ name = "guided_tour"
|
||||
required-features = ["macros"]
|
||||
|
||||
[[example]]
|
||||
name = "serialize"
|
||||
required-features = ["serialize"]
|
||||
name = "serde"
|
||||
required-features = ["serde"]
|
||||
|
||||
[[example]]
|
||||
name = "userdata"
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
# mlua
|
||||
[![Build Status]][github-actions] [![Latest Version]][crates.io] [![API Documentation]][docs.rs] [![Coverage Status]][codecov.io] ![MSRV]
|
||||
|
||||
[Build Status]: https://github.com/khvzak/mlua/workflows/CI/badge.svg
|
||||
[github-actions]: https://github.com/khvzak/mlua/actions
|
||||
[Build Status]: https://github.com/mlua-rs/mlua/workflows/CI/badge.svg
|
||||
[github-actions]: https://github.com/mlua-rs/mlua/actions
|
||||
[Latest Version]: https://img.shields.io/crates/v/mlua.svg
|
||||
[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/mlua-rs/mlua/branch/main/graph/badge.svg?token=99339FS1CG
|
||||
[codecov.io]: https://codecov.io/gh/mlua-rs/mlua
|
||||
[MSRV]: https://img.shields.io/badge/rust-1.71+-brightgreen.svg?&logo=rust
|
||||
[MSRV]: https://img.shields.io/badge/rust-1.79+-brightgreen.svg?&logo=rust
|
||||
|
||||
[Guided Tour] | [Benchmarks] | [FAQ]
|
||||
|
||||
@@ -17,25 +17,23 @@
|
||||
[Benchmarks]: https://github.com/khvzak/script-bench-rs
|
||||
[FAQ]: FAQ.md
|
||||
|
||||
# The main branch is the v0.10, development version of `mlua`. Please see the [v0.9](https://github.com/mlua-rs/mlua/tree/v0.9) branch for the stable versions of `mlua`.
|
||||
`mlua` is a set of bindings to the [Lua](https://www.lua.org) programming language for Rust with a goal to provide a
|
||||
_safe_ (as much as possible), high level, easy to use, practical and flexible API.
|
||||
|
||||
`mlua` is bindings to [Lua](https://www.lua.org) programming language for Rust with a goal to provide
|
||||
_safe_ (as far as it's possible), high level, easy to use, practical and flexible API.
|
||||
Started as an `rlua` fork, `mlua` supports Lua 5.4, 5.3, 5.2, 5.1 (including LuaJIT) and [Luau] and allows writing native Lua modules in Rust as well as using Lua in a standalone mode.
|
||||
|
||||
Started as `rlua` fork, `mlua` supports Lua 5.4, 5.3, 5.2, 5.1 (including LuaJIT) and [Roblox Luau] and allows to write native Lua modules in Rust as well as use Lua in a standalone mode.
|
||||
`mlua` is tested on Windows/macOS/Linux including module mode in [GitHub Actions] on `x86_64` platforms and cross-compilation to `aarch64` (other targets are also supported).
|
||||
|
||||
`mlua` tested on Windows/macOS/Linux including module mode in [GitHub Actions] on `x86_64` platform and cross-compilation to `aarch64` (other targets are also supported).
|
||||
WebAssembly (WASM) is supported through `wasm32-unknown-emscripten` target for all Lua/Luau versions excluding JIT.
|
||||
|
||||
WebAssembly (WASM) is supported through `wasm32-unknown-emscripten` target for all Lua versions excluding JIT.
|
||||
|
||||
[GitHub Actions]: https://github.com/khvzak/mlua/actions
|
||||
[Roblox Luau]: https://luau-lang.org
|
||||
[GitHub Actions]: https://github.com/mlua-rs/mlua/actions
|
||||
[Luau]: https://luau.org
|
||||
|
||||
## Usage
|
||||
|
||||
### Feature flags
|
||||
|
||||
`mlua` uses feature flags to reduce the amount of dependencies, compiled code and allow to choose only required set of features.
|
||||
`mlua` uses feature flags to reduce the amount of dependencies and compiled code, and allow to choose only required set of features.
|
||||
Below is a list of the available feature flags. By default `mlua` does not enable any features.
|
||||
|
||||
* `lua54`: enable Lua [5.4] support
|
||||
@@ -47,21 +45,24 @@ Below is a list of the available feature flags. By default `mlua` does not enabl
|
||||
* `luau`: enable [Luau] support (auto vendored mode)
|
||||
* `luau-jit`: enable [Luau] support with JIT backend.
|
||||
* `luau-vector4`: enable [Luau] support with 4-dimensional vector.
|
||||
* `vendored`: build static Lua(JIT) library from sources during `mlua` compilation using [lua-src] or [luajit-src] crates
|
||||
* `vendored`: build static Lua(JIT) libraries from sources during `mlua` compilation using [lua-src] or [luajit-src]
|
||||
* `module`: enable module mode (building loadable `cdylib` library for Lua)
|
||||
* `async`: enable async/await support (any executor can be used, eg. [tokio] or [async-std])
|
||||
* `send`: make `mlua::Lua: Send + Sync` (adds [`Send`] requirement to `mlua::Function` and `mlua::UserData`)
|
||||
* `serialize`: add serialization and deserialization support to `mlua` types using [serde] framework
|
||||
* `error-send`: make `mlua:Error: Send + Sync`
|
||||
* `serde`: add serialization and deserialization support to `mlua` types using [serde]
|
||||
* `macros`: enable procedural macros (such as `chunk!`)
|
||||
* `anyhow`: enable `anyhow::Error` conversion into Lua
|
||||
* `userdata-wrappers`: opt into `impl UserData` for `Rc<T>`/`Arc<T>`/`Rc<RefCell<T>>`/`Arc<Mutex<T>>` where `T: UserData`
|
||||
|
||||
[5.4]: https://www.lua.org/manual/5.4/manual.html
|
||||
[5.3]: https://www.lua.org/manual/5.3/manual.html
|
||||
[5.2]: https://www.lua.org/manual/5.2/manual.html
|
||||
[5.1]: https://www.lua.org/manual/5.1/manual.html
|
||||
[LuaJIT]: https://luajit.org/
|
||||
[Luau]: https://github.com/Roblox/luau
|
||||
[lua-src]: https://github.com/khvzak/lua-src-rs
|
||||
[luajit-src]: https://github.com/khvzak/luajit-src-rs
|
||||
[Luau]: https://github.com/luau-lang/luau
|
||||
[lua-src]: https://github.com/mlua-rs/lua-src-rs
|
||||
[luajit-src]: https://github.com/mlua-rs/luajit-src-rs
|
||||
[tokio]: https://github.com/tokio-rs/tokio
|
||||
[async-std]: https://github.com/async-rs/async-std
|
||||
[`Send`]: https://doc.rust-lang.org/std/marker/trait.Send.html
|
||||
@@ -71,7 +72,7 @@ Below is a list of the available feature flags. By default `mlua` does not enabl
|
||||
|
||||
`mlua` supports async/await for all Lua versions including Luau.
|
||||
|
||||
This works using Lua [coroutines](https://www.lua.org/manual/5.3/manual.html#2.6) and require running [Thread](https://docs.rs/mlua/latest/mlua/struct.Thread.html) along with enabling `feature = "async"` in `Cargo.toml`.
|
||||
This works using Lua [coroutines](https://www.lua.org/manual/5.3/manual.html#2.6) and requires running [Thread](https://docs.rs/mlua/latest/mlua/struct.Thread.html) along with enabling `feature = "async"` in `Cargo.toml`.
|
||||
|
||||
**Examples**:
|
||||
- [HTTP Client](examples/async_http_client.rs)
|
||||
@@ -86,18 +87,18 @@ This works using Lua [coroutines](https://www.lua.org/manual/5.3/manual.html#2.6
|
||||
cargo run --example async_http_client --features=lua54,async,macros
|
||||
|
||||
# async http client (reqwest)
|
||||
cargo run --example async_http_reqwest --features=lua54,async,macros,serialize
|
||||
cargo run --example async_http_reqwest --features=lua54,async,macros,serde
|
||||
|
||||
# async http server
|
||||
cargo run --example async_http_server --features=lua54,async,macros,send
|
||||
curl -v http://localhost:3000
|
||||
```
|
||||
|
||||
### Serialization (serde) support
|
||||
### Serde support
|
||||
|
||||
With `serialize` feature flag enabled, `mlua` allows you to serialize/deserialize any type that implements [`serde::Serialize`] and [`serde::Deserialize`] into/from [`mlua::Value`]. In addition `mlua` provides [`serde::Serialize`] trait implementation for it (including `UserData` support).
|
||||
With the `serde` feature flag enabled, `mlua` allows you to serialize/deserialize any type that implements [`serde::Serialize`] and [`serde::Deserialize`] into/from [`mlua::Value`]. In addition, `mlua` provides the [`serde::Serialize`] trait implementation for `mlua::Value` (including `UserData` support).
|
||||
|
||||
[Example](examples/serialize.rs)
|
||||
[Example](examples/serde.rs)
|
||||
|
||||
[`serde::Serialize`]: https://docs.serde.rs/serde/ser/trait.Serialize.html
|
||||
[`serde::Deserialize`]: https://docs.serde.rs/serde/de/trait.Deserialize.html
|
||||
@@ -107,28 +108,28 @@ With `serialize` feature flag enabled, `mlua` allows you to serialize/deserializ
|
||||
|
||||
You have to enable one of the features: `lua54`, `lua53`, `lua52`, `lua51`, `luajit(52)` or `luau`, according to the chosen Lua version.
|
||||
|
||||
By default `mlua` uses `pkg-config` tool to find lua includes and libraries for the chosen Lua version.
|
||||
In most cases it works as desired, although sometimes could be more preferable to use a custom lua library.
|
||||
To achieve this, mlua supports `LUA_LIB`, `LUA_LIB_NAME` and `LUA_LINK` environment variables.
|
||||
By default `mlua` uses `pkg-config` to find Lua includes and libraries for the chosen Lua version.
|
||||
In most cases it works as desired, although sometimes it may be preferable to use a custom Lua library.
|
||||
To achieve this, mlua supports the `LUA_LIB`, `LUA_LIB_NAME` and `LUA_LINK` environment variables.
|
||||
`LUA_LINK` is optional and may be `dylib` (a dynamic library) or `static` (a static library, `.a` archive).
|
||||
|
||||
An example how to use them:
|
||||
An example of how to use them:
|
||||
``` sh
|
||||
my_project $ LUA_LIB=$HOME/tmp/lua-5.2.4/src LUA_LIB_NAME=lua LUA_LINK=static cargo build
|
||||
```
|
||||
|
||||
`mlua` also supports vendored lua/luajit using the auxiliary crates [lua-src](https://crates.io/crates/lua-src) and
|
||||
`mlua` also supports vendored Lua/LuaJIT using the auxiliary crates [lua-src](https://crates.io/crates/lua-src) and
|
||||
[luajit-src](https://crates.io/crates/luajit-src).
|
||||
Just enable the `vendored` feature and cargo will automatically build and link specified lua/luajit version. This is the easiest way to get started with `mlua`.
|
||||
Just enable the `vendored` feature and cargo will automatically build and link the specified Lua/LuaJIT version. This is the easiest way to get started with `mlua`.
|
||||
|
||||
### Standalone mode
|
||||
In a standalone mode `mlua` allows to add to your application scripting support with a gently configured Lua runtime to ensure safety and soundness.
|
||||
In standalone mode, `mlua` allows adding scripting support to your application with a gently configured Lua runtime to ensure safety and soundness.
|
||||
|
||||
Add to `Cargo.toml` :
|
||||
Add to `Cargo.toml`:
|
||||
|
||||
``` toml
|
||||
[dependencies]
|
||||
mlua = { version = "0.9.9", features = ["lua54", "vendored"] }
|
||||
mlua = { version = "0.11", features = ["lua54", "vendored"] }
|
||||
```
|
||||
|
||||
`main.rs`
|
||||
@@ -152,21 +153,21 @@ fn main() -> LuaResult<()> {
|
||||
```
|
||||
|
||||
### Module mode
|
||||
In a module mode `mlua` allows to create a compiled Lua module that can be loaded from Lua code using [`require`](https://www.lua.org/manual/5.4/manual.html#pdf-require). In this case `mlua` uses an external Lua runtime which could lead to potential unsafety due to unpredictability of the Lua environment and usage of libraries such as [`debug`](https://www.lua.org/manual/5.4/manual.html#6.10).
|
||||
In module mode, `mlua` allows creating a compiled Lua module that can be loaded from Lua code using [`require`](https://www.lua.org/manual/5.4/manual.html#pdf-require). In this case `mlua` uses an external Lua runtime which could lead to potential unsafety due to the unpredictability of the Lua environment and usage of libraries such as [`debug`](https://www.lua.org/manual/5.4/manual.html#6.10).
|
||||
|
||||
[Example](examples/module)
|
||||
|
||||
Add to `Cargo.toml` :
|
||||
Add to `Cargo.toml`:
|
||||
|
||||
``` toml
|
||||
[lib]
|
||||
crate-type = ["cdylib"]
|
||||
|
||||
[dependencies]
|
||||
mlua = { version = "0.9.9", features = ["lua54", "module"] }
|
||||
mlua = { version = "0.11", features = ["lua54", "module"] }
|
||||
```
|
||||
|
||||
`lib.rs` :
|
||||
`lib.rs`:
|
||||
|
||||
``` rust
|
||||
use mlua::prelude::*;
|
||||
@@ -209,14 +210,14 @@ rustflags = [
|
||||
```
|
||||
On Linux you can build modules normally with `cargo build --release`.
|
||||
|
||||
On Windows the target module will be linked with `lua5x.dll` library (depending on your feature flags).
|
||||
On Windows the target module will be linked with the `lua5x.dll` library (depending on your feature flags).
|
||||
Your main application should provide this library.
|
||||
|
||||
Module builds don't require Lua lib or headers to be installed on the system.
|
||||
Module builds don't require Lua binaries or headers to be installed on the system.
|
||||
|
||||
### Publishing to luarocks.org
|
||||
|
||||
There is a LuaRocks build backend for mlua modules [`luarocks-build-rust-mlua`].
|
||||
There is a LuaRocks build backend for mlua modules: [`luarocks-build-rust-mlua`].
|
||||
|
||||
Modules written in Rust and published to luarocks:
|
||||
- [`decasify`](https://github.com/alerque/decasify)
|
||||
@@ -229,10 +230,10 @@ Modules written in Rust and published to luarocks:
|
||||
|
||||
## Safety
|
||||
|
||||
One of the `mlua` goals is to provide *safe* API between Rust and Lua.
|
||||
Every place where the Lua C API may trigger an error longjmp in any way is protected by `lua_pcall`,
|
||||
and the user of the library is protected from directly interacting with unsafe things like the Lua stack,
|
||||
and there is overhead associated with this safety.
|
||||
One of `mlua`'s goals is to provide a *safe* API between Rust and Lua.
|
||||
Every place where the Lua C API may trigger an error longjmp is protected by `lua_pcall`,
|
||||
and the user of the library is protected from directly interacting with unsafe things like the Lua stack.
|
||||
There is overhead associated with this safety.
|
||||
|
||||
Unfortunately, `mlua` does not provide absolute safety even without using `unsafe` .
|
||||
This library contains a huge amount of unsafe code. There are almost certainly bugs still lurking in this library!
|
||||
@@ -240,8 +241,8 @@ It is surprisingly, fiendishly difficult to use the Lua C API without the potent
|
||||
|
||||
## Panic handling
|
||||
|
||||
`mlua` wraps panics that are generated inside Rust callbacks in a regular Lua error. Panics could be
|
||||
resumed then by returning or propagating the Lua error to Rust code.
|
||||
`mlua` wraps panics that are generated inside Rust callbacks in a regular Lua error. Panics can then be
|
||||
resumed by returning or propagating the Lua error to Rust code.
|
||||
|
||||
For example:
|
||||
``` rust
|
||||
@@ -260,12 +261,12 @@ let _ = lua.load(r#"
|
||||
unreachable!()
|
||||
```
|
||||
|
||||
Optionally `mlua` can disable Rust panics catching in Lua via `pcall`/`xpcall` and automatically resume
|
||||
Optionally, `mlua` can disable Rust panic catching in Lua via `pcall`/`xpcall` and automatically resume
|
||||
them across the Lua API boundary. This is controlled via `LuaOptions` and done by wrapping the Lua `pcall`/`xpcall`
|
||||
functions on a way to prevent catching errors that are wrapped Rust panics.
|
||||
functions to prevent catching errors that are wrapped Rust panics.
|
||||
|
||||
`mlua` should also be panic safe in another way as well, which is that any `Lua` instances or handles
|
||||
remains usable after a user generated panic, and such panics should not break internal invariants or
|
||||
remain usable after a user generated panic, and such panics should not break internal invariants or
|
||||
leak Lua stack space. This is mostly important to safely use `mlua` types in Drop impls, as you should not be
|
||||
using panics for general error handling.
|
||||
|
||||
@@ -282,12 +283,12 @@ If you encounter them, a bug report would be very welcome:
|
||||
|
||||
## Sandboxing
|
||||
|
||||
Please check the [Luau Sandboxing] page if you are interested in running untrusted Lua scripts in controlled environment.
|
||||
Please check the [Luau Sandboxing] page if you are interested in running untrusted Lua scripts in a controlled environment.
|
||||
|
||||
`mlua` provides `Lua::sandbox` method for enabling sandbox mode (Luau only).
|
||||
`mlua` provides the `Lua::sandbox` method for enabling sandbox mode (Luau only).
|
||||
|
||||
[Luau Sandboxing]: https://luau-lang.org/sandbox
|
||||
[Luau Sandboxing]: https://luau.org/sandbox
|
||||
|
||||
## License
|
||||
|
||||
This project is licensed under the [MIT license](LICENSE)
|
||||
This project is licensed under the [MIT license](LICENSE).
|
||||
|
||||
@@ -0,0 +1,195 @@
|
||||
## mlua v0.10 release notes
|
||||
|
||||
The v0.10 version of mlua has goal to improve the user experience while keeping the same performance and safety guarantees.
|
||||
This document highlights the most notable features. For a full list of changes, see the [CHANGELOG].
|
||||
|
||||
[CHANGELOG]: https://github.com/mlua-rs/mlua/blob/main/CHANGELOG.md
|
||||
|
||||
### New features
|
||||
|
||||
#### `'static` Lua types
|
||||
|
||||
In previous mlua versions, it was required to have a `'lua` lifetime attached to every Lua value. v0.9 introduced (experimental) owned types that are `'static` without a lifetime attached, but they kept strong references to the Lua instance.
|
||||
In v0.10 all Lua types are `'static` and have only weak reference to the Lua instance. It means they are more flexible and can be used in more places without worrying about memory leaks.
|
||||
|
||||
#### Truly `send` feature
|
||||
|
||||
In this version Lua is `Send + Sync` when the `send` feature flag is enabled (previously was only `Send`). It means Lua instance and their values can be safely shared between threads and used in multi threaded async contexts.
|
||||
|
||||
```rust
|
||||
let lua = Lua::new();
|
||||
|
||||
lua.globals().set("i", 0)?;
|
||||
let func = lua.load("i = i + ...").into_function()?;
|
||||
|
||||
std::thread::scope(|s| {
|
||||
s.spawn(|| {
|
||||
for i in 0..5 {
|
||||
func.call::<()>(i).unwrap();
|
||||
}
|
||||
});
|
||||
s.spawn(|| {
|
||||
for i in 0..5 {
|
||||
func.call::<()>(i).unwrap();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
assert_eq!(lua.globals().get::<i32>("i")?, 20);
|
||||
```
|
||||
|
||||
Under the hood, to synchronize access to the Lua state, mlua uses [`ReentrantMutex`] which can be recursively locked by a single thread. Only one thread can execute Lua code at a time, but it's possible to share Lua values between threads.
|
||||
|
||||
This has some performance penalties (about 10-20%) compared to the lock free mode. This flag is disabled by default and does not supported in module mode.
|
||||
|
||||
[`ReentrantMutex`]: https://docs.rs/parking_lot/latest/parking_lot/type.ReentrantMutex.html
|
||||
|
||||
#### Register Rust functions with variable number of arguments
|
||||
|
||||
The new traits `LuaNativeFn`/`LuaNativeFnMut`/`LuaNativeAsyncFn` have been introduced to provide a way to register Rust functions with variable number of arguments in Lua, without needing to pass all arguments as a tuple.
|
||||
|
||||
They are used by `Function::wrap`/`Function::wrap_mut`/`Function::wrap_async` methods:
|
||||
|
||||
```rust
|
||||
let add = Function::wrap(|a: i64, b: i64| Ok(a + b));
|
||||
|
||||
lua.globals().set("add", add).unwrap();
|
||||
|
||||
// Prints 50
|
||||
lua.load(r#"print(add(5, 45))"#).exec().unwrap();
|
||||
```
|
||||
|
||||
To wrap functions that return direct value (non-`Result`) you can use `Function::wrap_raw` method.
|
||||
|
||||
#### Setting metatable for Lua builtin types
|
||||
|
||||
For Lua builtin types (like `string`, `function`, `number`, etc.) that have a shared metatable for all instances, it's now possible to set a custom metatable for them.
|
||||
|
||||
```rust
|
||||
let mt = lua.create_table()?;
|
||||
mt.set("__tostring", lua.create_function(|_, b: bool| Ok(if b { "2" } else { "0" }))?)?;
|
||||
lua.set_type_metatable::<bool>(Some(mt));
|
||||
lua.load("assert(tostring(true) == '2')").exec().unwrap();
|
||||
```
|
||||
|
||||
### Improvements
|
||||
|
||||
#### New `ObjectLike` trait
|
||||
|
||||
The `ObjectLike` trait is a combination of the `AnyUserDataExt` and `TableExt` traits used in previous versions. It provides a unified interface for working with Lua tables and userdata.
|
||||
|
||||
#### `Either<L, R>` enum
|
||||
|
||||
The `Either<L, R>` enum is a simple enum that can hold either `L` or `R` value. It's useful when you need to return or receive one of two types in a function.
|
||||
This type implements `IntoLua` and `FromLua` traits and can generate a meaningful error message when conversion fails.
|
||||
|
||||
```rust
|
||||
let func = Function::wrap(|x: Either<i32, String>| Ok(format!("received: {x}")));
|
||||
|
||||
lua.globals().set("func", func).unwrap();
|
||||
|
||||
// Prints: received: 123
|
||||
lua.load(r#"print(func(123))"#).exec().unwrap();
|
||||
|
||||
// Prints: bad argument #1: error converting Lua table to Either<i32, String>
|
||||
lua.load(r#"print(pcall(func, {}))"#).exec().unwrap();
|
||||
```
|
||||
|
||||
#### `Lua::exec_raw` helper to execute low-level Lua C API code
|
||||
|
||||
For advanced users, it's now possible to execute low-level Lua C API code using the `Lua::exec_raw` method.
|
||||
|
||||
```rust
|
||||
let t = lua.create_sequence_from([1, 2, 3, 4, 5])?;
|
||||
let sum: i64 = unsafe {
|
||||
lua.exec_raw(&t, |state| {
|
||||
// top of the stack: table `t`
|
||||
let mut sum = 0;
|
||||
// push nil as the first key
|
||||
mlua::ffi::lua_pushnil(state);
|
||||
while mlua::ffi::lua_next(state, -2) != 0 {
|
||||
sum += mlua::ffi::lua_tointeger(state, -1);
|
||||
// Remove the value, keep the key for the next iteration
|
||||
mlua::ffi::lua_pop(state, 1);
|
||||
}
|
||||
mlua::ffi::lua_pop(state, 1);
|
||||
mlua::ffi::lua_pushinteger(state, sum);
|
||||
// top of the stack: sum
|
||||
})
|
||||
}?;
|
||||
assert_eq!(sum, 15);
|
||||
```
|
||||
|
||||
The `exec_raw` method is longjmp-safe. It's not recommended to move `Drop` types into the closure to avoid possible memory leaks.
|
||||
|
||||
#### `anyhow` feature flag
|
||||
|
||||
The new `anyhow` feature flag adds `IntoLua` and `Into<mlua::Error>` implementation for the `anyhow::Error` type.
|
||||
|
||||
```rust
|
||||
let f = lua.create_function(|_, ()| {
|
||||
Err(anyhow!("error message"))?;
|
||||
Ok(())
|
||||
})?;
|
||||
```
|
||||
|
||||
### Breaking changes
|
||||
|
||||
#### Scope changes
|
||||
|
||||
The following `Scope` methods were changed:
|
||||
- Removed `Scope::create_any_userdata`
|
||||
- `Scope::create_nonstatic_userdata` is renamed to `Scope::create_userdata`
|
||||
|
||||
Instead, scope has comprehensive support for borrowed userdata: `create_any_userdata_ref`, `create_any_userdata_ref_mut`, `create_userdata_ref`, `create_userdata_ref_mut`.
|
||||
|
||||
`UserDataRef` and `UserDataRefMut` are no longer acceptable for scoped userdata access as they require owned underlying data.
|
||||
In mlua v0.9 this can cause read-after-free bug in some edge cases.
|
||||
|
||||
To temporarily borrow underlying data, the `AnyUserData::borrow_scoped` and `AnyUserData::borrow_mut_scoped` methods were introduced:
|
||||
|
||||
```rust
|
||||
let data = "hello".to_string();
|
||||
lua.scope(|scope| {
|
||||
let ud = scope.create_any_userdata_ref(&data)?;
|
||||
|
||||
// We can only borrow scoped userdata using this method
|
||||
ud.borrow_scoped::<String, ()>(|s| {
|
||||
assert_eq!(s, "hello");
|
||||
})?;
|
||||
|
||||
Ok(())
|
||||
})?;
|
||||
```
|
||||
|
||||
Those methods work for scoped and regular userdata objects (but still require `T: 'static`).
|
||||
|
||||
#### String changes
|
||||
|
||||
Since `mlua::String` holds a weak reference to Lua without any guarantees about the lifetime of the underlying data, getting a `&str` or `&[u8]` from it is no longer safe.
|
||||
Lua instance can be destroyed while reference to the data is still alive:
|
||||
|
||||
```rust
|
||||
let lua = Lua::new();
|
||||
let s: mlua::String = lua.create_string("hello, world")?; // only weak reference to Lua!
|
||||
let s_ref: &str = s.to_str()?; // this is not safe!
|
||||
drop(lua);
|
||||
println!("{s_ref}"); // use after free!
|
||||
```
|
||||
|
||||
To solve this issue, return types of `mlua::String::to_str` and `mlua::String::as_bytes` methods changed to `BorrowedStr` and `BorrowedBytes` respectively.
|
||||
|
||||
These new types hold a strong reference to the Lua instance and can be safely converted to `&str` or `&[u8]`:
|
||||
|
||||
```rust
|
||||
let lua = Lua::new();
|
||||
let s: mlua::String = lua.create_string("hello, world")?;
|
||||
let s_ref: mlua::BorrowedStr = s.to_str()?; // The strong reference to Lua is held here
|
||||
drop(lua);
|
||||
println!("{s_ref}"); // ok
|
||||
```
|
||||
|
||||
The good news is that `BorrowedStr` implements `Deref<Target = str>`/`AsRef<str>` as well as `Display`, `Debug`, `Eq`, `PartialEq` and other traits for easy usage.
|
||||
The same applies to `BorrowedBytes`.
|
||||
|
||||
Unfortunately, `mlua::String::to_string_lossy` cannot return `Cow<'a, str>` anymore, because it requires a strong reference to Lua. It now returns Rust `String` instead.
|
||||
@@ -3,7 +3,7 @@
|
||||
The v0.9 version of mlua is a major release that includes a number of API changes and improvements. This release is a stepping stone towards the v1.0.
|
||||
This document highlights the most important changes. For a full list of changes, see the [CHANGELOG].
|
||||
|
||||
[CHANGELOG]: https://github.com/khvzak/mlua/blob/main/CHANGELOG.md
|
||||
[CHANGELOG]: https://github.com/mlua-rs/mlua/blob/main/CHANGELOG.md
|
||||
|
||||
### New features
|
||||
|
||||
@@ -304,7 +304,7 @@ assert_eq!(f.call::<_, mlua::String>(())?, "hello");
|
||||
|
||||
The new mlua version has a number of performance improvements. Please check the [benchmarks results] to see how mlua compares to rlua and rhai.
|
||||
|
||||
[benchmarks results]: https://github.com/khvzak/script-bench-rs
|
||||
[benchmarks results]: https://github.com/mlua-rs/script-bench-rs
|
||||
|
||||
### Changes in `module` mode
|
||||
|
||||
|
||||
+10
-8
@@ -20,14 +20,16 @@ fn main() {
|
||||
match lua.load(&line).eval::<MultiValue>() {
|
||||
Ok(values) => {
|
||||
editor.add_history_entry(line).unwrap();
|
||||
println!(
|
||||
"{}",
|
||||
values
|
||||
.iter()
|
||||
.map(|value| format!("{:#?}", value))
|
||||
.collect::<Vec<_>>()
|
||||
.join("\t")
|
||||
);
|
||||
if values.len() > 0 {
|
||||
println!(
|
||||
"{}",
|
||||
values
|
||||
.iter()
|
||||
.map(|value| format!("{:#?}", value))
|
||||
.collect::<Vec<_>>()
|
||||
.join("\t")
|
||||
);
|
||||
}
|
||||
break;
|
||||
}
|
||||
Err(Error::SyntaxError {
|
||||
|
||||
+7
-6
@@ -1,10 +1,10 @@
|
||||
[package]
|
||||
name = "mlua-sys"
|
||||
version = "0.6.3"
|
||||
version = "0.8.3"
|
||||
authors = ["Aleksandr Orlenko <zxteam@pm.me>"]
|
||||
rust-version = "1.71"
|
||||
edition = "2021"
|
||||
repository = "https://github.com/khvzak/mlua"
|
||||
repository = "https://github.com/mlua-rs/mlua"
|
||||
documentation = "https://docs.rs/mlua-sys"
|
||||
readme = "README.md"
|
||||
categories = ["external-ffi-bindings"]
|
||||
@@ -12,7 +12,7 @@ license = "MIT"
|
||||
links = "lua"
|
||||
build = "build/main.rs"
|
||||
description = """
|
||||
Low level (FFI) bindings to Lua 5.4/5.3/5.2/5.1 (including LuaJIT) and Roblox Luau
|
||||
Low level (FFI) bindings to Lua 5.4/5.3/5.2/5.1 (including LuaJIT) and Luau
|
||||
"""
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
@@ -30,6 +30,7 @@ luau = ["luau0-src"]
|
||||
luau-codegen = ["luau"]
|
||||
luau-vector4 = ["luau"]
|
||||
vendored = ["lua-src", "luajit-src"]
|
||||
external = []
|
||||
module = []
|
||||
|
||||
[dependencies]
|
||||
@@ -38,9 +39,9 @@ module = []
|
||||
cc = "1.0"
|
||||
cfg-if = "1.0"
|
||||
pkg-config = "0.3.17"
|
||||
lua-src = { version = ">= 547.0.0, < 547.1.0", optional = true }
|
||||
luajit-src = { version = ">= 210.5.0, < 210.6.0", optional = true }
|
||||
luau0-src = { version = "0.10.0", optional = true }
|
||||
lua-src = { version = ">= 548.1.0, < 548.2.0", optional = true }
|
||||
luajit-src = { version = ">= 210.6.0, < 210.7.0", optional = true }
|
||||
luau0-src = { version = "0.15.4", optional = true }
|
||||
|
||||
[lints.rust]
|
||||
unexpected_cfgs = { level = "allow", check-cfg = ['cfg(raw_dylib)'] }
|
||||
|
||||
+2
-2
@@ -1,8 +1,8 @@
|
||||
# mlua-sys
|
||||
|
||||
Low level (FFI) bindings to Lua 5.4/5.3/5.2/5.1 (including LuaJIT) and Roblox [Luau].
|
||||
Low level (FFI) bindings to Lua 5.4/5.3/5.2/5.1 (including LuaJIT) and [Luau].
|
||||
|
||||
Intended to be consumed by the [mlua] crate.
|
||||
|
||||
[Luau]: https://github.com/Roblox/luau
|
||||
[Luau]: https://github.com/luau-lang/luau
|
||||
[mlua]: https://crates.io/crates/mlua
|
||||
|
||||
@@ -32,15 +32,19 @@ pub fn probe_lua() {
|
||||
// Find using `pkg-config`
|
||||
|
||||
#[cfg(feature = "lua54")]
|
||||
let (incl_bound, excl_bound, alt_probe, ver) = ("5.4", "5.5", Some("lua5.4"), "5.4");
|
||||
let (incl_bound, excl_bound, alt_probe, ver) =
|
||||
("5.4", "5.5", ["lua5.4", "lua-5.4", "lua54"], "5.4");
|
||||
#[cfg(feature = "lua53")]
|
||||
let (incl_bound, excl_bound, alt_probe, ver) = ("5.3", "5.4", Some("lua5.3"), "5.3");
|
||||
let (incl_bound, excl_bound, alt_probe, ver) =
|
||||
("5.3", "5.4", ["lua5.3", "lua-5.3", "lua53"], "5.3");
|
||||
#[cfg(feature = "lua52")]
|
||||
let (incl_bound, excl_bound, alt_probe, ver) = ("5.2", "5.3", Some("lua5.2"), "5.2");
|
||||
let (incl_bound, excl_bound, alt_probe, ver) =
|
||||
("5.2", "5.3", ["lua5.2", "lua-5.2", "lua52"], "5.2");
|
||||
#[cfg(feature = "lua51")]
|
||||
let (incl_bound, excl_bound, alt_probe, ver) = ("5.1", "5.2", Some("lua5.1"), "5.1");
|
||||
let (incl_bound, excl_bound, alt_probe, ver) =
|
||||
("5.1", "5.2", ["lua5.1", "lua-5.1", "lua51"], "5.1");
|
||||
#[cfg(feature = "luajit")]
|
||||
let (incl_bound, excl_bound, alt_probe, ver) = ("2.0.4", "2.2", None, "JIT");
|
||||
let (incl_bound, excl_bound, alt_probe, ver) = ("2.0.4", "2.2", [], "JIT");
|
||||
|
||||
#[rustfmt::skip]
|
||||
let mut lua = pkg_config::Config::new()
|
||||
@@ -48,10 +52,16 @@ pub fn probe_lua() {
|
||||
.cargo_metadata(true)
|
||||
.probe(if cfg!(feature = "luajit") { "luajit" } else { "lua" });
|
||||
|
||||
if lua.is_err() && alt_probe.is_some() {
|
||||
lua = pkg_config::Config::new()
|
||||
.cargo_metadata(true)
|
||||
.probe(alt_probe.unwrap());
|
||||
if lua.is_err() {
|
||||
for pkg in alt_probe {
|
||||
lua = pkg_config::Config::new()
|
||||
.cargo_metadata(true)
|
||||
.probe(pkg);
|
||||
|
||||
if lua.is_ok() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
lua.unwrap_or_else(|err| panic!("cannot find Lua{ver} using `pkg-config`: {err}"));
|
||||
|
||||
@@ -14,22 +14,29 @@ fn main() {
|
||||
#[cfg(all(feature = "luau", feature = "module", windows))]
|
||||
compile_error!("Luau does not support `module` mode on Windows");
|
||||
|
||||
#[cfg(all(feature = "module", feature = "vendored"))]
|
||||
compile_error!("`vendored` and `module` features are mutually exclusive");
|
||||
#[cfg(any(
|
||||
all(feature = "vendored", any(feature = "external", feature = "module")),
|
||||
all(feature = "external", any(feature = "vendored", feature = "module")),
|
||||
all(feature = "module", any(feature = "vendored", feature = "external"))
|
||||
))]
|
||||
compile_error!("`vendored`, `external` and `module` features are mutually exclusive");
|
||||
|
||||
println!("cargo:rerun-if-changed=build");
|
||||
|
||||
let target_os = env::var("CARGO_CFG_TARGET_OS").unwrap();
|
||||
if target_os == "windows" && cfg!(feature = "module") {
|
||||
if !std::env::var("LUA_LIB_NAME").unwrap_or_default().is_empty() {
|
||||
// Don't use raw-dylib linking
|
||||
find::probe_lua();
|
||||
return;
|
||||
// Check if compilation and linking is handled by external crate
|
||||
if cfg!(not(feature = "external")) {
|
||||
let target_os = env::var("CARGO_CFG_TARGET_OS").unwrap();
|
||||
if target_os == "windows" && cfg!(feature = "module") {
|
||||
if !std::env::var("LUA_LIB_NAME").unwrap_or_default().is_empty() {
|
||||
// Don't use raw-dylib linking
|
||||
find::probe_lua();
|
||||
return;
|
||||
}
|
||||
|
||||
println!("cargo:rustc-cfg=raw_dylib");
|
||||
}
|
||||
|
||||
println!("cargo:rustc-cfg=raw_dylib");
|
||||
#[cfg(not(feature = "module"))]
|
||||
find::probe_lua();
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "module"))]
|
||||
find::probe_lua();
|
||||
}
|
||||
|
||||
+3
-2
@@ -1,7 +1,8 @@
|
||||
//! Low level bindings to Lua 5.4/5.3/5.2/5.1 (including LuaJIT) and Roblox Luau.
|
||||
//! Low level bindings to Lua 5.4/5.3/5.2/5.1 (including LuaJIT) and Luau.
|
||||
|
||||
#![allow(non_camel_case_types, non_snake_case, dead_code)]
|
||||
#![allow(non_camel_case_types, non_snake_case)]
|
||||
#![allow(clippy::missing_safety_doc)]
|
||||
#![allow(unsafe_op_in_unsafe_fn)]
|
||||
#![doc(test(attr(deny(warnings))))]
|
||||
#![cfg_attr(docsrs, feature(doc_cfg))]
|
||||
|
||||
|
||||
@@ -90,7 +90,7 @@ unsafe fn compat53_findfield(L: *mut lua_State, objidx: c_int, level: c_int) ->
|
||||
} else if compat53_findfield(L, objidx, level - 1) != 0 {
|
||||
// try recursively
|
||||
lua_remove(L, -2); // remove table (but keep name)
|
||||
lua_pushliteral(L, ".");
|
||||
lua_pushliteral(L, c".");
|
||||
lua_insert(L, -2); // place '.' between the two names
|
||||
lua_concat(L, 3);
|
||||
return 1;
|
||||
@@ -121,13 +121,13 @@ unsafe fn compat53_pushfuncname(L: *mut lua_State, ar: *mut lua_Debug) {
|
||||
lua_pushfstring(L, cstr!("function '%s'"), (*ar).name);
|
||||
} else if *(*ar).what == b'm' as c_char {
|
||||
// main?
|
||||
lua_pushliteral(L, "main chunk");
|
||||
lua_pushliteral(L, c"main chunk");
|
||||
} else if *(*ar).what == b'C' as c_char {
|
||||
if compat53_pushglobalfuncname(L, ar) != 0 {
|
||||
lua_pushfstring(L, cstr!("function '%s'"), lua_tostring(L, -1));
|
||||
lua_remove(L, -2); // remove name
|
||||
} else {
|
||||
lua_pushliteral(L, "?");
|
||||
lua_pushliteral(L, c"?");
|
||||
}
|
||||
} else {
|
||||
lua_pushfstring(
|
||||
@@ -176,7 +176,7 @@ pub unsafe fn lua_rotate(L: *mut lua_State, mut idx: c_int, mut n: c_int) {
|
||||
#[inline(always)]
|
||||
pub unsafe fn lua_copy(L: *mut lua_State, fromidx: c_int, toidx: c_int) {
|
||||
let abs_to = lua_absindex(L, toidx);
|
||||
luaL_checkstack(L, 1, cstr!("not enough stack slots"));
|
||||
luaL_checkstack(L, 1, cstr!("not enough stack slots available"));
|
||||
lua_pushvalue(L, fromidx);
|
||||
lua_replace(L, abs_to);
|
||||
}
|
||||
@@ -186,7 +186,8 @@ pub unsafe fn lua_isinteger(L: *mut lua_State, idx: c_int) -> c_int {
|
||||
if lua_type(L, idx) == LUA_TNUMBER {
|
||||
let n = lua_tonumber(L, idx);
|
||||
let i = lua_tointeger(L, idx);
|
||||
if (n - i as lua_Number).abs() < lua_Number::EPSILON {
|
||||
// Lua 5.3+ returns "false" for `-0.0`
|
||||
if n.to_bits() == (i as lua_Number).to_bits() {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
@@ -314,7 +315,7 @@ pub unsafe fn lua_rawseti(L: *mut lua_State, idx: c_int, n: lua_Integer) {
|
||||
#[inline(always)]
|
||||
pub unsafe fn lua_rawsetp(L: *mut lua_State, idx: c_int, p: *const c_void) {
|
||||
let abs_i = lua_absindex(L, idx);
|
||||
luaL_checkstack(L, 1, cstr!("not enough stack slots"));
|
||||
luaL_checkstack(L, 1, cstr!("not enough stack slots available"));
|
||||
lua_pushlightuserdata(L, p as *mut c_void);
|
||||
lua_insert(L, -2);
|
||||
lua_rawset(L, abs_i);
|
||||
@@ -377,7 +378,7 @@ pub unsafe fn luaL_checkstack(L: *mut lua_State, sz: c_int, msg: *const c_char)
|
||||
if !msg.is_null() {
|
||||
luaL_error(L, cstr!("stack overflow (%s)"), msg);
|
||||
} else {
|
||||
lua_pushliteral(L, "stack overflow");
|
||||
lua_pushliteral(L, c"stack overflow");
|
||||
lua_error(L);
|
||||
}
|
||||
}
|
||||
@@ -444,7 +445,7 @@ pub unsafe fn luaL_loadbufferx(
|
||||
#[inline(always)]
|
||||
pub unsafe fn luaL_len(L: *mut lua_State, idx: c_int) -> lua_Integer {
|
||||
let mut isnum = 0;
|
||||
luaL_checkstack(L, 1, cstr!("not enough stack slots"));
|
||||
luaL_checkstack(L, 1, cstr!("not enough stack slots available"));
|
||||
lua_len(L, idx);
|
||||
let res = lua_tointegerx(L, -1, &mut isnum);
|
||||
lua_pop(L, 1);
|
||||
@@ -467,12 +468,12 @@ pub unsafe fn luaL_traceback(L: *mut lua_State, L1: *mut lua_State, msg: *const
|
||||
if !msg.is_null() {
|
||||
lua_pushfstring(L, cstr!("%s\n"), msg);
|
||||
}
|
||||
lua_pushliteral(L, "stack traceback:");
|
||||
lua_pushliteral(L, c"stack traceback:");
|
||||
while lua_getstack(L1, level, &mut ar) != 0 {
|
||||
level += 1;
|
||||
if level == mark {
|
||||
// too many levels?
|
||||
lua_pushliteral(L, "\n\t..."); // add a '...'
|
||||
lua_pushliteral(L, c"\n\t..."); // add a '...'
|
||||
level = numlevels - COMPAT53_LEVELS2; // and skip to last ones
|
||||
} else {
|
||||
lua_getinfo(L1, cstr!("Slnt"), &mut ar);
|
||||
@@ -480,7 +481,7 @@ pub unsafe fn luaL_traceback(L: *mut lua_State, L1: *mut lua_State, msg: *const
|
||||
if ar.currentline > 0 {
|
||||
lua_pushfstring(L, cstr!("%d:"), ar.currentline);
|
||||
}
|
||||
lua_pushliteral(L, " in ");
|
||||
lua_pushliteral(L, c" in ");
|
||||
compat53_pushfuncname(L, &mut ar);
|
||||
lua_concat(L, lua_gettop(L) - top);
|
||||
}
|
||||
@@ -493,16 +494,16 @@ pub unsafe fn luaL_tolstring(L: *mut lua_State, mut idx: c_int, len: *mut usize)
|
||||
if luaL_callmeta(L, idx, cstr!("__tostring")) == 0 {
|
||||
match lua_type(L, idx) {
|
||||
LUA_TNIL => {
|
||||
lua_pushliteral(L, "nil");
|
||||
lua_pushliteral(L, c"nil");
|
||||
}
|
||||
LUA_TSTRING | LUA_TNUMBER => {
|
||||
lua_pushvalue(L, idx);
|
||||
}
|
||||
LUA_TBOOLEAN => {
|
||||
if lua_toboolean(L, idx) == 0 {
|
||||
lua_pushliteral(L, "false");
|
||||
lua_pushliteral(L, c"false");
|
||||
} else {
|
||||
lua_pushliteral(L, "true");
|
||||
lua_pushliteral(L, c"true");
|
||||
}
|
||||
}
|
||||
t => {
|
||||
@@ -526,14 +527,14 @@ pub unsafe fn luaL_tolstring(L: *mut lua_State, mut idx: c_int, len: *mut usize)
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn luaL_setmetatable(L: *mut lua_State, tname: *const c_char) {
|
||||
luaL_checkstack(L, 1, cstr!("not enough stack slots"));
|
||||
luaL_checkstack(L, 1, cstr!("not enough stack slots available"));
|
||||
luaL_getmetatable(L, tname);
|
||||
lua_setmetatable(L, -2);
|
||||
}
|
||||
|
||||
pub unsafe fn luaL_getsubtable(L: *mut lua_State, idx: c_int, fname: *const c_char) -> c_int {
|
||||
let abs_i = lua_absindex(L, idx);
|
||||
luaL_checkstack(L, 3, cstr!("not enough stack slots"));
|
||||
luaL_checkstack(L, 3, cstr!("not enough stack slots available"));
|
||||
lua_pushstring_(L, fname);
|
||||
if lua_gettable(L, abs_i) == LUA_TTABLE {
|
||||
return 1;
|
||||
@@ -548,7 +549,7 @@ pub unsafe fn luaL_getsubtable(L: *mut lua_State, idx: c_int, fname: *const c_ch
|
||||
|
||||
pub unsafe fn luaL_requiref(L: *mut lua_State, modname: *const c_char, openf: lua_CFunction, glb: c_int) {
|
||||
luaL_checkstack(L, 3, cstr!("not enough stack slots available"));
|
||||
luaL_getsubtable(L, LUA_REGISTRYINDEX, cstr!("_LOADED"));
|
||||
luaL_getsubtable(L, LUA_REGISTRYINDEX, LUA_LOADED_TABLE);
|
||||
if lua_getfield(L, -1, modname) == LUA_TNIL {
|
||||
lua_pop(L, 1);
|
||||
lua_pushcfunction(L, openf);
|
||||
|
||||
@@ -8,6 +8,9 @@ use super::lua::{self, lua_CFunction, lua_Integer, lua_Number, lua_State};
|
||||
// Extra error code for 'luaL_load'
|
||||
pub const LUA_ERRFILE: c_int = lua::LUA_ERRERR + 1;
|
||||
|
||||
// Key, in the registry, for table of loaded modules
|
||||
pub const LUA_LOADED_TABLE: *const c_char = cstr!("_LOADED");
|
||||
|
||||
#[repr(C)]
|
||||
pub struct luaL_Reg {
|
||||
pub name: *const c_char,
|
||||
@@ -15,7 +18,7 @@ pub struct luaL_Reg {
|
||||
}
|
||||
|
||||
#[cfg_attr(all(windows, raw_dylib), link(name = "lua51", kind = "raw-dylib"))]
|
||||
extern "C-unwind" {
|
||||
unsafe extern "C-unwind" {
|
||||
pub fn luaL_register(L: *mut lua_State, libname: *const c_char, l: *const luaL_Reg);
|
||||
#[link_name = "luaL_getmetafield"]
|
||||
pub fn luaL_getmetafield_(L: *mut lua_State, obj: c_int, e: *const c_char) -> c_int;
|
||||
@@ -58,7 +61,7 @@ pub const LUA_NOREF: c_int = -2;
|
||||
pub const LUA_REFNIL: c_int = -1;
|
||||
|
||||
#[cfg_attr(all(windows, raw_dylib), link(name = "lua51", kind = "raw-dylib"))]
|
||||
extern "C-unwind" {
|
||||
unsafe extern "C-unwind" {
|
||||
pub fn luaL_ref(L: *mut lua_State, t: c_int) -> c_int;
|
||||
pub fn luaL_unref(L: *mut lua_State, t: c_int, r#ref: c_int);
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
//! Contains definitions from `lua.h`.
|
||||
|
||||
use std::ffi::CStr;
|
||||
use std::marker::{PhantomData, PhantomPinned};
|
||||
use std::os::raw::{c_char, c_double, c_int, c_void};
|
||||
use std::ptr;
|
||||
@@ -83,13 +84,13 @@ pub type lua_Reader =
|
||||
pub type lua_Writer =
|
||||
unsafe extern "C-unwind" fn(L: *mut lua_State, p: *const c_void, sz: usize, ud: *mut c_void) -> c_int;
|
||||
|
||||
/// Type for memory-allocation functions
|
||||
/// Type for memory-allocation functions (no unwinding)
|
||||
#[rustfmt::skip]
|
||||
pub type lua_Alloc =
|
||||
unsafe extern "C-unwind" fn(ud: *mut c_void, ptr: *mut c_void, osize: usize, nsize: usize) -> *mut c_void;
|
||||
unsafe extern "C" fn(ud: *mut c_void, ptr: *mut c_void, osize: usize, nsize: usize) -> *mut c_void;
|
||||
|
||||
#[cfg_attr(all(windows, raw_dylib), link(name = "lua51", kind = "raw-dylib"))]
|
||||
extern "C-unwind" {
|
||||
unsafe extern "C-unwind" {
|
||||
//
|
||||
// State manipulation
|
||||
//
|
||||
@@ -219,7 +220,7 @@ pub const LUA_GCSETPAUSE: c_int = 6;
|
||||
pub const LUA_GCSETSTEPMUL: c_int = 7;
|
||||
|
||||
#[cfg_attr(all(windows, raw_dylib), link(name = "lua51", kind = "raw-dylib"))]
|
||||
extern "C-unwind" {
|
||||
unsafe extern "C-unwind" {
|
||||
pub fn lua_gc(L: *mut lua_State, what: c_int, data: c_int) -> c_int;
|
||||
}
|
||||
|
||||
@@ -227,7 +228,7 @@ extern "C-unwind" {
|
||||
// Miscellaneous functions
|
||||
//
|
||||
#[cfg_attr(all(windows, raw_dylib), link(name = "lua51", kind = "raw-dylib"))]
|
||||
extern "C-unwind" {
|
||||
unsafe extern "C-unwind" {
|
||||
#[link_name = "lua_error"]
|
||||
fn lua_error_(L: *mut lua_State) -> c_int;
|
||||
pub fn lua_next(L: *mut lua_State, idx: c_int) -> c_int;
|
||||
@@ -312,10 +313,8 @@ pub unsafe fn lua_isnoneornil(L: *mut lua_State, n: c_int) -> c_int {
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn lua_pushliteral(L: *mut lua_State, s: &'static str) {
|
||||
use std::ffi::CString;
|
||||
let c_str = CString::new(s).unwrap();
|
||||
lua_pushlstring_(L, c_str.as_ptr(), c_str.as_bytes().len())
|
||||
pub unsafe fn lua_pushliteral(L: *mut lua_State, s: &'static CStr) {
|
||||
lua_pushstring_(L, s.as_ptr());
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
@@ -371,7 +370,7 @@ pub const LUA_MASKCOUNT: c_int = 1 << (LUA_HOOKCOUNT as usize);
|
||||
pub type lua_Hook = unsafe extern "C-unwind" fn(L: *mut lua_State, ar: *mut lua_Debug);
|
||||
|
||||
#[cfg_attr(all(windows, raw_dylib), link(name = "lua51", kind = "raw-dylib"))]
|
||||
extern "C-unwind" {
|
||||
unsafe extern "C-unwind" {
|
||||
pub fn lua_getstack(L: *mut lua_State, level: c_int, ar: *mut lua_Debug) -> c_int;
|
||||
pub fn lua_getinfo(L: *mut lua_State, what: *const c_char, ar: *mut lua_Debug) -> c_int;
|
||||
pub fn lua_getlocal(L: *mut lua_State, ar: *const lua_Debug, n: c_int) -> *const c_char;
|
||||
|
||||
@@ -1,27 +1,27 @@
|
||||
//! Contains definitions from `lualib.h`.
|
||||
|
||||
use std::os::raw::c_int;
|
||||
use std::os::raw::{c_char, c_int};
|
||||
|
||||
use super::lua::lua_State;
|
||||
|
||||
pub const LUA_COLIBNAME: &str = "coroutine";
|
||||
pub const LUA_TABLIBNAME: &str = "table";
|
||||
pub const LUA_IOLIBNAME: &str = "io";
|
||||
pub const LUA_OSLIBNAME: &str = "os";
|
||||
pub const LUA_STRLIBNAME: &str = "string";
|
||||
pub const LUA_MATHLIBNAME: &str = "math";
|
||||
pub const LUA_DBLIBNAME: &str = "debug";
|
||||
pub const LUA_LOADLIBNAME: &str = "package";
|
||||
pub const LUA_COLIBNAME: *const c_char = cstr!("coroutine");
|
||||
pub const LUA_TABLIBNAME: *const c_char = cstr!("table");
|
||||
pub const LUA_IOLIBNAME: *const c_char = cstr!("io");
|
||||
pub const LUA_OSLIBNAME: *const c_char = cstr!("os");
|
||||
pub const LUA_STRLIBNAME: *const c_char = cstr!("string");
|
||||
pub const LUA_MATHLIBNAME: *const c_char = cstr!("math");
|
||||
pub const LUA_DBLIBNAME: *const c_char = cstr!("debug");
|
||||
pub const LUA_LOADLIBNAME: *const c_char = cstr!("package");
|
||||
|
||||
#[cfg(feature = "luajit")]
|
||||
pub const LUA_BITLIBNAME: &str = "bit";
|
||||
pub const LUA_BITLIBNAME: *const c_char = cstr!("bit");
|
||||
#[cfg(feature = "luajit")]
|
||||
pub const LUA_JITLIBNAME: &str = "jit";
|
||||
pub const LUA_JITLIBNAME: *const c_char = cstr!("jit");
|
||||
#[cfg(feature = "luajit")]
|
||||
pub const LUA_FFILIBNAME: &str = "ffi";
|
||||
pub const LUA_FFILIBNAME: *const c_char = cstr!("ffi");
|
||||
|
||||
#[cfg_attr(all(windows, raw_dylib), link(name = "lua51", kind = "raw-dylib"))]
|
||||
extern "C-unwind" {
|
||||
unsafe extern "C-unwind" {
|
||||
pub fn luaopen_base(L: *mut lua_State) -> c_int;
|
||||
pub fn luaopen_table(L: *mut lua_State) -> c_int;
|
||||
pub fn luaopen_io(L: *mut lua_State) -> c_int;
|
||||
|
||||
@@ -51,7 +51,8 @@ pub unsafe fn lua_isinteger(L: *mut lua_State, idx: c_int) -> c_int {
|
||||
if lua_type(L, idx) == LUA_TNUMBER {
|
||||
let n = lua_tonumber(L, idx);
|
||||
let i = lua_tointeger(L, idx);
|
||||
if (n - i as lua_Number).abs() < lua_Number::EPSILON {
|
||||
// Lua 5.3+ returns "false" for `-0.0`
|
||||
if n.to_bits() == (i as lua_Number).to_bits() {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
@@ -199,16 +200,16 @@ pub unsafe fn luaL_tolstring(L: *mut lua_State, mut idx: c_int, len: *mut usize)
|
||||
if luaL_callmeta(L, idx, cstr!("__tostring")) == 0 {
|
||||
match lua_type(L, idx) {
|
||||
LUA_TNIL => {
|
||||
lua_pushliteral(L, "nil");
|
||||
lua_pushliteral(L, c"nil");
|
||||
}
|
||||
LUA_TSTRING | LUA_TNUMBER => {
|
||||
lua_pushvalue(L, idx);
|
||||
}
|
||||
LUA_TBOOLEAN => {
|
||||
if lua_toboolean(L, idx) == 0 {
|
||||
lua_pushliteral(L, "false");
|
||||
lua_pushliteral(L, c"false");
|
||||
} else {
|
||||
lua_pushliteral(L, "true");
|
||||
lua_pushliteral(L, c"true");
|
||||
}
|
||||
}
|
||||
t => {
|
||||
@@ -232,7 +233,7 @@ pub unsafe fn luaL_tolstring(L: *mut lua_State, mut idx: c_int, len: *mut usize)
|
||||
|
||||
pub unsafe fn luaL_requiref(L: *mut lua_State, modname: *const c_char, openf: lua_CFunction, glb: c_int) {
|
||||
luaL_checkstack(L, 3, cstr!("not enough stack slots available"));
|
||||
luaL_getsubtable(L, LUA_REGISTRYINDEX, cstr!("_LOADED"));
|
||||
luaL_getsubtable(L, LUA_REGISTRYINDEX, LUA_LOADED_TABLE);
|
||||
if lua_getfield(L, -1, modname) == LUA_TNIL {
|
||||
lua_pop(L, 1);
|
||||
lua_pushcfunction(L, openf);
|
||||
|
||||
@@ -8,6 +8,12 @@ use super::lua::{self, lua_CFunction, lua_Integer, lua_Number, lua_State, lua_Un
|
||||
// Extra error code for 'luaL_load'
|
||||
pub const LUA_ERRFILE: c_int = lua::LUA_ERRERR + 1;
|
||||
|
||||
// Key, in the registry, for table of loaded modules
|
||||
pub const LUA_LOADED_TABLE: *const c_char = cstr!("_LOADED");
|
||||
|
||||
// Key, in the registry, for table of preloaded loaders
|
||||
pub const LUA_PRELOAD_TABLE: *const c_char = cstr!("_PRELOAD");
|
||||
|
||||
#[repr(C)]
|
||||
pub struct luaL_Reg {
|
||||
pub name: *const c_char,
|
||||
@@ -15,7 +21,7 @@ pub struct luaL_Reg {
|
||||
}
|
||||
|
||||
#[cfg_attr(all(windows, raw_dylib), link(name = "lua52", kind = "raw-dylib"))]
|
||||
extern "C-unwind" {
|
||||
unsafe extern "C-unwind" {
|
||||
pub fn luaL_checkversion_(L: *mut lua_State, ver: lua_Number);
|
||||
|
||||
#[link_name = "luaL_getmetafield"]
|
||||
@@ -63,7 +69,7 @@ pub const LUA_NOREF: c_int = -2;
|
||||
pub const LUA_REFNIL: c_int = -1;
|
||||
|
||||
#[cfg_attr(all(windows, raw_dylib), link(name = "lua52", kind = "raw-dylib"))]
|
||||
extern "C-unwind" {
|
||||
unsafe extern "C-unwind" {
|
||||
pub fn luaL_ref(L: *mut lua_State, t: c_int) -> c_int;
|
||||
pub fn luaL_unref(L: *mut lua_State, t: c_int, r#ref: c_int);
|
||||
|
||||
@@ -76,7 +82,7 @@ pub unsafe fn luaL_loadfile(L: *mut lua_State, f: *const c_char) -> c_int {
|
||||
}
|
||||
|
||||
#[cfg_attr(all(windows, raw_dylib), link(name = "lua52", kind = "raw-dylib"))]
|
||||
extern "C-unwind" {
|
||||
unsafe extern "C-unwind" {
|
||||
pub fn luaL_loadbufferx(
|
||||
L: *mut lua_State,
|
||||
buff: *const c_char,
|
||||
|
||||
+12
-13
@@ -1,5 +1,6 @@
|
||||
//! Contains definitions from `lua.h`.
|
||||
|
||||
use std::ffi::CStr;
|
||||
use std::marker::{PhantomData, PhantomPinned};
|
||||
use std::os::raw::{c_char, c_double, c_int, c_uchar, c_uint, c_void};
|
||||
use std::ptr;
|
||||
@@ -88,13 +89,13 @@ pub type lua_Reader =
|
||||
pub type lua_Writer =
|
||||
unsafe extern "C-unwind" fn(L: *mut lua_State, p: *const c_void, sz: usize, ud: *mut c_void) -> c_int;
|
||||
|
||||
/// Type for memory-allocation functions
|
||||
/// Type for memory-allocation functions (no unwinding)
|
||||
#[rustfmt::skip]
|
||||
pub type lua_Alloc =
|
||||
unsafe extern "C-unwind" fn(ud: *mut c_void, ptr: *mut c_void, osize: usize, nsize: usize) -> *mut c_void;
|
||||
unsafe extern "C" fn(ud: *mut c_void, ptr: *mut c_void, osize: usize, nsize: usize) -> *mut c_void;
|
||||
|
||||
#[cfg_attr(all(windows, raw_dylib), link(name = "lua52", kind = "raw-dylib"))]
|
||||
extern "C-unwind" {
|
||||
unsafe extern "C-unwind" {
|
||||
//
|
||||
// State manipulation
|
||||
//
|
||||
@@ -160,14 +161,14 @@ pub const LUA_OPLT: c_int = 1;
|
||||
pub const LUA_OPLE: c_int = 2;
|
||||
|
||||
#[cfg_attr(all(windows, raw_dylib), link(name = "lua52", kind = "raw-dylib"))]
|
||||
extern "C-unwind" {
|
||||
unsafe extern "C-unwind" {
|
||||
pub fn lua_arith(L: *mut lua_State, op: c_int);
|
||||
pub fn lua_rawequal(L: *mut lua_State, idx1: c_int, idx2: c_int) -> c_int;
|
||||
pub fn lua_compare(L: *mut lua_State, idx1: c_int, idx2: c_int, op: c_int) -> c_int;
|
||||
}
|
||||
|
||||
#[cfg_attr(all(windows, raw_dylib), link(name = "lua52", kind = "raw-dylib"))]
|
||||
extern "C-unwind" {
|
||||
unsafe extern "C-unwind" {
|
||||
//
|
||||
// Push functions (C -> stack)
|
||||
//
|
||||
@@ -256,7 +257,7 @@ pub unsafe fn lua_pcall(L: *mut lua_State, n: c_int, r: c_int, f: c_int) -> c_in
|
||||
}
|
||||
|
||||
#[cfg_attr(all(windows, raw_dylib), link(name = "lua52", kind = "raw-dylib"))]
|
||||
extern "C-unwind" {
|
||||
unsafe extern "C-unwind" {
|
||||
//
|
||||
// Coroutine functions
|
||||
//
|
||||
@@ -288,12 +289,12 @@ pub const LUA_GCGEN: c_int = 10;
|
||||
pub const LUA_GCINC: c_int = 11;
|
||||
|
||||
#[cfg_attr(all(windows, raw_dylib), link(name = "lua52", kind = "raw-dylib"))]
|
||||
extern "C-unwind" {
|
||||
unsafe extern "C-unwind" {
|
||||
pub fn lua_gc(L: *mut lua_State, what: c_int, data: c_int) -> c_int;
|
||||
}
|
||||
|
||||
#[cfg_attr(all(windows, raw_dylib), link(name = "lua52", kind = "raw-dylib"))]
|
||||
extern "C-unwind" {
|
||||
unsafe extern "C-unwind" {
|
||||
//
|
||||
// Miscellaneous functions
|
||||
//
|
||||
@@ -395,10 +396,8 @@ pub unsafe fn lua_isnoneornil(L: *mut lua_State, n: c_int) -> c_int {
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn lua_pushliteral(L: *mut lua_State, s: &'static str) -> *const c_char {
|
||||
use std::ffi::CString;
|
||||
let c_str = CString::new(s).unwrap();
|
||||
lua_pushlstring_(L, c_str.as_ptr(), c_str.as_bytes().len())
|
||||
pub unsafe fn lua_pushliteral(L: *mut lua_State, s: &'static CStr) {
|
||||
lua_pushstring(L, s.as_ptr());
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
@@ -449,7 +448,7 @@ pub const LUA_MASKCOUNT: c_int = 1 << (LUA_HOOKCOUNT as usize);
|
||||
pub type lua_Hook = unsafe extern "C-unwind" fn(L: *mut lua_State, ar: *mut lua_Debug);
|
||||
|
||||
#[cfg_attr(all(windows, raw_dylib), link(name = "lua52", kind = "raw-dylib"))]
|
||||
extern "C-unwind" {
|
||||
unsafe extern "C-unwind" {
|
||||
pub fn lua_getstack(L: *mut lua_State, level: c_int, ar: *mut lua_Debug) -> c_int;
|
||||
pub fn lua_getinfo(L: *mut lua_State, what: *const c_char, ar: *mut lua_Debug) -> c_int;
|
||||
pub fn lua_getlocal(L: *mut lua_State, ar: *const lua_Debug, n: c_int) -> *const c_char;
|
||||
|
||||
@@ -1,21 +1,21 @@
|
||||
//! Contains definitions from `lualib.h`.
|
||||
|
||||
use std::os::raw::c_int;
|
||||
use std::os::raw::{c_char, c_int};
|
||||
|
||||
use super::lua::lua_State;
|
||||
|
||||
pub const LUA_COLIBNAME: &str = "coroutine";
|
||||
pub const LUA_TABLIBNAME: &str = "table";
|
||||
pub const LUA_IOLIBNAME: &str = "io";
|
||||
pub const LUA_OSLIBNAME: &str = "os";
|
||||
pub const LUA_STRLIBNAME: &str = "string";
|
||||
pub const LUA_BITLIBNAME: &str = "bit32";
|
||||
pub const LUA_MATHLIBNAME: &str = "math";
|
||||
pub const LUA_DBLIBNAME: &str = "debug";
|
||||
pub const LUA_LOADLIBNAME: &str = "package";
|
||||
pub const LUA_COLIBNAME: *const c_char = cstr!("coroutine");
|
||||
pub const LUA_TABLIBNAME: *const c_char = cstr!("table");
|
||||
pub const LUA_IOLIBNAME: *const c_char = cstr!("io");
|
||||
pub const LUA_OSLIBNAME: *const c_char = cstr!("os");
|
||||
pub const LUA_STRLIBNAME: *const c_char = cstr!("string");
|
||||
pub const LUA_BITLIBNAME: *const c_char = cstr!("bit32");
|
||||
pub const LUA_MATHLIBNAME: *const c_char = cstr!("math");
|
||||
pub const LUA_DBLIBNAME: *const c_char = cstr!("debug");
|
||||
pub const LUA_LOADLIBNAME: *const c_char = cstr!("package");
|
||||
|
||||
#[cfg_attr(all(windows, raw_dylib), link(name = "lua52", kind = "raw-dylib"))]
|
||||
extern "C-unwind" {
|
||||
unsafe extern "C-unwind" {
|
||||
pub fn luaopen_base(L: *mut lua_State) -> c_int;
|
||||
pub fn luaopen_coroutine(L: *mut lua_State) -> c_int;
|
||||
pub fn luaopen_table(L: *mut lua_State) -> c_int;
|
||||
|
||||
@@ -9,10 +9,10 @@ use super::lua::{self, lua_CFunction, lua_Integer, lua_Number, lua_State};
|
||||
pub const LUA_ERRFILE: c_int = lua::LUA_ERRERR + 1;
|
||||
|
||||
// Key, in the registry, for table of loaded modules
|
||||
pub const LUA_LOADED_TABLE: &str = "_LOADED";
|
||||
pub const LUA_LOADED_TABLE: *const c_char = cstr!("_LOADED");
|
||||
|
||||
// Key, in the registry, for table of preloaded loaders
|
||||
pub const LUA_PRELOAD_TABLE: &str = "_PRELOAD";
|
||||
pub const LUA_PRELOAD_TABLE: *const c_char = cstr!("_PRELOAD");
|
||||
|
||||
#[repr(C)]
|
||||
pub struct luaL_Reg {
|
||||
@@ -21,7 +21,7 @@ pub struct luaL_Reg {
|
||||
}
|
||||
|
||||
#[cfg_attr(all(windows, raw_dylib), link(name = "lua53", kind = "raw-dylib"))]
|
||||
extern "C-unwind" {
|
||||
unsafe extern "C-unwind" {
|
||||
pub fn luaL_checkversion_(L: *mut lua_State, ver: lua_Number, sz: usize);
|
||||
|
||||
pub fn luaL_getmetafield(L: *mut lua_State, obj: c_int, e: *const c_char) -> c_int;
|
||||
@@ -65,7 +65,7 @@ pub const LUA_NOREF: c_int = -2;
|
||||
pub const LUA_REFNIL: c_int = -1;
|
||||
|
||||
#[cfg_attr(all(windows, raw_dylib), link(name = "lua53", kind = "raw-dylib"))]
|
||||
extern "C-unwind" {
|
||||
unsafe extern "C-unwind" {
|
||||
pub fn luaL_ref(L: *mut lua_State, t: c_int) -> c_int;
|
||||
pub fn luaL_unref(L: *mut lua_State, t: c_int, r#ref: c_int);
|
||||
|
||||
@@ -78,7 +78,7 @@ pub unsafe fn luaL_loadfile(L: *mut lua_State, f: *const c_char) -> c_int {
|
||||
}
|
||||
|
||||
#[cfg_attr(all(windows, raw_dylib), link(name = "lua53", kind = "raw-dylib"))]
|
||||
extern "C-unwind" {
|
||||
unsafe extern "C-unwind" {
|
||||
pub fn luaL_loadbufferx(
|
||||
L: *mut lua_State,
|
||||
buff: *const c_char,
|
||||
|
||||
+12
-13
@@ -1,5 +1,6 @@
|
||||
//! Contains definitions from `lua.h`.
|
||||
|
||||
use std::ffi::CStr;
|
||||
use std::marker::{PhantomData, PhantomPinned};
|
||||
use std::os::raw::{c_char, c_double, c_int, c_uchar, c_void};
|
||||
use std::{mem, ptr};
|
||||
@@ -95,13 +96,13 @@ pub type lua_Reader =
|
||||
pub type lua_Writer =
|
||||
unsafe extern "C-unwind" fn(L: *mut lua_State, p: *const c_void, sz: usize, ud: *mut c_void) -> c_int;
|
||||
|
||||
/// Type for memory-allocation functions
|
||||
/// Type for memory-allocation functions (no unwinding)
|
||||
#[rustfmt::skip]
|
||||
pub type lua_Alloc =
|
||||
unsafe extern "C-unwind" fn(ud: *mut c_void, ptr: *mut c_void, osize: usize, nsize: usize) -> *mut c_void;
|
||||
unsafe extern "C" fn(ud: *mut c_void, ptr: *mut c_void, osize: usize, nsize: usize) -> *mut c_void;
|
||||
|
||||
#[cfg_attr(all(windows, raw_dylib), link(name = "lua53", kind = "raw-dylib"))]
|
||||
extern "C-unwind" {
|
||||
unsafe extern "C-unwind" {
|
||||
//
|
||||
// State manipulation
|
||||
//
|
||||
@@ -171,14 +172,14 @@ pub const LUA_OPLT: c_int = 1;
|
||||
pub const LUA_OPLE: c_int = 2;
|
||||
|
||||
#[cfg_attr(all(windows, raw_dylib), link(name = "lua53", kind = "raw-dylib"))]
|
||||
extern "C-unwind" {
|
||||
unsafe extern "C-unwind" {
|
||||
pub fn lua_arith(L: *mut lua_State, op: c_int);
|
||||
pub fn lua_rawequal(L: *mut lua_State, idx1: c_int, idx2: c_int) -> c_int;
|
||||
pub fn lua_compare(L: *mut lua_State, idx1: c_int, idx2: c_int, op: c_int) -> c_int;
|
||||
}
|
||||
|
||||
#[cfg_attr(all(windows, raw_dylib), link(name = "lua53", kind = "raw-dylib"))]
|
||||
extern "C-unwind" {
|
||||
unsafe extern "C-unwind" {
|
||||
//
|
||||
// Push functions (C -> stack)
|
||||
//
|
||||
@@ -264,7 +265,7 @@ pub unsafe fn lua_pcall(L: *mut lua_State, n: c_int, r: c_int, f: c_int) -> c_in
|
||||
}
|
||||
|
||||
#[cfg_attr(all(windows, raw_dylib), link(name = "lua53", kind = "raw-dylib"))]
|
||||
extern "C-unwind" {
|
||||
unsafe extern "C-unwind" {
|
||||
//
|
||||
// Coroutine functions
|
||||
//
|
||||
@@ -299,12 +300,12 @@ pub const LUA_GCSETSTEPMUL: c_int = 7;
|
||||
pub const LUA_GCISRUNNING: c_int = 9;
|
||||
|
||||
#[cfg_attr(all(windows, raw_dylib), link(name = "lua53", kind = "raw-dylib"))]
|
||||
extern "C-unwind" {
|
||||
unsafe extern "C-unwind" {
|
||||
pub fn lua_gc(L: *mut lua_State, what: c_int, data: c_int) -> c_int;
|
||||
}
|
||||
|
||||
#[cfg_attr(all(windows, raw_dylib), link(name = "lua53", kind = "raw-dylib"))]
|
||||
extern "C-unwind" {
|
||||
unsafe extern "C-unwind" {
|
||||
//
|
||||
// Miscellaneous functions
|
||||
//
|
||||
@@ -407,10 +408,8 @@ pub unsafe fn lua_isnoneornil(L: *mut lua_State, n: c_int) -> c_int {
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn lua_pushliteral(L: *mut lua_State, s: &'static str) -> *const c_char {
|
||||
use std::ffi::CString;
|
||||
let c_str = CString::new(s).unwrap();
|
||||
lua_pushlstring(L, c_str.as_ptr(), c_str.as_bytes().len())
|
||||
pub unsafe fn lua_pushliteral(L: *mut lua_State, s: &'static CStr) {
|
||||
lua_pushstring(L, s.as_ptr());
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
@@ -478,7 +477,7 @@ pub const LUA_MASKCOUNT: c_int = 1 << (LUA_HOOKCOUNT as usize);
|
||||
pub type lua_Hook = unsafe extern "C-unwind" fn(L: *mut lua_State, ar: *mut lua_Debug);
|
||||
|
||||
#[cfg_attr(all(windows, raw_dylib), link(name = "lua53", kind = "raw-dylib"))]
|
||||
extern "C-unwind" {
|
||||
unsafe extern "C-unwind" {
|
||||
pub fn lua_getstack(L: *mut lua_State, level: c_int, ar: *mut lua_Debug) -> c_int;
|
||||
pub fn lua_getinfo(L: *mut lua_State, what: *const c_char, ar: *mut lua_Debug) -> c_int;
|
||||
pub fn lua_getlocal(L: *mut lua_State, ar: *const lua_Debug, n: c_int) -> *const c_char;
|
||||
|
||||
@@ -1,22 +1,22 @@
|
||||
//! Contains definitions from `lualib.h`.
|
||||
|
||||
use std::os::raw::c_int;
|
||||
use std::os::raw::{c_char, c_int};
|
||||
|
||||
use super::lua::lua_State;
|
||||
|
||||
pub const LUA_COLIBNAME: &str = "coroutine";
|
||||
pub const LUA_TABLIBNAME: &str = "table";
|
||||
pub const LUA_IOLIBNAME: &str = "io";
|
||||
pub const LUA_OSLIBNAME: &str = "os";
|
||||
pub const LUA_STRLIBNAME: &str = "string";
|
||||
pub const LUA_UTF8LIBNAME: &str = "utf8";
|
||||
pub const LUA_BITLIBNAME: &str = "bit32";
|
||||
pub const LUA_MATHLIBNAME: &str = "math";
|
||||
pub const LUA_DBLIBNAME: &str = "debug";
|
||||
pub const LUA_LOADLIBNAME: &str = "package";
|
||||
pub const LUA_COLIBNAME: *const c_char = cstr!("coroutine");
|
||||
pub const LUA_TABLIBNAME: *const c_char = cstr!("table");
|
||||
pub const LUA_IOLIBNAME: *const c_char = cstr!("io");
|
||||
pub const LUA_OSLIBNAME: *const c_char = cstr!("os");
|
||||
pub const LUA_STRLIBNAME: *const c_char = cstr!("string");
|
||||
pub const LUA_UTF8LIBNAME: *const c_char = cstr!("utf8");
|
||||
pub const LUA_BITLIBNAME: *const c_char = cstr!("bit32");
|
||||
pub const LUA_MATHLIBNAME: *const c_char = cstr!("math");
|
||||
pub const LUA_DBLIBNAME: *const c_char = cstr!("debug");
|
||||
pub const LUA_LOADLIBNAME: *const c_char = cstr!("package");
|
||||
|
||||
#[cfg_attr(all(windows, raw_dylib), link(name = "lua53", kind = "raw-dylib"))]
|
||||
extern "C-unwind" {
|
||||
unsafe extern "C-unwind" {
|
||||
pub fn luaopen_base(L: *mut lua_State) -> c_int;
|
||||
pub fn luaopen_coroutine(L: *mut lua_State) -> c_int;
|
||||
pub fn luaopen_table(L: *mut lua_State) -> c_int;
|
||||
|
||||
@@ -9,10 +9,10 @@ use super::lua::{self, lua_CFunction, lua_Integer, lua_Number, lua_State};
|
||||
pub const LUA_ERRFILE: c_int = lua::LUA_ERRERR + 1;
|
||||
|
||||
// Key, in the registry, for table of loaded modules
|
||||
pub const LUA_LOADED_TABLE: &str = "_LOADED";
|
||||
pub const LUA_LOADED_TABLE: *const c_char = cstr!("_LOADED");
|
||||
|
||||
// Key, in the registry, for table of preloaded loaders
|
||||
pub const LUA_PRELOAD_TABLE: &str = "_PRELOAD";
|
||||
pub const LUA_PRELOAD_TABLE: *const c_char = cstr!("_PRELOAD");
|
||||
|
||||
#[repr(C)]
|
||||
pub struct luaL_Reg {
|
||||
@@ -21,7 +21,7 @@ pub struct luaL_Reg {
|
||||
}
|
||||
|
||||
#[cfg_attr(all(windows, raw_dylib), link(name = "lua54", kind = "raw-dylib"))]
|
||||
extern "C-unwind" {
|
||||
unsafe extern "C-unwind" {
|
||||
pub fn luaL_checkversion_(L: *mut lua_State, ver: lua_Number, sz: usize);
|
||||
|
||||
pub fn luaL_getmetafield(L: *mut lua_State, obj: c_int, e: *const c_char) -> c_int;
|
||||
@@ -64,7 +64,7 @@ pub const LUA_NOREF: c_int = -2;
|
||||
pub const LUA_REFNIL: c_int = -1;
|
||||
|
||||
#[cfg_attr(all(windows, raw_dylib), link(name = "lua54", kind = "raw-dylib"))]
|
||||
extern "C-unwind" {
|
||||
unsafe extern "C-unwind" {
|
||||
pub fn luaL_ref(L: *mut lua_State, t: c_int) -> c_int;
|
||||
pub fn luaL_unref(L: *mut lua_State, t: c_int, r#ref: c_int);
|
||||
|
||||
@@ -77,7 +77,7 @@ pub unsafe fn luaL_loadfile(L: *mut lua_State, f: *const c_char) -> c_int {
|
||||
}
|
||||
|
||||
#[cfg_attr(all(windows, raw_dylib), link(name = "lua54", kind = "raw-dylib"))]
|
||||
extern "C-unwind" {
|
||||
unsafe extern "C-unwind" {
|
||||
pub fn luaL_loadbufferx(
|
||||
L: *mut lua_State,
|
||||
buff: *const c_char,
|
||||
|
||||
+13
-14
@@ -1,5 +1,6 @@
|
||||
//! Contains definitions from `lua.h`.
|
||||
|
||||
use std::ffi::CStr;
|
||||
use std::marker::{PhantomData, PhantomPinned};
|
||||
use std::os::raw::{c_char, c_double, c_int, c_uchar, c_ushort, c_void};
|
||||
use std::{mem, ptr};
|
||||
@@ -94,16 +95,16 @@ pub type lua_Reader =
|
||||
pub type lua_Writer =
|
||||
unsafe extern "C-unwind" fn(L: *mut lua_State, p: *const c_void, sz: usize, ud: *mut c_void) -> c_int;
|
||||
|
||||
/// Type for memory-allocation functions
|
||||
/// Type for memory-allocation functions (no unwinding)
|
||||
#[rustfmt::skip]
|
||||
pub type lua_Alloc =
|
||||
unsafe extern "C-unwind" fn(ud: *mut c_void, ptr: *mut c_void, osize: usize, nsize: usize) -> *mut c_void;
|
||||
unsafe extern "C" fn(ud: *mut c_void, ptr: *mut c_void, osize: usize, nsize: usize) -> *mut c_void;
|
||||
|
||||
/// Type for warning functions
|
||||
pub type lua_WarnFunction = unsafe extern "C-unwind" fn(ud: *mut c_void, msg: *const c_char, tocont: c_int);
|
||||
|
||||
#[cfg_attr(all(windows, raw_dylib), link(name = "lua54", kind = "raw-dylib"))]
|
||||
extern "C-unwind" {
|
||||
unsafe extern "C-unwind" {
|
||||
//
|
||||
// State manipulation
|
||||
//
|
||||
@@ -185,14 +186,14 @@ pub const LUA_OPLT: c_int = 1;
|
||||
pub const LUA_OPLE: c_int = 2;
|
||||
|
||||
#[cfg_attr(all(windows, raw_dylib), link(name = "lua54", kind = "raw-dylib"))]
|
||||
extern "C-unwind" {
|
||||
unsafe extern "C-unwind" {
|
||||
pub fn lua_arith(L: *mut lua_State, op: c_int);
|
||||
pub fn lua_rawequal(L: *mut lua_State, idx1: c_int, idx2: c_int) -> c_int;
|
||||
pub fn lua_compare(L: *mut lua_State, idx1: c_int, idx2: c_int, op: c_int) -> c_int;
|
||||
}
|
||||
|
||||
#[cfg_attr(all(windows, raw_dylib), link(name = "lua54", kind = "raw-dylib"))]
|
||||
extern "C-unwind" {
|
||||
unsafe extern "C-unwind" {
|
||||
//
|
||||
// Push functions (C -> stack)
|
||||
//
|
||||
@@ -278,7 +279,7 @@ pub unsafe fn lua_pcall(L: *mut lua_State, n: c_int, r: c_int, f: c_int) -> c_in
|
||||
}
|
||||
|
||||
#[cfg_attr(all(windows, raw_dylib), link(name = "lua54", kind = "raw-dylib"))]
|
||||
extern "C-unwind" {
|
||||
unsafe extern "C-unwind" {
|
||||
//
|
||||
// Coroutine functions
|
||||
//
|
||||
@@ -302,7 +303,7 @@ pub unsafe fn lua_yield(L: *mut lua_State, n: c_int) -> c_int {
|
||||
// Warning-related functions
|
||||
//
|
||||
#[cfg_attr(all(windows, raw_dylib), link(name = "lua54", kind = "raw-dylib"))]
|
||||
extern "C-unwind" {
|
||||
unsafe extern "C-unwind" {
|
||||
pub fn lua_setwarnf(L: *mut lua_State, f: Option<lua_WarnFunction>, ud: *mut c_void);
|
||||
pub fn lua_warning(L: *mut lua_State, msg: *const c_char, tocont: c_int);
|
||||
}
|
||||
@@ -323,12 +324,12 @@ pub const LUA_GCGEN: c_int = 10;
|
||||
pub const LUA_GCINC: c_int = 11;
|
||||
|
||||
#[cfg_attr(all(windows, raw_dylib), link(name = "lua54", kind = "raw-dylib"))]
|
||||
extern "C-unwind" {
|
||||
unsafe extern "C-unwind" {
|
||||
pub fn lua_gc(L: *mut lua_State, what: c_int, ...) -> c_int;
|
||||
}
|
||||
|
||||
#[cfg_attr(all(windows, raw_dylib), link(name = "lua54", kind = "raw-dylib"))]
|
||||
extern "C-unwind" {
|
||||
unsafe extern "C-unwind" {
|
||||
//
|
||||
// Miscellaneous functions
|
||||
//
|
||||
@@ -434,10 +435,8 @@ pub unsafe fn lua_isnoneornil(L: *mut lua_State, n: c_int) -> c_int {
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn lua_pushliteral(L: *mut lua_State, s: &'static str) -> *const c_char {
|
||||
use std::ffi::CString;
|
||||
let c_str = CString::new(s).unwrap();
|
||||
lua_pushlstring(L, c_str.as_ptr(), c_str.as_bytes().len())
|
||||
pub unsafe fn lua_pushliteral(L: *mut lua_State, s: &'static CStr) {
|
||||
lua_pushstring(L, s.as_ptr());
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
@@ -520,7 +519,7 @@ pub const LUA_MASKCOUNT: c_int = 1 << (LUA_HOOKCOUNT as usize);
|
||||
pub type lua_Hook = unsafe extern "C-unwind" fn(L: *mut lua_State, ar: *mut lua_Debug);
|
||||
|
||||
#[cfg_attr(all(windows, raw_dylib), link(name = "lua54", kind = "raw-dylib"))]
|
||||
extern "C-unwind" {
|
||||
unsafe extern "C-unwind" {
|
||||
pub fn lua_getstack(L: *mut lua_State, level: c_int, ar: *mut lua_Debug) -> c_int;
|
||||
pub fn lua_getinfo(L: *mut lua_State, what: *const c_char, ar: *mut lua_Debug) -> c_int;
|
||||
pub fn lua_getlocal(L: *mut lua_State, ar: *const lua_Debug, n: c_int) -> *const c_char;
|
||||
|
||||
@@ -1,21 +1,21 @@
|
||||
//! Contains definitions from `lualib.h`.
|
||||
|
||||
use std::os::raw::c_int;
|
||||
use std::os::raw::{c_char, c_int};
|
||||
|
||||
use super::lua::lua_State;
|
||||
|
||||
pub const LUA_COLIBNAME: &str = "coroutine";
|
||||
pub const LUA_TABLIBNAME: &str = "table";
|
||||
pub const LUA_IOLIBNAME: &str = "io";
|
||||
pub const LUA_OSLIBNAME: &str = "os";
|
||||
pub const LUA_STRLIBNAME: &str = "string";
|
||||
pub const LUA_UTF8LIBNAME: &str = "utf8";
|
||||
pub const LUA_MATHLIBNAME: &str = "math";
|
||||
pub const LUA_DBLIBNAME: &str = "debug";
|
||||
pub const LUA_LOADLIBNAME: &str = "package";
|
||||
pub const LUA_COLIBNAME: *const c_char = cstr!("coroutine");
|
||||
pub const LUA_TABLIBNAME: *const c_char = cstr!("table");
|
||||
pub const LUA_IOLIBNAME: *const c_char = cstr!("io");
|
||||
pub const LUA_OSLIBNAME: *const c_char = cstr!("os");
|
||||
pub const LUA_STRLIBNAME: *const c_char = cstr!("string");
|
||||
pub const LUA_UTF8LIBNAME: *const c_char = cstr!("utf8");
|
||||
pub const LUA_MATHLIBNAME: *const c_char = cstr!("math");
|
||||
pub const LUA_DBLIBNAME: *const c_char = cstr!("debug");
|
||||
pub const LUA_LOADLIBNAME: *const c_char = cstr!("package");
|
||||
|
||||
#[cfg_attr(all(windows, raw_dylib), link(name = "lua54", kind = "raw-dylib"))]
|
||||
extern "C-unwind" {
|
||||
unsafe extern "C-unwind" {
|
||||
pub fn luaopen_base(L: *mut lua_State) -> c_int;
|
||||
pub fn luaopen_coroutine(L: *mut lua_State) -> c_int;
|
||||
pub fn luaopen_table(L: *mut lua_State) -> c_int;
|
||||
|
||||
+87
-28
@@ -1,4 +1,4 @@
|
||||
//! MLua compatibility layer for Roblox Luau.
|
||||
//! MLua compatibility layer for Luau.
|
||||
//!
|
||||
//! Based on github.com/keplerproject/lua-compat-5.3
|
||||
|
||||
@@ -10,6 +10,8 @@ use super::lauxlib::*;
|
||||
use super::lua::*;
|
||||
use super::luacode::*;
|
||||
|
||||
pub const LUA_RESUMEERROR: c_int = -1;
|
||||
|
||||
unsafe fn compat53_reverse(L: *mut lua_State, mut a: c_int, mut b: c_int) {
|
||||
while a < b {
|
||||
lua_pushvalue(L, a);
|
||||
@@ -41,7 +43,7 @@ unsafe fn compat53_findfield(L: *mut lua_State, objidx: c_int, level: c_int) ->
|
||||
} else if compat53_findfield(L, objidx, level - 1) != 0 {
|
||||
// try recursively
|
||||
lua_remove(L, -2); // remove table (but keep name)
|
||||
lua_pushliteral(L, ".");
|
||||
lua_pushliteral(L, c".");
|
||||
lua_insert(L, -2); // place '.' between the two names
|
||||
lua_concat(L, 3);
|
||||
return 1;
|
||||
@@ -75,7 +77,7 @@ unsafe fn compat53_pushfuncname(L: *mut lua_State, level: c_int, ar: *mut lua_De
|
||||
lua_pushfstring(L, cstr!("function '%s'"), lua_tostring(L, -1));
|
||||
lua_remove(L, -2); // remove name
|
||||
} else {
|
||||
lua_pushliteral(L, "?");
|
||||
lua_pushliteral(L, c"?");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -108,7 +110,7 @@ pub unsafe fn lua_rotate(L: *mut lua_State, mut idx: c_int, mut n: c_int) {
|
||||
#[inline(always)]
|
||||
pub unsafe fn lua_copy(L: *mut lua_State, fromidx: c_int, toidx: c_int) {
|
||||
let abs_to = lua_absindex(L, toidx);
|
||||
luaL_checkstack(L, 1, cstr!("not enough stack slots"));
|
||||
luaL_checkstack(L, 1, cstr!("not enough stack slots available"));
|
||||
lua_pushvalue(L, fromidx);
|
||||
lua_replace(L, abs_to);
|
||||
}
|
||||
@@ -118,13 +120,19 @@ pub unsafe fn lua_isinteger(L: *mut lua_State, idx: c_int) -> c_int {
|
||||
if lua_type(L, idx) == LUA_TNUMBER {
|
||||
let n = lua_tonumber(L, idx);
|
||||
let i = lua_tointeger(L, idx);
|
||||
if (n - i as lua_Number).abs() < lua_Number::EPSILON {
|
||||
// Lua 5.3+ returns "false" for `-0.0`
|
||||
if n.to_bits() == (i as lua_Number).to_bits() {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
0
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn lua_pushinteger(L: *mut lua_State, i: lua_Integer) {
|
||||
lua_pushnumber(L, i as lua_Number);
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn lua_tointeger(L: *mut lua_State, i: c_int) -> lua_Integer {
|
||||
lua_tointegerx(L, i, ptr::null_mut())
|
||||
@@ -176,6 +184,7 @@ pub unsafe fn lua_geti(L: *mut lua_State, mut idx: c_int, n: lua_Integer) -> c_i
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn lua_rawgeti(L: *mut lua_State, idx: c_int, n: lua_Integer) -> c_int {
|
||||
let n = n.try_into().expect("cannot convert index from lua_Integer");
|
||||
lua_rawgeti_(L, idx, n)
|
||||
}
|
||||
|
||||
@@ -190,7 +199,7 @@ pub unsafe fn lua_rawgetp(L: *mut lua_State, idx: c_int, p: *const c_void) -> c_
|
||||
pub unsafe fn lua_getuservalue(L: *mut lua_State, mut idx: c_int) -> c_int {
|
||||
luaL_checkstack(L, 2, cstr!("not enough stack slots available"));
|
||||
idx = lua_absindex(L, idx);
|
||||
lua_pushliteral(L, "__mlua_uservalues");
|
||||
lua_pushliteral(L, c"__mlua_uservalues");
|
||||
if lua_rawget(L, LUA_REGISTRYINDEX) != LUA_TTABLE {
|
||||
return LUA_TNIL;
|
||||
}
|
||||
@@ -211,13 +220,14 @@ pub unsafe fn lua_seti(L: *mut lua_State, mut idx: c_int, n: lua_Integer) {
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn lua_rawseti(L: *mut lua_State, idx: c_int, n: lua_Integer) {
|
||||
let n = n.try_into().expect("cannot convert index from lua_Integer");
|
||||
lua_rawseti_(L, idx, n)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn lua_rawsetp(L: *mut lua_State, idx: c_int, p: *const c_void) {
|
||||
let abs_i = lua_absindex(L, idx);
|
||||
luaL_checkstack(L, 1, cstr!("not enough stack slots"));
|
||||
luaL_checkstack(L, 1, cstr!("not enough stack slots available"));
|
||||
lua_pushlightuserdata(L, p as *mut c_void);
|
||||
lua_insert(L, -2);
|
||||
lua_rawset(L, abs_i);
|
||||
@@ -227,13 +237,13 @@ pub unsafe fn lua_rawsetp(L: *mut lua_State, idx: c_int, p: *const c_void) {
|
||||
pub unsafe fn lua_setuservalue(L: *mut lua_State, mut idx: c_int) {
|
||||
luaL_checkstack(L, 4, cstr!("not enough stack slots available"));
|
||||
idx = lua_absindex(L, idx);
|
||||
lua_pushliteral(L, "__mlua_uservalues");
|
||||
lua_pushliteral(L, c"__mlua_uservalues");
|
||||
lua_pushvalue(L, -1);
|
||||
if lua_rawget(L, LUA_REGISTRYINDEX) != LUA_TTABLE {
|
||||
lua_pop(L, 1);
|
||||
lua_createtable(L, 0, 2); // main table
|
||||
lua_createtable(L, 0, 1); // metatable
|
||||
lua_pushliteral(L, "k");
|
||||
lua_pushliteral(L, c"k");
|
||||
lua_setfield(L, -2, cstr!("__mode"));
|
||||
lua_setmetatable(L, -2);
|
||||
lua_pushvalue(L, -2);
|
||||
@@ -284,6 +294,19 @@ pub unsafe fn lua_resume(L: *mut lua_State, from: *mut lua_State, narg: c_int, n
|
||||
ret
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn lua_resumex(L: *mut lua_State, from: *mut lua_State, narg: c_int, nres: *mut c_int) -> c_int {
|
||||
let ret = if narg == LUA_RESUMEERROR {
|
||||
lua_resumeerror(L, from)
|
||||
} else {
|
||||
lua_resume_(L, from, narg)
|
||||
};
|
||||
if (ret == LUA_OK || ret == LUA_YIELD) && !(nres.is_null()) {
|
||||
*nres = lua_gettop(L);
|
||||
}
|
||||
ret
|
||||
}
|
||||
|
||||
//
|
||||
// lauxlib ported functions
|
||||
//
|
||||
@@ -294,12 +317,30 @@ pub unsafe fn luaL_checkstack(L: *mut lua_State, sz: c_int, msg: *const c_char)
|
||||
if !msg.is_null() {
|
||||
luaL_error(L, cstr!("stack overflow (%s)"), msg);
|
||||
} else {
|
||||
lua_pushliteral(L, "stack overflow");
|
||||
lua_pushliteral(L, c"stack overflow");
|
||||
lua_error(L);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn luaL_checkinteger(L: *mut lua_State, narg: c_int) -> lua_Integer {
|
||||
let mut isnum = 0;
|
||||
let int = lua_tointegerx(L, narg, &mut isnum);
|
||||
if isnum == 0 {
|
||||
luaL_typeerror(L, narg, lua_typename(L, LUA_TNUMBER));
|
||||
}
|
||||
int
|
||||
}
|
||||
|
||||
pub unsafe fn luaL_optinteger(L: *mut lua_State, narg: c_int, def: lua_Integer) -> lua_Integer {
|
||||
if lua_isnoneornil(L, narg) != 0 {
|
||||
def
|
||||
} else {
|
||||
luaL_checkinteger(L, narg)
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn luaL_getmetafield(L: *mut lua_State, obj: c_int, e: *const c_char) -> c_int {
|
||||
if luaL_getmetafield_(L, obj, e) != 0 {
|
||||
@@ -326,12 +367,16 @@ pub unsafe fn luaL_loadbufferenv(
|
||||
mut size: usize,
|
||||
name: *const c_char,
|
||||
mode: *const c_char,
|
||||
env: c_int,
|
||||
mut env: c_int,
|
||||
) -> c_int {
|
||||
extern "C" {
|
||||
unsafe extern "C" {
|
||||
fn free(p: *mut c_void);
|
||||
}
|
||||
|
||||
unsafe extern "C" fn data_dtor(_: *mut lua_State, data: *mut c_void) {
|
||||
free(*(data as *mut *mut c_char) as *mut c_void);
|
||||
}
|
||||
|
||||
let chunk_is_text = size == 0 || (*data as u8) >= b'\t';
|
||||
if !mode.is_null() {
|
||||
let modeb = CStr::from_ptr(mode).to_bytes();
|
||||
@@ -344,16 +389,30 @@ pub unsafe fn luaL_loadbufferenv(
|
||||
}
|
||||
}
|
||||
|
||||
if chunk_is_text {
|
||||
let data = luau_compile_(data, size, ptr::null_mut(), &mut size);
|
||||
let ok = luau_load(L, name, data, size, env) == 0;
|
||||
free(data as *mut c_void);
|
||||
if !ok {
|
||||
return LUA_ERRSYNTAX;
|
||||
let status = if chunk_is_text {
|
||||
if env < 0 {
|
||||
env -= 1;
|
||||
}
|
||||
let data_ud = lua_newuserdatadtor(L, mem::size_of::<*mut c_char>(), data_dtor) as *mut *mut c_char;
|
||||
let data = luau_compile_(data, size, ptr::null_mut(), &mut size);
|
||||
ptr::write(data_ud, data);
|
||||
// By deferring the `free(data)` to the userdata destructor, we ensure that
|
||||
// even if `luau_load` throws an error, the `data` is still released.
|
||||
let status = luau_load(L, name, data, size, env);
|
||||
lua_replace(L, -2); // replace data with the result
|
||||
status
|
||||
} else {
|
||||
luau_load(L, name, data, size, env)
|
||||
};
|
||||
|
||||
if status != 0 {
|
||||
if lua_isstring(L, -1) != 0 && CStr::from_ptr(lua_tostring(L, -1)) == c"not enough memory" {
|
||||
// A case for Luau >= 0.679
|
||||
return LUA_ERRMEM;
|
||||
}
|
||||
} else if luau_load(L, name, data, size, env) != 0 {
|
||||
return LUA_ERRSYNTAX;
|
||||
}
|
||||
|
||||
LUA_OK
|
||||
}
|
||||
|
||||
@@ -381,7 +440,7 @@ pub unsafe fn luaL_loadbuffer(
|
||||
#[inline(always)]
|
||||
pub unsafe fn luaL_len(L: *mut lua_State, idx: c_int) -> lua_Integer {
|
||||
let mut isnum = 0;
|
||||
luaL_checkstack(L, 1, cstr!("not enough stack slots"));
|
||||
luaL_checkstack(L, 1, cstr!("not enough stack slots available"));
|
||||
lua_len(L, idx);
|
||||
let res = lua_tointegerx(L, -1, &mut isnum);
|
||||
lua_pop(L, 1);
|
||||
@@ -404,11 +463,11 @@ pub unsafe fn luaL_traceback(L: *mut lua_State, L1: *mut lua_State, msg: *const
|
||||
if !msg.is_null() {
|
||||
lua_pushfstring(L, cstr!("%s\n"), msg);
|
||||
}
|
||||
lua_pushliteral(L, "stack traceback:");
|
||||
lua_pushliteral(L, c"stack traceback:");
|
||||
while lua_getinfo(L1, level, cstr!(""), &mut ar) != 0 {
|
||||
if level + 1 == mark {
|
||||
// too many levels?
|
||||
lua_pushliteral(L, "\n\t..."); // add a '...'
|
||||
lua_pushliteral(L, c"\n\t..."); // add a '...'
|
||||
level = numlevels - COMPAT53_LEVELS2; // and skip to last ones
|
||||
} else {
|
||||
lua_getinfo(L1, level, cstr!("sln"), &mut ar);
|
||||
@@ -416,7 +475,7 @@ pub unsafe fn luaL_traceback(L: *mut lua_State, L1: *mut lua_State, msg: *const
|
||||
if ar.currentline > 0 {
|
||||
lua_pushfstring(L, cstr!("%d:"), ar.currentline);
|
||||
}
|
||||
lua_pushliteral(L, " in ");
|
||||
lua_pushliteral(L, c" in ");
|
||||
compat53_pushfuncname(L, level, &mut ar);
|
||||
lua_concat(L, lua_gettop(L) - top);
|
||||
}
|
||||
@@ -430,16 +489,16 @@ pub unsafe fn luaL_tolstring(L: *mut lua_State, mut idx: c_int, len: *mut usize)
|
||||
if luaL_callmeta(L, idx, cstr!("__tostring")) == 0 {
|
||||
match lua_type(L, idx) {
|
||||
LUA_TNIL => {
|
||||
lua_pushliteral(L, "nil");
|
||||
lua_pushliteral(L, c"nil");
|
||||
}
|
||||
LUA_TSTRING | LUA_TNUMBER => {
|
||||
lua_pushvalue(L, idx);
|
||||
}
|
||||
LUA_TBOOLEAN => {
|
||||
if lua_toboolean(L, idx) == 0 {
|
||||
lua_pushliteral(L, "false");
|
||||
lua_pushliteral(L, c"false");
|
||||
} else {
|
||||
lua_pushliteral(L, "true");
|
||||
lua_pushliteral(L, c"true");
|
||||
}
|
||||
}
|
||||
t => {
|
||||
@@ -463,14 +522,14 @@ pub unsafe fn luaL_tolstring(L: *mut lua_State, mut idx: c_int, len: *mut usize)
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn luaL_setmetatable(L: *mut lua_State, tname: *const c_char) {
|
||||
luaL_checkstack(L, 1, cstr!("not enough stack slots"));
|
||||
luaL_checkstack(L, 1, cstr!("not enough stack slots available"));
|
||||
luaL_getmetatable(L, tname);
|
||||
lua_setmetatable(L, -2);
|
||||
}
|
||||
|
||||
pub unsafe fn luaL_getsubtable(L: *mut lua_State, idx: c_int, fname: *const c_char) -> c_int {
|
||||
let abs_i = lua_absindex(L, idx);
|
||||
luaL_checkstack(L, 3, cstr!("not enough stack slots"));
|
||||
luaL_checkstack(L, 3, cstr!("not enough stack slots available"));
|
||||
lua_pushstring_(L, fname);
|
||||
if lua_gettable(L, abs_i) == LUA_TTABLE {
|
||||
return 1;
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
use std::os::raw::{c_char, c_float, c_int, c_void};
|
||||
use std::ptr;
|
||||
|
||||
use super::lua::{self, lua_CFunction, lua_Integer, lua_Number, lua_State, lua_Unsigned, LUA_REGISTRYINDEX};
|
||||
use super::lua::{self, lua_CFunction, lua_Number, lua_State, lua_Unsigned, LUA_REGISTRYINDEX};
|
||||
|
||||
#[repr(C)]
|
||||
pub struct luaL_Reg {
|
||||
@@ -11,7 +11,7 @@ pub struct luaL_Reg {
|
||||
pub func: lua_CFunction,
|
||||
}
|
||||
|
||||
extern "C-unwind" {
|
||||
unsafe extern "C-unwind" {
|
||||
pub fn luaL_register(L: *mut lua_State, libname: *const c_char, l: *const luaL_Reg);
|
||||
#[link_name = "luaL_getmetafield"]
|
||||
pub fn luaL_getmetafield_(L: *mut lua_State, obj: c_int, e: *const c_char) -> c_int;
|
||||
@@ -33,8 +33,10 @@ extern "C-unwind" {
|
||||
pub fn luaL_checkboolean(L: *mut lua_State, narg: c_int) -> c_int;
|
||||
pub fn luaL_optboolean(L: *mut lua_State, narg: c_int, def: c_int) -> c_int;
|
||||
|
||||
pub fn luaL_checkinteger(L: *mut lua_State, narg: c_int) -> lua_Integer;
|
||||
pub fn luaL_optinteger(L: *mut lua_State, narg: c_int, def: lua_Integer) -> lua_Integer;
|
||||
#[link_name = "luaL_checkinteger"]
|
||||
pub fn luaL_checkinteger_(L: *mut lua_State, narg: c_int) -> c_int;
|
||||
#[link_name = "luaL_optinteger"]
|
||||
pub fn luaL_optinteger_(L: *mut lua_State, narg: c_int, def: c_int) -> c_int;
|
||||
pub fn luaL_checkunsigned(L: *mut lua_State, narg: c_int) -> lua_Unsigned;
|
||||
pub fn luaL_optunsigned(L: *mut lua_State, narg: c_int, def: lua_Unsigned) -> lua_Unsigned;
|
||||
|
||||
@@ -69,10 +71,17 @@ extern "C-unwind" {
|
||||
|
||||
pub fn luaL_newstate() -> *mut lua_State;
|
||||
|
||||
// TODO: luaL_findtable
|
||||
pub fn luaL_findtable(
|
||||
L: *mut lua_State,
|
||||
idx: c_int,
|
||||
fname: *const c_char,
|
||||
szhint: c_int,
|
||||
) -> *const c_char;
|
||||
|
||||
pub fn luaL_typename(L: *mut lua_State, idx: c_int) -> *const c_char;
|
||||
|
||||
pub fn luaL_callyieldable(L: *mut lua_State, nargs: c_int, nresults: c_int) -> c_int;
|
||||
|
||||
// sandbox libraries and globals
|
||||
#[link_name = "luaL_sandbox"]
|
||||
pub fn luaL_sandbox_(L: *mut lua_State);
|
||||
@@ -141,7 +150,7 @@ pub unsafe fn luaL_sandbox(L: *mut lua_State, enabled: c_int) {
|
||||
}
|
||||
|
||||
// set all builtin metatables to read-only
|
||||
lua_pushliteral(L, "");
|
||||
lua_pushliteral(L, c"");
|
||||
if lua_getmetatable(L, -1) != 0 {
|
||||
lua_setreadonly(L, -1, enabled);
|
||||
lua_pop(L, 2);
|
||||
@@ -173,7 +182,7 @@ pub struct luaL_Strbuf {
|
||||
// For compatibility
|
||||
pub type luaL_Buffer = luaL_Strbuf;
|
||||
|
||||
extern "C-unwind" {
|
||||
unsafe extern "C-unwind" {
|
||||
pub fn luaL_buffinit(L: *mut lua_State, B: *mut luaL_Strbuf);
|
||||
pub fn luaL_buffinitsize(L: *mut lua_State, B: *mut luaL_Strbuf, size: usize) -> *mut c_char;
|
||||
pub fn luaL_prepbuffsize(B: *mut luaL_Strbuf, size: usize) -> *mut c_char;
|
||||
|
||||
+41
-32
@@ -1,5 +1,6 @@
|
||||
//! Contains definitions from `lua.h`.
|
||||
|
||||
use std::ffi::CStr;
|
||||
use std::marker::{PhantomData, PhantomPinned};
|
||||
use std::os::raw::{c_char, c_double, c_float, c_int, c_uint, c_void};
|
||||
use std::{mem, ptr};
|
||||
@@ -11,10 +12,10 @@ pub const LUA_MULTRET: c_int = -1;
|
||||
const LUAI_MAXCSTACK: c_int = 1000000;
|
||||
|
||||
// Number of valid Lua userdata tags
|
||||
const LUA_UTAG_LIMIT: c_int = 128;
|
||||
pub const LUA_UTAG_LIMIT: c_int = 128;
|
||||
|
||||
// Number of valid Lua lightuserdata tags
|
||||
const LUA_LUTAG_LIMIT: c_int = 128;
|
||||
pub const LUA_LUTAG_LIMIT: c_int = 128;
|
||||
|
||||
//
|
||||
// Pseudo-indices
|
||||
@@ -69,8 +70,11 @@ pub const LUA_MINSTACK: c_int = 20;
|
||||
/// A Lua number, usually equivalent to `f64`.
|
||||
pub type lua_Number = c_double;
|
||||
|
||||
/// A Lua integer, equivalent to `i32`.
|
||||
pub type lua_Integer = c_int;
|
||||
/// A Lua integer, usually equivalent to `i64`
|
||||
#[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`.
|
||||
pub type lua_Unsigned = c_uint;
|
||||
@@ -79,20 +83,19 @@ pub type lua_Unsigned = c_uint;
|
||||
pub type lua_CFunction = unsafe extern "C-unwind" fn(L: *mut lua_State) -> c_int;
|
||||
pub type lua_Continuation = unsafe extern "C-unwind" fn(L: *mut lua_State, status: c_int) -> c_int;
|
||||
|
||||
/// Type for userdata destructor functions.
|
||||
pub type lua_Udestructor = unsafe extern "C-unwind" fn(*mut c_void);
|
||||
pub type lua_Destructor = unsafe extern "C-unwind" fn(L: *mut lua_State, *mut c_void);
|
||||
/// Type for userdata destructor functions (no unwinding).
|
||||
pub type lua_Destructor = unsafe extern "C" fn(L: *mut lua_State, *mut c_void);
|
||||
|
||||
/// Type for memory-allocation functions.
|
||||
/// Type for memory-allocation functions (no unwinding).
|
||||
pub type lua_Alloc =
|
||||
unsafe extern "C-unwind" fn(ud: *mut c_void, ptr: *mut c_void, osize: usize, nsize: usize) -> *mut c_void;
|
||||
unsafe extern "C" fn(ud: *mut c_void, ptr: *mut c_void, osize: usize, nsize: usize) -> *mut c_void;
|
||||
|
||||
/// Returns Luau release version (eg. `0.xxx`).
|
||||
pub const fn luau_version() -> Option<&'static str> {
|
||||
option_env!("LUAU_VERSION")
|
||||
}
|
||||
|
||||
extern "C-unwind" {
|
||||
unsafe extern "C-unwind" {
|
||||
//
|
||||
// State manipulation
|
||||
//
|
||||
@@ -136,7 +139,7 @@ extern "C-unwind" {
|
||||
|
||||
pub fn lua_tonumberx(L: *mut lua_State, idx: c_int, isnum: *mut c_int) -> lua_Number;
|
||||
#[link_name = "lua_tointegerx"]
|
||||
pub fn lua_tointegerx_(L: *mut lua_State, idx: c_int, isnum: *mut c_int) -> lua_Integer;
|
||||
pub fn lua_tointegerx_(L: *mut lua_State, idx: c_int, isnum: *mut c_int) -> c_int;
|
||||
pub fn lua_tounsignedx(L: *mut lua_State, idx: c_int, isnum: *mut c_int) -> lua_Unsigned;
|
||||
pub fn lua_tovector(L: *mut lua_State, idx: c_int) -> *const c_float;
|
||||
pub fn lua_toboolean(L: *mut lua_State, idx: c_int) -> c_int;
|
||||
@@ -160,7 +163,8 @@ extern "C-unwind" {
|
||||
//
|
||||
pub fn lua_pushnil(L: *mut lua_State);
|
||||
pub fn lua_pushnumber(L: *mut lua_State, n: lua_Number);
|
||||
pub fn lua_pushinteger(L: *mut lua_State, n: lua_Integer);
|
||||
#[link_name = "lua_pushinteger"]
|
||||
pub fn lua_pushinteger_(L: *mut lua_State, n: c_int);
|
||||
pub fn lua_pushunsigned(L: *mut lua_State, n: lua_Unsigned);
|
||||
#[cfg(not(feature = "luau-vector4"))]
|
||||
pub fn lua_pushvector(L: *mut lua_State, x: c_float, y: c_float, z: c_float);
|
||||
@@ -185,7 +189,8 @@ extern "C-unwind" {
|
||||
|
||||
pub fn lua_pushlightuserdatatagged(L: *mut lua_State, p: *mut c_void, tag: c_int);
|
||||
pub fn lua_newuserdatatagged(L: *mut lua_State, sz: usize, tag: c_int) -> *mut c_void;
|
||||
pub fn lua_newuserdatadtor(L: *mut lua_State, sz: usize, dtor: lua_Udestructor) -> *mut c_void;
|
||||
pub fn lua_newuserdatataggedwithmetatable(L: *mut lua_State, sz: usize, tag: c_int) -> *mut c_void;
|
||||
pub fn lua_newuserdatadtor(L: *mut lua_State, sz: usize, dtor: lua_Destructor) -> *mut c_void;
|
||||
|
||||
pub fn lua_newbuffer(L: *mut lua_State, sz: usize) -> *mut c_void;
|
||||
|
||||
@@ -259,14 +264,14 @@ pub const LUA_GCSETGOAL: c_int = 7;
|
||||
pub const LUA_GCSETSTEPMUL: c_int = 8;
|
||||
pub const LUA_GCSETSTEPSIZE: c_int = 9;
|
||||
|
||||
extern "C-unwind" {
|
||||
unsafe extern "C-unwind" {
|
||||
pub fn lua_gc(L: *mut lua_State, what: c_int, data: c_int) -> c_int;
|
||||
}
|
||||
|
||||
//
|
||||
// Memory statistics
|
||||
//
|
||||
extern "C-unwind" {
|
||||
unsafe extern "C-unwind" {
|
||||
pub fn lua_setmemcat(L: *mut lua_State, category: c_int);
|
||||
pub fn lua_totalbytes(L: *mut lua_State, category: c_int) -> usize;
|
||||
}
|
||||
@@ -274,7 +279,7 @@ extern "C-unwind" {
|
||||
//
|
||||
// Miscellaneous functions
|
||||
//
|
||||
extern "C-unwind" {
|
||||
unsafe extern "C-unwind" {
|
||||
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;
|
||||
@@ -284,7 +289,7 @@ extern "C-unwind" {
|
||||
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<lua_Destructor>);
|
||||
pub fn lua_getuserdatadtor(L: *mut lua_State, tag: c_int) -> Option<lua_Destructor>;
|
||||
pub fn lua_setuserdatametatable(L: *mut lua_State, tag: c_int, idx: c_int);
|
||||
pub fn lua_setuserdatametatable(L: *mut lua_State, tag: c_int);
|
||||
pub fn lua_getuserdatametatable(L: *mut lua_State, tag: c_int);
|
||||
pub fn lua_setlightuserdataname(L: *mut lua_State, tag: c_int, name: *const c_char);
|
||||
pub fn lua_getlightuserdataname(L: *mut lua_State, tag: c_int) -> *const c_char;
|
||||
@@ -299,7 +304,7 @@ extern "C-unwind" {
|
||||
pub const LUA_NOREF: c_int = -1;
|
||||
pub const LUA_REFNIL: c_int = 0;
|
||||
|
||||
extern "C-unwind" {
|
||||
unsafe extern "C-unwind" {
|
||||
pub fn lua_ref(L: *mut lua_State, idx: c_int) -> c_int;
|
||||
pub fn lua_unref(L: *mut lua_State, r#ref: c_int);
|
||||
}
|
||||
@@ -309,13 +314,13 @@ extern "C-unwind" {
|
||||
//
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn lua_tonumber(L: *mut lua_State, i: c_int) -> lua_Number {
|
||||
lua_tonumberx(L, i, ptr::null_mut())
|
||||
pub unsafe fn lua_tonumber(L: *mut lua_State, idx: c_int) -> lua_Number {
|
||||
lua_tonumberx(L, idx, ptr::null_mut())
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn lua_tointeger_(L: *mut lua_State, i: c_int) -> lua_Integer {
|
||||
lua_tointegerx_(L, i, ptr::null_mut())
|
||||
pub unsafe fn lua_tointeger_(L: *mut lua_State, idx: c_int) -> c_int {
|
||||
lua_tointegerx_(L, idx, ptr::null_mut())
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
@@ -339,12 +344,14 @@ pub unsafe fn lua_newuserdata(L: *mut lua_State, sz: usize) -> *mut c_void {
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn lua_newuserdata_t<T>(L: *mut lua_State) -> *mut T {
|
||||
unsafe extern "C-unwind" fn destructor<T>(ud: *mut c_void) {
|
||||
pub unsafe fn lua_newuserdata_t<T>(L: *mut lua_State, data: T) -> *mut T {
|
||||
unsafe extern "C" fn destructor<T>(_: *mut lua_State, ud: *mut c_void) {
|
||||
ptr::drop_in_place(ud as *mut T);
|
||||
}
|
||||
|
||||
lua_newuserdatadtor(L, mem::size_of::<T>(), destructor::<T>) as *mut T
|
||||
let ud_ptr = lua_newuserdatadtor(L, const { mem::size_of::<T>() }, destructor::<T>) as *mut T;
|
||||
ptr::write(ud_ptr, data);
|
||||
ud_ptr
|
||||
}
|
||||
|
||||
// TODO: lua_strlen
|
||||
@@ -400,10 +407,8 @@ pub unsafe fn lua_isnoneornil(L: *mut lua_State, n: c_int) -> c_int {
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn lua_pushliteral(L: *mut lua_State, s: &'static str) {
|
||||
use std::ffi::CString;
|
||||
let c_str = CString::new(s).unwrap();
|
||||
lua_pushlstring_(L, c_str.as_ptr(), c_str.as_bytes().len())
|
||||
pub unsafe fn lua_pushliteral(L: *mut lua_State, s: &'static CStr) {
|
||||
lua_pushstring_(L, s.as_ptr());
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
@@ -465,7 +470,7 @@ pub type lua_Coverage = unsafe extern "C-unwind" fn(
|
||||
size: usize,
|
||||
);
|
||||
|
||||
extern "C-unwind" {
|
||||
unsafe extern "C-unwind" {
|
||||
pub fn lua_stackdepth(L: *mut lua_State) -> c_int;
|
||||
pub fn lua_getinfo(L: *mut lua_State, level: c_int, what: *const c_char, ar: *mut lua_Debug) -> c_int;
|
||||
pub fn lua_getargument(L: *mut lua_State, level: c_int, n: c_int) -> c_int;
|
||||
@@ -526,13 +531,17 @@ pub struct lua_Callbacks {
|
||||
pub debuginterrupt: Option<unsafe extern "C-unwind" fn(L: *mut lua_State, ar: *mut lua_Debug)>,
|
||||
/// gets called when protected call results in an error
|
||||
pub debugprotectederror: Option<unsafe extern "C-unwind" fn(L: *mut lua_State)>,
|
||||
|
||||
/// gets called when memory is allocated
|
||||
pub onallocate: Option<unsafe extern "C-unwind" fn(L: *mut lua_State, osize: usize, nsize: usize)>,
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
unsafe extern "C" {
|
||||
pub fn lua_callbacks(L: *mut lua_State) -> *mut lua_Callbacks;
|
||||
}
|
||||
|
||||
// Functions from customization lib
|
||||
extern "C" {
|
||||
unsafe extern "C" {
|
||||
pub fn luau_setfflag(name: *const c_char, value: c_int) -> c_int;
|
||||
pub fn lua_getmetatablepointer(L: *mut lua_State, idx: c_int) -> *const c_void;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
//! Contains definitions from `luacode.h`.
|
||||
|
||||
use std::marker::{PhantomData, PhantomPinned};
|
||||
use std::os::raw::{c_char, c_int, c_void};
|
||||
use std::{ptr, slice};
|
||||
|
||||
@@ -15,6 +16,10 @@ pub struct lua_CompileOptions {
|
||||
pub vectorType: *const c_char,
|
||||
pub mutableGlobals: *const *const c_char,
|
||||
pub userdataTypes: *const *const c_char,
|
||||
pub librariesWithKnownMembers: *const *const c_char,
|
||||
pub libraryMemberTypeCallback: Option<lua_LibraryMemberTypeCallback>,
|
||||
pub libraryMemberConstantCallback: Option<lua_LibraryMemberConstantCallback>,
|
||||
pub disabledBuiltins: *const *const c_char,
|
||||
}
|
||||
|
||||
impl Default for lua_CompileOptions {
|
||||
@@ -29,11 +34,57 @@ impl Default for lua_CompileOptions {
|
||||
vectorType: ptr::null(),
|
||||
mutableGlobals: ptr::null(),
|
||||
userdataTypes: ptr::null(),
|
||||
librariesWithKnownMembers: ptr::null(),
|
||||
libraryMemberTypeCallback: None,
|
||||
libraryMemberConstantCallback: None,
|
||||
disabledBuiltins: ptr::null(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extern "C-unwind" {
|
||||
#[repr(C)]
|
||||
pub struct lua_CompileConstant {
|
||||
_data: [u8; 0],
|
||||
_marker: PhantomData<(*mut u8, PhantomPinned)>,
|
||||
}
|
||||
|
||||
/// Type table tags
|
||||
#[doc(hidden)]
|
||||
#[repr(i32)]
|
||||
#[non_exhaustive]
|
||||
pub enum luau_BytecodeType {
|
||||
Nil = 0,
|
||||
Boolean,
|
||||
Number,
|
||||
String,
|
||||
Table,
|
||||
Function,
|
||||
Thread,
|
||||
UserData,
|
||||
Vector,
|
||||
Buffer,
|
||||
|
||||
Any = 15,
|
||||
}
|
||||
|
||||
pub type lua_LibraryMemberTypeCallback =
|
||||
unsafe extern "C-unwind" fn(library: *const c_char, member: *const c_char) -> c_int;
|
||||
|
||||
pub type lua_LibraryMemberConstantCallback = unsafe extern "C-unwind" fn(
|
||||
library: *const c_char,
|
||||
member: *const c_char,
|
||||
constant: *mut lua_CompileConstant,
|
||||
);
|
||||
|
||||
unsafe extern "C" {
|
||||
pub fn luau_set_compile_constant_nil(cons: *mut lua_CompileConstant);
|
||||
pub fn luau_set_compile_constant_boolean(cons: *mut lua_CompileConstant, b: c_int);
|
||||
pub fn luau_set_compile_constant_number(cons: *mut lua_CompileConstant, n: f64);
|
||||
pub fn luau_set_compile_constant_vector(cons: *mut lua_CompileConstant, x: f32, y: f32, z: f32, w: f32);
|
||||
pub fn luau_set_compile_constant_string(cons: *mut lua_CompileConstant, s: *const c_char, l: usize);
|
||||
}
|
||||
|
||||
unsafe extern "C-unwind" {
|
||||
#[link_name = "luau_compile"]
|
||||
pub fn luau_compile_(
|
||||
source: *const c_char,
|
||||
@@ -43,7 +94,7 @@ extern "C-unwind" {
|
||||
) -> *mut c_char;
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
unsafe extern "C" {
|
||||
fn free(p: *mut c_void);
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ use std::os::raw::c_int;
|
||||
|
||||
use super::lua::lua_State;
|
||||
|
||||
extern "C-unwind" {
|
||||
unsafe extern "C-unwind" {
|
||||
pub fn luau_codegen_supported() -> c_int;
|
||||
pub fn luau_codegen_create(state: *mut lua_State);
|
||||
pub fn luau_codegen_compile(state: *mut lua_State, idx: c_int);
|
||||
|
||||
+13
-11
@@ -1,20 +1,21 @@
|
||||
//! Contains definitions from `lualib.h`.
|
||||
|
||||
use std::os::raw::c_int;
|
||||
use std::os::raw::{c_char, c_int};
|
||||
|
||||
use super::lua::lua_State;
|
||||
|
||||
pub const LUA_COLIBNAME: &str = "coroutine";
|
||||
pub const LUA_TABLIBNAME: &str = "table";
|
||||
pub const LUA_OSLIBNAME: &str = "os";
|
||||
pub const LUA_STRLIBNAME: &str = "string";
|
||||
pub const LUA_BITLIBNAME: &str = "bit32";
|
||||
pub const LUA_BUFFERLIBNAME: &str = "buffer";
|
||||
pub const LUA_UTF8LIBNAME: &str = "utf8";
|
||||
pub const LUA_MATHLIBNAME: &str = "math";
|
||||
pub const LUA_DBLIBNAME: &str = "debug";
|
||||
pub const LUA_COLIBNAME: *const c_char = cstr!("coroutine");
|
||||
pub const LUA_TABLIBNAME: *const c_char = cstr!("table");
|
||||
pub const LUA_OSLIBNAME: *const c_char = cstr!("os");
|
||||
pub const LUA_STRLIBNAME: *const c_char = cstr!("string");
|
||||
pub const LUA_BITLIBNAME: *const c_char = cstr!("bit32");
|
||||
pub const LUA_BUFFERLIBNAME: *const c_char = cstr!("buffer");
|
||||
pub const LUA_UTF8LIBNAME: *const c_char = cstr!("utf8");
|
||||
pub const LUA_MATHLIBNAME: *const c_char = cstr!("math");
|
||||
pub const LUA_DBLIBNAME: *const c_char = cstr!("debug");
|
||||
pub const LUA_VECLIBNAME: *const c_char = cstr!("vector");
|
||||
|
||||
extern "C-unwind" {
|
||||
unsafe extern "C-unwind" {
|
||||
pub fn luaopen_base(L: *mut lua_State) -> c_int;
|
||||
pub fn luaopen_coroutine(L: *mut lua_State) -> c_int;
|
||||
pub fn luaopen_table(L: *mut lua_State) -> c_int;
|
||||
@@ -25,6 +26,7 @@ extern "C-unwind" {
|
||||
pub fn luaopen_utf8(L: *mut lua_State) -> c_int;
|
||||
pub fn luaopen_math(L: *mut lua_State) -> c_int;
|
||||
pub fn luaopen_debug(L: *mut lua_State) -> c_int;
|
||||
pub fn luaopen_vector(L: *mut lua_State) -> c_int;
|
||||
|
||||
// open all builtin libraries
|
||||
pub fn luaL_openlibs(L: *mut lua_State);
|
||||
|
||||
@@ -0,0 +1,177 @@
|
||||
//! Contains definitions from `Require.h`.
|
||||
|
||||
use std::os::raw::{c_char, c_int, c_void};
|
||||
|
||||
use super::lua::lua_State;
|
||||
|
||||
pub const LUA_REGISTERED_MODULES_TABLE: *const c_char = cstr!("_REGISTEREDMODULES");
|
||||
|
||||
#[repr(C)]
|
||||
pub enum luarequire_NavigateResult {
|
||||
Success,
|
||||
Ambiguous,
|
||||
NotFound,
|
||||
}
|
||||
|
||||
// Functions returning WriteSuccess are expected to set their size_out argument
|
||||
// to the number of bytes written to the buffer. If WriteBufferTooSmall is
|
||||
// returned, size_out should be set to the required buffer size.
|
||||
#[repr(C)]
|
||||
pub enum luarequire_WriteResult {
|
||||
Success,
|
||||
BufferTooSmall,
|
||||
Failure,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct luarequire_Configuration {
|
||||
// Returns whether requires are permitted from the given chunkname.
|
||||
pub is_require_allowed: unsafe extern "C-unwind" fn(
|
||||
L: *mut lua_State,
|
||||
ctx: *mut c_void,
|
||||
requirer_chunkname: *const c_char,
|
||||
) -> bool,
|
||||
|
||||
// Resets the internal state to point at the requirer module.
|
||||
pub reset: unsafe extern "C-unwind" fn(
|
||||
L: *mut lua_State,
|
||||
ctx: *mut c_void,
|
||||
requirer_chunkname: *const c_char,
|
||||
) -> luarequire_NavigateResult,
|
||||
|
||||
// Resets the internal state to point at an aliased module, given its exact path from a configuration
|
||||
// file. This function is only called when an alias's path cannot be resolved relative to its
|
||||
// configuration file.
|
||||
pub jump_to_alias: unsafe extern "C-unwind" fn(
|
||||
L: *mut lua_State,
|
||||
ctx: *mut c_void,
|
||||
path: *const c_char,
|
||||
) -> luarequire_NavigateResult,
|
||||
|
||||
// Navigates through the context by making mutations to the internal state.
|
||||
pub to_parent:
|
||||
unsafe extern "C-unwind" fn(L: *mut lua_State, ctx: *mut c_void) -> luarequire_NavigateResult,
|
||||
pub to_child: unsafe extern "C-unwind" fn(
|
||||
L: *mut lua_State,
|
||||
ctx: *mut c_void,
|
||||
name: *const c_char,
|
||||
) -> luarequire_NavigateResult,
|
||||
|
||||
// Returns whether the context is currently pointing at a module.
|
||||
pub is_module_present: unsafe extern "C-unwind" fn(L: *mut lua_State, ctx: *mut c_void) -> bool,
|
||||
|
||||
// Provides a chunkname for the current module. This will be accessible through the debug library. This
|
||||
// function is only called if is_module_present returns true.
|
||||
pub get_chunkname: unsafe extern "C-unwind" fn(
|
||||
L: *mut lua_State,
|
||||
ctx: *mut c_void,
|
||||
buffer: *mut c_char,
|
||||
buffer_size: usize,
|
||||
size_out: *mut usize,
|
||||
) -> luarequire_WriteResult,
|
||||
|
||||
// Provides a loadname that identifies the current module and is passed to load. This function
|
||||
// is only called if is_module_present returns true.
|
||||
pub get_loadname: unsafe extern "C-unwind" fn(
|
||||
L: *mut lua_State,
|
||||
ctx: *mut c_void,
|
||||
buffer: *mut c_char,
|
||||
buffer_size: usize,
|
||||
size_out: *mut usize,
|
||||
) -> luarequire_WriteResult,
|
||||
|
||||
// Provides a cache key representing the current module. This function is only called if
|
||||
// is_module_present returns true.
|
||||
pub get_cache_key: unsafe extern "C-unwind" fn(
|
||||
L: *mut lua_State,
|
||||
ctx: *mut c_void,
|
||||
buffer: *mut c_char,
|
||||
buffer_size: usize,
|
||||
size_out: *mut usize,
|
||||
) -> luarequire_WriteResult,
|
||||
|
||||
// Returns whether a configuration file is present in the current context.
|
||||
// If not, require-by-string will call to_parent until either a configuration file is present or
|
||||
// NAVIGATE_FAILURE is returned (at root).
|
||||
pub is_config_present: unsafe extern "C-unwind" fn(L: *mut lua_State, ctx: *mut c_void) -> bool,
|
||||
|
||||
// Parses the configuration file in the current context for the given alias and returns its
|
||||
// value or WRITE_FAILURE if not found. This function is only called if is_config_present
|
||||
// returns true. If this function pointer is set, get_config must not be set. Opting in to this
|
||||
// function pointer disables parsing configuration files internally and can be used for finer
|
||||
// control over the configuration file parsing process.
|
||||
pub get_alias: Option<
|
||||
unsafe extern "C-unwind" fn(
|
||||
L: *mut lua_State,
|
||||
ctx: *mut c_void,
|
||||
alias: *const c_char,
|
||||
buffer: *mut c_char,
|
||||
buffer_size: usize,
|
||||
size_out: *mut usize,
|
||||
) -> luarequire_WriteResult,
|
||||
>,
|
||||
|
||||
// Provides the contents of the configuration file in the current context. This function is only called
|
||||
// if is_config_present returns true. If this function pointer is set, get_alias must not be set. Opting
|
||||
// in to this function pointer enables parsing configuration files internally.
|
||||
pub get_config: Option<
|
||||
unsafe extern "C-unwind" fn(
|
||||
L: *mut lua_State,
|
||||
ctx: *mut c_void,
|
||||
buffer: *mut c_char,
|
||||
buffer_size: usize,
|
||||
size_out: *mut usize,
|
||||
) -> luarequire_WriteResult,
|
||||
>,
|
||||
|
||||
// Executes the module and places the result on the stack. Returns the number of results placed on the
|
||||
// stack.
|
||||
// Returning -1 directs the requiring thread to yield. In this case, this thread should be resumed with
|
||||
// the module result pushed onto its stack.
|
||||
pub load: unsafe extern "C-unwind" fn(
|
||||
L: *mut lua_State,
|
||||
ctx: *mut c_void,
|
||||
path: *const c_char,
|
||||
chunkname: *const c_char,
|
||||
loadname: *const c_char,
|
||||
) -> c_int,
|
||||
}
|
||||
|
||||
// Populates function pointers in the given luarequire_Configuration.
|
||||
pub type luarequire_Configuration_init = unsafe extern "C-unwind" fn(config: *mut luarequire_Configuration);
|
||||
|
||||
unsafe extern "C-unwind" {
|
||||
// Initializes and pushes the require closure onto the stack without registration.
|
||||
pub fn luarequire_pushrequire(
|
||||
L: *mut lua_State,
|
||||
config_init: luarequire_Configuration_init,
|
||||
ctx: *mut c_void,
|
||||
) -> c_int;
|
||||
|
||||
// Initializes the require library and registers it globally.
|
||||
pub fn luaopen_require(L: *mut lua_State, config_init: luarequire_Configuration_init, ctx: *mut c_void);
|
||||
|
||||
// Initializes and pushes a "proxyrequire" closure onto the stack.
|
||||
//
|
||||
// The closure takes two parameters: the string path to resolve and the chunkname of an existing
|
||||
// module.
|
||||
pub fn luarequire_pushproxyrequire(
|
||||
L: *mut lua_State,
|
||||
config_init: luarequire_Configuration_init,
|
||||
ctx: *mut c_void,
|
||||
) -> c_int;
|
||||
|
||||
// Registers an aliased require path to a result.
|
||||
//
|
||||
// After registration, the given result will always be immediately returned when the given path is
|
||||
// required.
|
||||
// Expects the path and table to be passed as arguments on the stack.
|
||||
pub fn luarequire_registermodule(L: *mut lua_State) -> c_int;
|
||||
|
||||
// Clears the entry associated with the given cache key from the require cache.
|
||||
// Expects the cache key to be passed as an argument on the stack.
|
||||
pub fn luarequire_clearcacheentry(L: *mut lua_State) -> c_int;
|
||||
|
||||
// Clears all entries from the require cache.
|
||||
pub fn luarequire_clearcache(L: *mut lua_State) -> c_int;
|
||||
}
|
||||
@@ -6,6 +6,7 @@ pub use lua::*;
|
||||
pub use luacode::*;
|
||||
pub use luacodegen::*;
|
||||
pub use lualib::*;
|
||||
pub use luarequire::*;
|
||||
|
||||
pub mod compat;
|
||||
pub mod lauxlib;
|
||||
@@ -13,3 +14,4 @@ pub mod lua;
|
||||
pub mod luacode;
|
||||
pub mod luacodegen;
|
||||
pub mod lualib;
|
||||
pub mod luarequire;
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
[package]
|
||||
name = "mlua_derive"
|
||||
version = "0.10.0-beta.1"
|
||||
version = "0.11.0"
|
||||
authors = ["Aleksandr Orlenko <zxteam@pm.me>"]
|
||||
edition = "2021"
|
||||
description = "Procedural macros for the mlua crate."
|
||||
repository = "https://github.com/khvzak/mlua"
|
||||
repository = "https://github.com/mlua-rs/mlua"
|
||||
keywords = ["lua", "mlua"]
|
||||
license = "MIT"
|
||||
|
||||
@@ -12,13 +12,13 @@ license = "MIT"
|
||||
proc-macro = true
|
||||
|
||||
[features]
|
||||
macros = ["proc-macro-error", "itertools", "regex", "once_cell"]
|
||||
macros = ["proc-macro-error2", "itertools", "regex", "once_cell"]
|
||||
|
||||
[dependencies]
|
||||
quote = "1.0"
|
||||
proc-macro2 = { version = "1.0", features = ["span-locations"] }
|
||||
proc-macro-error = { version = "1.0", optional = true }
|
||||
proc-macro-error2 = { version = "2.0.1", optional = true }
|
||||
syn = { version = "2.0", features = ["full"] }
|
||||
itertools = { version = "0.13", optional = true }
|
||||
itertools = { version = "0.14", optional = true }
|
||||
regex = { version = "1.4", optional = true }
|
||||
once_cell = { version = "1.0", optional = true }
|
||||
|
||||
@@ -20,7 +20,7 @@ pub fn from_lua(input: TokenStream) -> TokenStream {
|
||||
::mlua::Value::UserData(ud) => Ok(ud.borrow::<Self>()?.clone()),
|
||||
_ => Err(::mlua::Error::FromLuaConversionError {
|
||||
from: value.type_name(),
|
||||
to: #ident_str,
|
||||
to: #ident_str.to_string(),
|
||||
message: None,
|
||||
}),
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ use syn::{parse_macro_input, ItemFn, LitStr, Result};
|
||||
#[cfg(feature = "macros")]
|
||||
use {
|
||||
crate::chunk::Chunk, proc_macro::TokenTree, proc_macro2::TokenStream as TokenStream2,
|
||||
proc_macro_error::proc_macro_error,
|
||||
proc_macro_error2::proc_macro_error,
|
||||
};
|
||||
|
||||
#[derive(Default)]
|
||||
@@ -103,7 +103,7 @@ pub fn chunk(input: TokenStream) -> TokenStream {
|
||||
|
||||
struct InnerChunk<F: FnOnce(&Lua) -> Result<Table>>(Cell<Option<F>>);
|
||||
|
||||
impl<F> AsChunk<'static> for InnerChunk<F>
|
||||
impl<F> AsChunk for InnerChunk<F>
|
||||
where
|
||||
F: FnOnce(&Lua) -> Result<Table>,
|
||||
{
|
||||
@@ -120,7 +120,7 @@ pub fn chunk(input: TokenStream) -> TokenStream {
|
||||
Some(ChunkMode::Text)
|
||||
}
|
||||
|
||||
fn source(self) -> IoResult<Cow<'static, [u8]>> {
|
||||
fn source<'a>(&self) -> IoResult<Cow<'a, [u8]>> {
|
||||
Ok(Cow::Borrowed((#source).as_bytes()))
|
||||
}
|
||||
}
|
||||
@@ -129,13 +129,13 @@ pub fn chunk(input: TokenStream) -> TokenStream {
|
||||
let globals = lua.globals();
|
||||
let env = lua.create_table()?;
|
||||
let meta = lua.create_table()?;
|
||||
meta.raw_set("__index", globals.clone())?;
|
||||
meta.raw_set("__newindex", globals)?;
|
||||
meta.raw_set("__index", &globals)?;
|
||||
meta.raw_set("__newindex", &globals)?;
|
||||
|
||||
// Add captured variables
|
||||
#(#caps)*
|
||||
|
||||
env.set_metatable(Some(meta));
|
||||
env.set_metatable(Some(meta))?;
|
||||
Ok(env)
|
||||
};
|
||||
|
||||
|
||||
@@ -74,7 +74,7 @@ fn parse_pos(span: &Span) -> Option<(usize, usize)> {
|
||||
fn fallback_span_pos(span: &Span) -> (Pos, Pos) {
|
||||
let (start, end) = match parse_pos(span) {
|
||||
Some(v) => v,
|
||||
None => proc_macro_error::abort_call_site!("Cannot retrieve span information; please use nightly"),
|
||||
None => proc_macro_error2::abort_call_site!("Cannot retrieve span information; please use nightly"),
|
||||
};
|
||||
(Pos::new(1, start), Pos::new(1, end))
|
||||
}
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
#[cfg(feature = "serde")]
|
||||
use serde::ser::{Serialize, Serializer};
|
||||
|
||||
use crate::state::RawLua;
|
||||
use crate::types::ValueRef;
|
||||
|
||||
/// A Luau buffer type.
|
||||
///
|
||||
/// See the buffer [documentation] for more information.
|
||||
///
|
||||
/// [documentation]: https://luau.org/library#buffer-library
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct Buffer(pub(crate) ValueRef);
|
||||
|
||||
#[cfg_attr(not(feature = "luau"), allow(unused))]
|
||||
impl Buffer {
|
||||
/// Copies the buffer data into a new `Vec<u8>`.
|
||||
pub fn to_vec(&self) -> Vec<u8> {
|
||||
let lua = self.0.lua.lock();
|
||||
self.as_slice(&lua).to_vec()
|
||||
}
|
||||
|
||||
/// Returns the length of the buffer.
|
||||
pub fn len(&self) -> usize {
|
||||
let lua = self.0.lua.lock();
|
||||
self.as_slice(&lua).len()
|
||||
}
|
||||
|
||||
/// Returns `true` if the buffer is empty.
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.len() == 0
|
||||
}
|
||||
|
||||
/// Reads given number of bytes from the buffer at the given offset.
|
||||
///
|
||||
/// Offset is 0-based.
|
||||
#[track_caller]
|
||||
pub fn read_bytes<const N: usize>(&self, offset: usize) -> [u8; N] {
|
||||
let lua = self.0.lua.lock();
|
||||
let data = self.as_slice(&lua);
|
||||
let mut bytes = [0u8; N];
|
||||
bytes.copy_from_slice(&data[offset..offset + N]);
|
||||
bytes
|
||||
}
|
||||
|
||||
/// Writes given bytes to the buffer at the given offset.
|
||||
///
|
||||
/// Offset is 0-based.
|
||||
#[track_caller]
|
||||
pub fn write_bytes(&self, offset: usize, bytes: &[u8]) {
|
||||
let lua = self.0.lua.lock();
|
||||
let data = unsafe {
|
||||
let (buf, size) = self.as_raw_parts(&lua);
|
||||
std::slice::from_raw_parts_mut(buf, size)
|
||||
};
|
||||
data[offset..offset + bytes.len()].copy_from_slice(bytes);
|
||||
}
|
||||
|
||||
pub(crate) fn as_slice(&self, lua: &RawLua) -> &[u8] {
|
||||
unsafe {
|
||||
let (buf, size) = self.as_raw_parts(lua);
|
||||
std::slice::from_raw_parts(buf, size)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "luau")]
|
||||
unsafe fn as_raw_parts(&self, lua: &RawLua) -> (*mut u8, usize) {
|
||||
let mut size = 0usize;
|
||||
let buf = ffi::lua_tobuffer(lua.ref_thread(), self.0.index, &mut size);
|
||||
mlua_assert!(!buf.is_null(), "invalid Luau buffer");
|
||||
(buf as *mut u8, size)
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "luau"))]
|
||||
unsafe fn as_raw_parts(&self, lua: &RawLua) -> (*mut u8, usize) {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
impl Serialize for Buffer {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error> {
|
||||
let lua = self.0.lua.lock();
|
||||
serializer.serialize_bytes(self.as_slice(&lua))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "luau")]
|
||||
impl crate::types::LuaType for Buffer {
|
||||
const TYPE_ID: std::os::raw::c_int = ffi::LUA_TBUFFER;
|
||||
}
|
||||
+324
-88
@@ -2,6 +2,7 @@ use std::borrow::Cow;
|
||||
use std::collections::HashMap;
|
||||
use std::ffi::CString;
|
||||
use std::io::Result as IoResult;
|
||||
use std::panic::Location;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::string::String as StdString;
|
||||
|
||||
@@ -9,14 +10,16 @@ use crate::error::{Error, Result};
|
||||
use crate::function::Function;
|
||||
use crate::state::{Lua, WeakLua};
|
||||
use crate::table::Table;
|
||||
use crate::value::{FromLuaMulti, IntoLuaMulti};
|
||||
use crate::traits::{FromLuaMulti, IntoLua, IntoLuaMulti};
|
||||
use crate::value::Value;
|
||||
|
||||
/// Trait for types [loadable by Lua] and convertible to a [`Chunk`]
|
||||
///
|
||||
/// [loadable by Lua]: https://www.lua.org/manual/5.4/manual.html#3.3.2
|
||||
/// [`Chunk`]: crate::Chunk
|
||||
pub trait AsChunk<'a> {
|
||||
pub trait AsChunk {
|
||||
/// Returns optional chunk name
|
||||
///
|
||||
/// See [`Chunk::set_name`] for possible name prefixes.
|
||||
fn name(&self) -> Option<StdString> {
|
||||
None
|
||||
}
|
||||
@@ -35,68 +38,101 @@ pub trait AsChunk<'a> {
|
||||
}
|
||||
|
||||
/// Returns chunk data (can be text or binary)
|
||||
fn source(self) -> IoResult<Cow<'a, [u8]>>;
|
||||
fn source<'a>(&self) -> IoResult<Cow<'a, [u8]>>
|
||||
where
|
||||
Self: 'a;
|
||||
}
|
||||
|
||||
impl<'a> AsChunk<'a> for &'a str {
|
||||
fn source(self) -> IoResult<Cow<'a, [u8]>> {
|
||||
Ok(Cow::Borrowed(self.as_ref()))
|
||||
}
|
||||
}
|
||||
|
||||
impl AsChunk<'static> for StdString {
|
||||
fn source(self) -> IoResult<Cow<'static, [u8]>> {
|
||||
Ok(Cow::Owned(self.into_bytes()))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> AsChunk<'a> for &'a StdString {
|
||||
fn source(self) -> IoResult<Cow<'a, [u8]>> {
|
||||
impl AsChunk for &str {
|
||||
fn source<'a>(&self) -> IoResult<Cow<'a, [u8]>>
|
||||
where
|
||||
Self: 'a,
|
||||
{
|
||||
Ok(Cow::Borrowed(self.as_bytes()))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> AsChunk<'a> for &'a [u8] {
|
||||
fn source(self) -> IoResult<Cow<'a, [u8]>> {
|
||||
impl AsChunk for StdString {
|
||||
fn source<'a>(&self) -> IoResult<Cow<'a, [u8]>> {
|
||||
Ok(Cow::Owned(self.clone().into_bytes()))
|
||||
}
|
||||
}
|
||||
|
||||
impl AsChunk for &StdString {
|
||||
fn source<'a>(&self) -> IoResult<Cow<'a, [u8]>>
|
||||
where
|
||||
Self: 'a,
|
||||
{
|
||||
Ok(Cow::Borrowed(self.as_bytes()))
|
||||
}
|
||||
}
|
||||
|
||||
impl AsChunk for &[u8] {
|
||||
fn source<'a>(&self) -> IoResult<Cow<'a, [u8]>>
|
||||
where
|
||||
Self: 'a,
|
||||
{
|
||||
Ok(Cow::Borrowed(self))
|
||||
}
|
||||
}
|
||||
|
||||
impl AsChunk<'static> for Vec<u8> {
|
||||
fn source(self) -> IoResult<Cow<'static, [u8]>> {
|
||||
Ok(Cow::Owned(self))
|
||||
impl AsChunk for Vec<u8> {
|
||||
fn source<'a>(&self) -> IoResult<Cow<'a, [u8]>> {
|
||||
Ok(Cow::Owned(self.clone()))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> AsChunk<'a> for &'a Vec<u8> {
|
||||
fn source(self) -> IoResult<Cow<'a, [u8]>> {
|
||||
Ok(Cow::Borrowed(self.as_ref()))
|
||||
impl AsChunk for &Vec<u8> {
|
||||
fn source<'a>(&self) -> IoResult<Cow<'a, [u8]>>
|
||||
where
|
||||
Self: 'a,
|
||||
{
|
||||
Ok(Cow::Borrowed(self))
|
||||
}
|
||||
}
|
||||
|
||||
impl AsChunk<'static> for &Path {
|
||||
impl AsChunk for &Path {
|
||||
fn name(&self) -> Option<StdString> {
|
||||
Some(format!("@{}", self.display()))
|
||||
}
|
||||
|
||||
fn source(self) -> IoResult<Cow<'static, [u8]>> {
|
||||
fn source<'a>(&self) -> IoResult<Cow<'a, [u8]>> {
|
||||
std::fs::read(self).map(Cow::Owned)
|
||||
}
|
||||
}
|
||||
|
||||
impl AsChunk<'static> for PathBuf {
|
||||
impl AsChunk for PathBuf {
|
||||
fn name(&self) -> Option<StdString> {
|
||||
Some(format!("@{}", self.display()))
|
||||
}
|
||||
|
||||
fn source(self) -> IoResult<Cow<'static, [u8]>> {
|
||||
fn source<'a>(&self) -> IoResult<Cow<'a, [u8]>> {
|
||||
std::fs::read(self).map(Cow::Owned)
|
||||
}
|
||||
}
|
||||
|
||||
impl<C: AsChunk + ?Sized> AsChunk for Box<C> {
|
||||
fn name(&self) -> Option<StdString> {
|
||||
(**self).name()
|
||||
}
|
||||
|
||||
fn environment(&self, lua: &Lua) -> Result<Option<Table>> {
|
||||
(**self).environment(lua)
|
||||
}
|
||||
|
||||
fn mode(&self) -> Option<ChunkMode> {
|
||||
(**self).mode()
|
||||
}
|
||||
|
||||
fn source<'a>(&self) -> IoResult<Cow<'a, [u8]>>
|
||||
where
|
||||
Self: 'a,
|
||||
{
|
||||
(**self).source()
|
||||
}
|
||||
}
|
||||
|
||||
/// Returned from [`Lua::load`] and is used to finalize loading and executing Lua main chunks.
|
||||
///
|
||||
/// [`Lua::load`]: crate::Lua::load
|
||||
#[must_use = "`Chunk`s do nothing unless one of `exec`, `eval`, `call`, or `into_function` are called on them"]
|
||||
pub struct Chunk<'a> {
|
||||
pub(crate) lua: WeakLua,
|
||||
@@ -115,6 +151,49 @@ pub enum ChunkMode {
|
||||
Binary,
|
||||
}
|
||||
|
||||
/// Represents a constant value that can be used by Luau compiler.
|
||||
#[cfg(any(feature = "luau", doc))]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum CompileConstant {
|
||||
Nil,
|
||||
Boolean(bool),
|
||||
Number(crate::Number),
|
||||
Vector(crate::Vector),
|
||||
String(StdString),
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "luau", doc))]
|
||||
impl From<bool> for CompileConstant {
|
||||
fn from(b: bool) -> Self {
|
||||
CompileConstant::Boolean(b)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "luau", doc))]
|
||||
impl From<crate::Number> for CompileConstant {
|
||||
fn from(n: crate::Number) -> Self {
|
||||
CompileConstant::Number(n)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "luau", doc))]
|
||||
impl From<crate::Vector> for CompileConstant {
|
||||
fn from(v: crate::Vector) -> Self {
|
||||
CompileConstant::Vector(v)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "luau", doc))]
|
||||
impl From<&str> for CompileConstant {
|
||||
fn from(s: &str) -> Self {
|
||||
CompileConstant::String(s.to_owned())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "luau", doc))]
|
||||
type LibraryMemberConstantMap = HashMap<(StdString, StdString), CompileConstant>;
|
||||
|
||||
/// Luau compiler
|
||||
#[cfg(any(feature = "luau", doc))]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
|
||||
@@ -124,11 +203,14 @@ pub struct Compiler {
|
||||
debug_level: u8,
|
||||
type_info_level: u8,
|
||||
coverage_level: u8,
|
||||
vector_lib: Option<String>,
|
||||
vector_ctor: Option<String>,
|
||||
vector_type: Option<String>,
|
||||
mutable_globals: Vec<String>,
|
||||
userdata_types: Vec<String>,
|
||||
vector_lib: Option<StdString>,
|
||||
vector_ctor: Option<StdString>,
|
||||
vector_type: Option<StdString>,
|
||||
mutable_globals: Vec<StdString>,
|
||||
userdata_types: Vec<StdString>,
|
||||
libraries_with_known_members: Vec<StdString>,
|
||||
library_constants: Option<LibraryMemberConstantMap>,
|
||||
disabled_builtins: Vec<StdString>,
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "luau", doc))]
|
||||
@@ -153,6 +235,9 @@ impl Compiler {
|
||||
vector_type: None,
|
||||
mutable_globals: Vec::new(),
|
||||
userdata_types: Vec::new(),
|
||||
libraries_with_known_members: Vec::new(),
|
||||
library_constants: None,
|
||||
disabled_builtins: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -185,6 +270,7 @@ impl Compiler {
|
||||
/// Possible values:
|
||||
/// * 0 - generate for native modules (default)
|
||||
/// * 1 - generate for all modules
|
||||
#[must_use]
|
||||
pub const fn set_type_info_level(mut self, level: u8) -> Self {
|
||||
self.type_info_level = level;
|
||||
self
|
||||
@@ -202,48 +288,113 @@ impl Compiler {
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets alternative global builtin to construct vectors, in addition to default builtin
|
||||
/// `vector.create`.
|
||||
///
|
||||
/// To set the library and method name, use the `lib.ctor` format.
|
||||
#[doc(hidden)]
|
||||
#[must_use]
|
||||
pub fn set_vector_lib(mut self, lib: impl Into<String>) -> Self {
|
||||
self.vector_lib = Some(lib.into());
|
||||
pub fn set_vector_ctor(mut self, ctor: impl Into<StdString>) -> Self {
|
||||
let ctor = ctor.into();
|
||||
let lib_ctor = ctor.split_once('.');
|
||||
self.vector_lib = lib_ctor.as_ref().map(|&(lib, _)| lib.to_owned());
|
||||
self.vector_ctor = (lib_ctor.as_ref())
|
||||
.map(|&(_, ctor)| ctor.to_owned())
|
||||
.or(Some(ctor));
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets alternative vector type name for type tables, in addition to default type `vector`.
|
||||
#[doc(hidden)]
|
||||
#[must_use]
|
||||
pub fn set_vector_ctor(mut self, ctor: impl Into<String>) -> Self {
|
||||
self.vector_ctor = Some(ctor.into());
|
||||
self
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
#[must_use]
|
||||
pub fn set_vector_type(mut self, r#type: impl Into<String>) -> Self {
|
||||
pub fn set_vector_type(mut self, r#type: impl Into<StdString>) -> Self {
|
||||
self.vector_type = Some(r#type.into());
|
||||
self
|
||||
}
|
||||
|
||||
/// Adds a mutable global.
|
||||
///
|
||||
/// It disables the import optimization for fields accessed through it.
|
||||
#[must_use]
|
||||
pub fn add_mutable_global(mut self, global: impl Into<StdString>) -> Self {
|
||||
self.mutable_globals.push(global.into());
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets a list of globals that are mutable.
|
||||
///
|
||||
/// It disables the import optimization for fields accessed through these.
|
||||
#[must_use]
|
||||
pub fn set_mutable_globals(mut self, globals: Vec<String>) -> Self {
|
||||
self.mutable_globals = globals;
|
||||
pub fn set_mutable_globals<S: Into<StdString>>(mut self, globals: impl IntoIterator<Item = S>) -> Self {
|
||||
self.mutable_globals = globals.into_iter().map(|s| s.into()).collect();
|
||||
self
|
||||
}
|
||||
|
||||
/// Adds a userdata type to the list that will be included in the type information.
|
||||
#[must_use]
|
||||
pub fn add_userdata_type(mut self, r#type: impl Into<StdString>) -> Self {
|
||||
self.userdata_types.push(r#type.into());
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets a list of userdata types that will be included in the type information.
|
||||
#[must_use]
|
||||
pub fn set_userdata_types(mut self, types: Vec<String>) -> Self {
|
||||
self.userdata_types = types;
|
||||
pub fn set_userdata_types<S: Into<StdString>>(mut self, types: impl IntoIterator<Item = S>) -> Self {
|
||||
self.userdata_types = types.into_iter().map(|s| s.into()).collect();
|
||||
self
|
||||
}
|
||||
|
||||
/// Adds a constant for a known library member.
|
||||
///
|
||||
/// The constants are used by the compiler to optimize the generated bytecode.
|
||||
/// Optimization level must be at least 2 for this to have any effect.
|
||||
///
|
||||
/// The `name` is a string in the format `lib.member`, where `lib` is the library name
|
||||
/// and `member` is the member (constant) name.
|
||||
#[must_use]
|
||||
pub fn add_library_constant(
|
||||
mut self,
|
||||
name: impl AsRef<str>,
|
||||
r#const: impl Into<CompileConstant>,
|
||||
) -> Self {
|
||||
let Some((lib, member)) = name.as_ref().split_once('.') else {
|
||||
return self;
|
||||
};
|
||||
let (lib, member) = (lib.to_owned(), member.to_owned());
|
||||
|
||||
if !self.libraries_with_known_members.contains(&lib) {
|
||||
self.libraries_with_known_members.push(lib.clone());
|
||||
}
|
||||
self.library_constants
|
||||
.get_or_insert_with(HashMap::new)
|
||||
.insert((lib, member), r#const.into());
|
||||
self
|
||||
}
|
||||
|
||||
/// Adds a builtin that should be disabled.
|
||||
#[must_use]
|
||||
pub fn add_disabled_builtin(mut self, builtin: impl Into<StdString>) -> Self {
|
||||
self.disabled_builtins.push(builtin.into());
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets a list of builtins that should be disabled.
|
||||
#[must_use]
|
||||
pub fn set_disabled_builtins<S: Into<StdString>>(
|
||||
mut self,
|
||||
builtins: impl IntoIterator<Item = S>,
|
||||
) -> Self {
|
||||
self.disabled_builtins = builtins.into_iter().map(|s| s.into()).collect();
|
||||
self
|
||||
}
|
||||
|
||||
/// Compiles the `source` into bytecode.
|
||||
///
|
||||
/// Returns `Error::SyntaxError` if the source code is invalid.
|
||||
/// Returns [`Error::SyntaxError`] if the source code is invalid.
|
||||
pub fn compile(&self, source: impl AsRef<[u8]>) -> Result<Vec<u8>> {
|
||||
use std::os::raw::c_int;
|
||||
use std::cell::RefCell;
|
||||
use std::ffi::CStr;
|
||||
use std::os::raw::{c_char, c_int};
|
||||
use std::ptr;
|
||||
|
||||
let vector_lib = self.vector_lib.clone();
|
||||
@@ -275,6 +426,44 @@ impl Compiler {
|
||||
|
||||
vec2cstring_ptr!(mutable_globals, mutable_globals_ptr);
|
||||
vec2cstring_ptr!(userdata_types, userdata_types_ptr);
|
||||
vec2cstring_ptr!(libraries_with_known_members, libraries_with_known_members_ptr);
|
||||
vec2cstring_ptr!(disabled_builtins, disabled_builtins_ptr);
|
||||
|
||||
thread_local! {
|
||||
static LIBRARY_MEMBER_CONSTANT_MAP: RefCell<LibraryMemberConstantMap> = Default::default();
|
||||
}
|
||||
|
||||
#[cfg(feature = "luau")]
|
||||
unsafe extern "C-unwind" fn library_member_constant_callback(
|
||||
library: *const c_char,
|
||||
member: *const c_char,
|
||||
constant: *mut ffi::lua_CompileConstant,
|
||||
) {
|
||||
let library = CStr::from_ptr(library).to_string_lossy();
|
||||
let member = CStr::from_ptr(member).to_string_lossy();
|
||||
LIBRARY_MEMBER_CONSTANT_MAP.with_borrow(|map| {
|
||||
if let Some(cons) = map.get(&(library.to_string(), member.to_string())) {
|
||||
match cons {
|
||||
CompileConstant::Nil => ffi::luau_set_compile_constant_nil(constant),
|
||||
CompileConstant::Boolean(b) => {
|
||||
ffi::luau_set_compile_constant_boolean(constant, *b as c_int)
|
||||
}
|
||||
CompileConstant::Number(n) => ffi::luau_set_compile_constant_number(constant, *n),
|
||||
CompileConstant::Vector(v) => {
|
||||
#[cfg(not(feature = "luau-vector4"))]
|
||||
ffi::luau_set_compile_constant_vector(constant, v.x(), v.y(), v.z(), 0.0);
|
||||
#[cfg(feature = "luau-vector4")]
|
||||
ffi::luau_set_compile_constant_vector(constant, v.x(), v.y(), v.z(), v.w());
|
||||
}
|
||||
CompileConstant::String(s) => ffi::luau_set_compile_constant_string(
|
||||
constant,
|
||||
s.as_ptr() as *const c_char,
|
||||
s.len(),
|
||||
),
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
let bytecode = unsafe {
|
||||
let mut options = ffi::lua_CompileOptions::default();
|
||||
@@ -287,13 +476,21 @@ impl Compiler {
|
||||
options.vectorType = vector_type.map_or(ptr::null(), |s| s.as_ptr());
|
||||
options.mutableGlobals = mutable_globals_ptr;
|
||||
options.userdataTypes = userdata_types_ptr;
|
||||
options.librariesWithKnownMembers = libraries_with_known_members_ptr;
|
||||
if let Some(map) = self.library_constants.as_ref() {
|
||||
if !self.libraries_with_known_members.is_empty() {
|
||||
LIBRARY_MEMBER_CONSTANT_MAP.with_borrow_mut(|gmap| *gmap = map.clone());
|
||||
options.libraryMemberConstantCallback = Some(library_member_constant_callback);
|
||||
}
|
||||
}
|
||||
options.disabledBuiltins = disabled_builtins_ptr;
|
||||
ffi::luau_compile(source.as_ref(), options)
|
||||
};
|
||||
|
||||
if bytecode.first() == Some(&0) {
|
||||
// The rest of the bytecode is the error message starting with `:`
|
||||
// See https://github.com/luau-lang/luau/blob/0.640/Compiler/src/Compiler.cpp#L4336
|
||||
let message = String::from_utf8_lossy(&bytecode[2..]).to_string();
|
||||
let message = StdString::from_utf8_lossy(&bytecode[2..]).into_owned();
|
||||
return Err(Error::SyntaxError {
|
||||
incomplete_input: message.ends_with("<eof>"),
|
||||
message,
|
||||
@@ -304,13 +501,28 @@ impl Compiler {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Chunk<'a> {
|
||||
impl Chunk<'_> {
|
||||
/// Returns the name of this chunk.
|
||||
pub fn name(&self) -> &str {
|
||||
&self.name
|
||||
}
|
||||
|
||||
/// Sets the name of this chunk, which results in more informative error traces.
|
||||
pub fn set_name(mut self, name: impl Into<String>) -> Self {
|
||||
///
|
||||
/// Possible name prefixes:
|
||||
/// - `@` - file path (when truncation is needed, the end of the file path is kept, as this is
|
||||
/// more useful for identifying the file)
|
||||
/// - `=` - custom chunk name (when truncation is needed, the beginning of the name is kept)
|
||||
pub fn set_name(mut self, name: impl Into<StdString>) -> Self {
|
||||
self.name = name.into();
|
||||
self
|
||||
}
|
||||
|
||||
/// Returns the environment of this chunk.
|
||||
pub fn environment(&self) -> Option<&Table> {
|
||||
self.env.as_ref().ok()?.as_ref()
|
||||
}
|
||||
|
||||
/// Sets the environment of the loaded chunk to the given value.
|
||||
///
|
||||
/// In Lua >=5.2 main chunks always have exactly one upvalue, and this upvalue is used as the
|
||||
@@ -327,6 +539,11 @@ impl<'a> Chunk<'a> {
|
||||
self
|
||||
}
|
||||
|
||||
/// Returns the mode (auto-detected by default) of this chunk.
|
||||
pub fn mode(&self) -> ChunkMode {
|
||||
self.detect_mode()
|
||||
}
|
||||
|
||||
/// Sets whether the chunk is text or binary (autodetected by default).
|
||||
///
|
||||
/// Be aware, Lua does not check the consistency of the code inside binary chunks.
|
||||
@@ -339,8 +556,6 @@ impl<'a> Chunk<'a> {
|
||||
/// Sets or overwrites a Luau compiler used for this chunk.
|
||||
///
|
||||
/// See [`Compiler`] for details and possible options.
|
||||
///
|
||||
/// Requires `feature = "luau"`
|
||||
#[cfg(any(feature = "luau", doc))]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
|
||||
pub fn set_compiler(mut self, compiler: Compiler) -> Self {
|
||||
@@ -359,9 +574,7 @@ impl<'a> Chunk<'a> {
|
||||
///
|
||||
/// See [`exec`] for more details.
|
||||
///
|
||||
/// Requires `feature = "async"`
|
||||
///
|
||||
/// [`exec`]: #method.exec
|
||||
/// [`exec`]: Chunk::exec
|
||||
#[cfg(feature = "async")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
|
||||
pub async fn exec_async(self) -> Result<()> {
|
||||
@@ -391,9 +604,7 @@ impl<'a> Chunk<'a> {
|
||||
///
|
||||
/// See [`eval`] for more details.
|
||||
///
|
||||
/// Requires `feature = "async"`
|
||||
///
|
||||
/// [`eval`]: #method.eval
|
||||
/// [`eval`]: Chunk::eval
|
||||
#[cfg(feature = "async")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
|
||||
pub async fn eval_async<R>(self) -> Result<R>
|
||||
@@ -420,9 +631,7 @@ impl<'a> Chunk<'a> {
|
||||
///
|
||||
/// See [`call`] for more details.
|
||||
///
|
||||
/// Requires `feature = "async"`
|
||||
///
|
||||
/// [`call`]: #method.call
|
||||
/// [`call`]: Chunk::call
|
||||
#[cfg(feature = "async")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
|
||||
pub async fn call_async<R>(self, args: impl IntoLuaMulti) -> Result<R>
|
||||
@@ -432,7 +641,7 @@ impl<'a> Chunk<'a> {
|
||||
self.into_function()?.call_async(args).await
|
||||
}
|
||||
|
||||
/// Load this chunk into a regular `Function`.
|
||||
/// Load this chunk into a regular [`Function`].
|
||||
///
|
||||
/// This simply compiles the chunk without actually executing it.
|
||||
#[cfg_attr(not(feature = "luau"), allow(unused_mut))]
|
||||
@@ -481,7 +690,7 @@ impl<'a> Chunk<'a> {
|
||||
if let Ok(ref source) = self.source {
|
||||
if self.detect_mode() == ChunkMode::Text {
|
||||
let lua = self.lua.lock();
|
||||
if let Some(cache) = lua.app_data_ref::<ChunksCache>() {
|
||||
if let Some(cache) = lua.priv_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);
|
||||
@@ -498,12 +707,12 @@ impl<'a> Chunk<'a> {
|
||||
if let Ok(ref binary_source) = self.source {
|
||||
if self.detect_mode() == ChunkMode::Binary {
|
||||
let lua = self.lua.lock();
|
||||
if let Some(mut cache) = lua.app_data_mut::<ChunksCache>() {
|
||||
cache.0.insert(text_source, binary_source.as_ref().to_vec());
|
||||
if let Some(mut cache) = lua.priv_app_data_mut::<ChunksCache>() {
|
||||
cache.0.insert(text_source, binary_source.to_vec());
|
||||
} else {
|
||||
let mut cache = ChunksCache(HashMap::new());
|
||||
cache.0.insert(text_source, binary_source.as_ref().to_vec());
|
||||
let _ = lua.try_set_app_data(cache);
|
||||
cache.0.insert(text_source, binary_source.to_vec());
|
||||
lua.set_priv_app_data(cache);
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -536,24 +745,23 @@ impl<'a> Chunk<'a> {
|
||||
}
|
||||
|
||||
fn detect_mode(&self) -> ChunkMode {
|
||||
match (self.mode, &self.source) {
|
||||
(Some(mode), _) => mode,
|
||||
(None, Ok(source)) => {
|
||||
#[cfg(not(feature = "luau"))]
|
||||
if source.starts_with(ffi::LUA_SIGNATURE) {
|
||||
return ChunkMode::Binary;
|
||||
}
|
||||
#[cfg(feature = "luau")]
|
||||
if *source.first().unwrap_or(&u8::MAX) < b'\n' {
|
||||
return ChunkMode::Binary;
|
||||
}
|
||||
ChunkMode::Text
|
||||
}
|
||||
(None, Err(_)) => ChunkMode::Text, // any value is fine
|
||||
if let Some(mode) = self.mode {
|
||||
return mode;
|
||||
}
|
||||
if let Ok(source) = &self.source {
|
||||
#[cfg(not(feature = "luau"))]
|
||||
if source.starts_with(ffi::LUA_SIGNATURE) {
|
||||
return ChunkMode::Binary;
|
||||
}
|
||||
#[cfg(feature = "luau")]
|
||||
if *source.first().unwrap_or(&u8::MAX) < b'\n' {
|
||||
return ChunkMode::Binary;
|
||||
}
|
||||
}
|
||||
ChunkMode::Text
|
||||
}
|
||||
|
||||
fn convert_name(name: String) -> Result<CString> {
|
||||
fn convert_name(name: StdString) -> Result<CString> {
|
||||
CString::new(name).map_err(|err| Error::runtime(format!("invalid name: {err}")))
|
||||
}
|
||||
|
||||
@@ -564,3 +772,31 @@ impl<'a> Chunk<'a> {
|
||||
buf
|
||||
}
|
||||
}
|
||||
|
||||
struct WrappedChunk<T: AsChunk> {
|
||||
chunk: T,
|
||||
caller: &'static Location<'static>,
|
||||
}
|
||||
|
||||
impl Chunk<'_> {
|
||||
/// Wraps a chunk of Lua code, returning an opaque type that implements [`IntoLua`] trait.
|
||||
///
|
||||
/// The resulted `IntoLua` implementation will convert the chunk into a Lua function without
|
||||
/// executing it.
|
||||
#[doc(hidden)]
|
||||
#[track_caller]
|
||||
pub fn wrap(chunk: impl AsChunk) -> impl IntoLua {
|
||||
WrappedChunk {
|
||||
chunk,
|
||||
caller: Location::caller(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: AsChunk> IntoLua for WrappedChunk<T> {
|
||||
fn into_lua(self, lua: &Lua) -> Result<Value> {
|
||||
lua.load_with_location(self.chunk, self.caller)
|
||||
.into_function()
|
||||
.map(Value::Function)
|
||||
}
|
||||
}
|
||||
|
||||
+333
-78
@@ -1,23 +1,25 @@
|
||||
use std::borrow::Cow;
|
||||
use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet};
|
||||
use std::ffi::{CStr, CString};
|
||||
use std::ffi::{CStr, CString, OsStr, OsString};
|
||||
use std::hash::{BuildHasher, Hash};
|
||||
use std::os::raw::c_int;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::string::String as StdString;
|
||||
use std::{slice, str};
|
||||
use std::{mem, slice, str};
|
||||
|
||||
use bstr::{BStr, BString};
|
||||
use bstr::{BStr, BString, ByteSlice, ByteVec};
|
||||
use num_traits::cast;
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
use crate::function::Function;
|
||||
use crate::state::{Lua, RawLua};
|
||||
use crate::string::String;
|
||||
use crate::string::{BorrowedBytes, BorrowedStr, String};
|
||||
use crate::table::Table;
|
||||
use crate::thread::Thread;
|
||||
use crate::types::{LightUserData, MaybeSend, RegistryKey};
|
||||
use crate::traits::{FromLua, IntoLua, ShortTypeName as _};
|
||||
use crate::types::{Either, LightUserData, MaybeSend, RegistryKey};
|
||||
use crate::userdata::{AnyUserData, UserData};
|
||||
use crate::value::{FromLua, IntoLua, Nil, Value};
|
||||
use crate::value::{Nil, Value};
|
||||
|
||||
impl IntoLua for Value {
|
||||
#[inline]
|
||||
@@ -72,7 +74,7 @@ impl FromLua for String {
|
||||
lua.coerce_string(value)?
|
||||
.ok_or_else(|| Error::FromLuaConversionError {
|
||||
from: ty,
|
||||
to: "string",
|
||||
to: "string".to_string(),
|
||||
message: Some("expected string or number".to_string()),
|
||||
})
|
||||
}
|
||||
@@ -89,6 +91,94 @@ impl FromLua for String {
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoLua for BorrowedStr<'_> {
|
||||
#[inline]
|
||||
fn into_lua(self, _: &Lua) -> Result<Value> {
|
||||
Ok(Value::String(self.borrow.into_owned()))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn push_into_stack(self, lua: &RawLua) -> Result<()> {
|
||||
lua.push_ref(&self.borrow.0);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoLua for &BorrowedStr<'_> {
|
||||
#[inline]
|
||||
fn into_lua(self, _: &Lua) -> Result<Value> {
|
||||
Ok(Value::String(self.borrow.clone().into_owned()))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn push_into_stack(self, lua: &RawLua) -> Result<()> {
|
||||
lua.push_ref(&self.borrow.0);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl FromLua for BorrowedStr<'_> {
|
||||
fn from_lua(value: Value, lua: &Lua) -> Result<Self> {
|
||||
let s = String::from_lua(value, lua)?;
|
||||
let BorrowedStr { buf, _lua, .. } = BorrowedStr::try_from(&s)?;
|
||||
let buf = unsafe { mem::transmute::<&str, &'static str>(buf) };
|
||||
let borrow = Cow::Owned(s);
|
||||
Ok(Self { buf, borrow, _lua })
|
||||
}
|
||||
|
||||
unsafe fn from_stack(idx: c_int, lua: &RawLua) -> Result<Self> {
|
||||
let s = String::from_stack(idx, lua)?;
|
||||
let BorrowedStr { buf, _lua, .. } = BorrowedStr::try_from(&s)?;
|
||||
let buf = unsafe { mem::transmute::<&str, &'static str>(buf) };
|
||||
let borrow = Cow::Owned(s);
|
||||
Ok(Self { buf, borrow, _lua })
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoLua for BorrowedBytes<'_> {
|
||||
#[inline]
|
||||
fn into_lua(self, _: &Lua) -> Result<Value> {
|
||||
Ok(Value::String(self.borrow.into_owned()))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn push_into_stack(self, lua: &RawLua) -> Result<()> {
|
||||
lua.push_ref(&self.borrow.0);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoLua for &BorrowedBytes<'_> {
|
||||
#[inline]
|
||||
fn into_lua(self, _: &Lua) -> Result<Value> {
|
||||
Ok(Value::String(self.borrow.clone().into_owned()))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn push_into_stack(self, lua: &RawLua) -> Result<()> {
|
||||
lua.push_ref(&self.borrow.0);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl FromLua for BorrowedBytes<'_> {
|
||||
fn from_lua(value: Value, lua: &Lua) -> Result<Self> {
|
||||
let s = String::from_lua(value, lua)?;
|
||||
let BorrowedBytes { buf, _lua, .. } = BorrowedBytes::from(&s);
|
||||
let buf = unsafe { mem::transmute::<&[u8], &'static [u8]>(buf) };
|
||||
let borrow = Cow::Owned(s);
|
||||
Ok(Self { buf, borrow, _lua })
|
||||
}
|
||||
|
||||
unsafe fn from_stack(idx: c_int, lua: &RawLua) -> Result<Self> {
|
||||
let s = String::from_stack(idx, lua)?;
|
||||
let BorrowedBytes { buf, _lua, .. } = BorrowedBytes::from(&s);
|
||||
let buf = unsafe { mem::transmute::<&[u8], &'static [u8]>(buf) };
|
||||
let borrow = Cow::Owned(s);
|
||||
Ok(Self { buf, borrow, _lua })
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoLua for Table {
|
||||
#[inline]
|
||||
fn into_lua(self, _: &Lua) -> Result<Value> {
|
||||
@@ -116,7 +206,7 @@ impl FromLua for Table {
|
||||
Value::Table(table) => Ok(table),
|
||||
_ => Err(Error::FromLuaConversionError {
|
||||
from: value.type_name(),
|
||||
to: "table",
|
||||
to: "table".to_string(),
|
||||
message: None,
|
||||
}),
|
||||
}
|
||||
@@ -150,7 +240,7 @@ impl FromLua for Function {
|
||||
Value::Function(table) => Ok(table),
|
||||
_ => Err(Error::FromLuaConversionError {
|
||||
from: value.type_name(),
|
||||
to: "function",
|
||||
to: "function".to_string(),
|
||||
message: None,
|
||||
}),
|
||||
}
|
||||
@@ -184,7 +274,7 @@ impl FromLua for Thread {
|
||||
Value::Thread(t) => Ok(t),
|
||||
_ => Err(Error::FromLuaConversionError {
|
||||
from: value.type_name(),
|
||||
to: "thread",
|
||||
to: "thread".to_string(),
|
||||
message: None,
|
||||
}),
|
||||
}
|
||||
@@ -218,7 +308,7 @@ impl FromLua for AnyUserData {
|
||||
Value::UserData(ud) => Ok(ud),
|
||||
_ => Err(Error::FromLuaConversionError {
|
||||
from: value.type_name(),
|
||||
to: "userdata",
|
||||
to: "userdata".to_string(),
|
||||
message: None,
|
||||
}),
|
||||
}
|
||||
@@ -241,18 +331,22 @@ impl IntoLua for Error {
|
||||
|
||||
impl FromLua for Error {
|
||||
#[inline]
|
||||
fn from_lua(value: Value, lua: &Lua) -> Result<Error> {
|
||||
fn from_lua(value: Value, _: &Lua) -> Result<Error> {
|
||||
match value {
|
||||
Value::Error(err) => Ok(*err),
|
||||
val => Ok(Error::runtime(
|
||||
lua.coerce_string(val)?
|
||||
.and_then(|s| Some(s.to_str().ok()?.to_owned()))
|
||||
.unwrap_or_else(|| "<unprintable error>".to_owned()),
|
||||
)),
|
||||
val => Ok(Error::runtime(val.to_string()?)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "anyhow")]
|
||||
impl IntoLua for anyhow::Error {
|
||||
#[inline]
|
||||
fn into_lua(self, _: &Lua) -> Result<Value> {
|
||||
Ok(Value::Error(Box::new(Error::from(self))))
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoLua for RegistryKey {
|
||||
#[inline]
|
||||
fn into_lua(self, lua: &Lua) -> Result<Value> {
|
||||
@@ -336,7 +430,7 @@ impl FromLua for LightUserData {
|
||||
Value::LightUserData(ud) => Ok(ud),
|
||||
_ => Err(Error::FromLuaConversionError {
|
||||
from: value.type_name(),
|
||||
to: "light userdata",
|
||||
to: "lightuserdata".to_string(),
|
||||
message: None,
|
||||
}),
|
||||
}
|
||||
@@ -344,7 +438,7 @@ impl FromLua for LightUserData {
|
||||
}
|
||||
|
||||
#[cfg(feature = "luau")]
|
||||
impl IntoLua for crate::types::Vector {
|
||||
impl IntoLua for crate::Vector {
|
||||
#[inline]
|
||||
fn into_lua(self, _: &Lua) -> Result<Value> {
|
||||
Ok(Value::Vector(self))
|
||||
@@ -352,14 +446,51 @@ impl IntoLua for crate::types::Vector {
|
||||
}
|
||||
|
||||
#[cfg(feature = "luau")]
|
||||
impl FromLua for crate::types::Vector {
|
||||
impl FromLua for crate::Vector {
|
||||
#[inline]
|
||||
fn from_lua(value: Value, _: &Lua) -> Result<Self> {
|
||||
match value {
|
||||
Value::Vector(v) => Ok(v),
|
||||
_ => Err(Error::FromLuaConversionError {
|
||||
from: value.type_name(),
|
||||
to: "vector",
|
||||
to: "vector".to_string(),
|
||||
message: None,
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "luau")]
|
||||
impl IntoLua for crate::Buffer {
|
||||
#[inline]
|
||||
fn into_lua(self, _: &Lua) -> Result<Value> {
|
||||
Ok(Value::Buffer(self))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "luau")]
|
||||
impl IntoLua for &crate::Buffer {
|
||||
#[inline]
|
||||
fn into_lua(self, _: &Lua) -> Result<Value> {
|
||||
Ok(Value::Buffer(self.clone()))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn push_into_stack(self, lua: &RawLua) -> Result<()> {
|
||||
lua.push_ref(&self.0);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "luau")]
|
||||
impl FromLua for crate::Buffer {
|
||||
#[inline]
|
||||
fn from_lua(value: Value, _: &Lua) -> Result<Self> {
|
||||
match value {
|
||||
Value::Buffer(buf) => Ok(buf),
|
||||
_ => Err(Error::FromLuaConversionError {
|
||||
from: value.type_name(),
|
||||
to: "buffer".to_string(),
|
||||
message: None,
|
||||
}),
|
||||
}
|
||||
@@ -386,7 +517,7 @@ impl FromLua for StdString {
|
||||
.coerce_string(value)?
|
||||
.ok_or_else(|| Error::FromLuaConversionError {
|
||||
from: ty,
|
||||
to: "String",
|
||||
to: Self::type_name(),
|
||||
message: Some("expected string or number".to_string()),
|
||||
})?
|
||||
.to_str()?
|
||||
@@ -405,7 +536,7 @@ impl FromLua for StdString {
|
||||
.map(|s| s.to_owned())
|
||||
.map_err(|e| Error::FromLuaConversionError {
|
||||
from: "string",
|
||||
to: "String",
|
||||
to: Self::type_name(),
|
||||
message: Some(e.to_string()),
|
||||
});
|
||||
}
|
||||
@@ -448,7 +579,7 @@ impl FromLua for Box<str> {
|
||||
.coerce_string(value)?
|
||||
.ok_or_else(|| Error::FromLuaConversionError {
|
||||
from: ty,
|
||||
to: "Box<str>",
|
||||
to: Self::type_name(),
|
||||
message: Some("expected string or number".to_string()),
|
||||
})?
|
||||
.to_str()?
|
||||
@@ -472,7 +603,7 @@ impl FromLua for CString {
|
||||
.coerce_string(value)?
|
||||
.ok_or_else(|| Error::FromLuaConversionError {
|
||||
from: ty,
|
||||
to: "CString",
|
||||
to: Self::type_name(),
|
||||
message: Some("expected string or number".to_string()),
|
||||
})?;
|
||||
|
||||
@@ -480,7 +611,7 @@ impl FromLua for CString {
|
||||
Ok(s) => Ok(s.into()),
|
||||
Err(_) => Err(Error::FromLuaConversionError {
|
||||
from: ty,
|
||||
to: "CString",
|
||||
to: Self::type_name(),
|
||||
message: Some("invalid C-style string".to_string()),
|
||||
}),
|
||||
}
|
||||
@@ -514,18 +645,12 @@ impl FromLua for BString {
|
||||
match value {
|
||||
Value::String(s) => Ok((*s.as_bytes()).into()),
|
||||
#[cfg(feature = "luau")]
|
||||
Value::UserData(ud) if ud.1 == crate::types::SubtypeId::Buffer => unsafe {
|
||||
let lua = ud.0.lua.lock();
|
||||
let mut size = 0usize;
|
||||
let buf = ffi::lua_tobuffer(lua.ref_thread(), ud.0.index, &mut size);
|
||||
mlua_assert!(!buf.is_null(), "invalid Luau buffer");
|
||||
Ok(slice::from_raw_parts(buf as *const u8, size).into())
|
||||
},
|
||||
Value::Buffer(buf) => Ok(buf.to_vec().into()),
|
||||
_ => Ok((*lua
|
||||
.coerce_string(value)?
|
||||
.ok_or_else(|| Error::FromLuaConversionError {
|
||||
from: ty,
|
||||
to: "BString",
|
||||
to: Self::type_name(),
|
||||
message: Some("expected string or number".to_string()),
|
||||
})?
|
||||
.as_bytes())
|
||||
@@ -563,6 +688,106 @@ impl IntoLua for &BStr {
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoLua for OsString {
|
||||
#[inline]
|
||||
fn into_lua(self, lua: &Lua) -> Result<Value> {
|
||||
self.as_os_str().into_lua(lua)
|
||||
}
|
||||
}
|
||||
|
||||
impl FromLua for OsString {
|
||||
#[inline]
|
||||
fn from_lua(value: Value, lua: &Lua) -> Result<Self> {
|
||||
let ty = value.type_name();
|
||||
let bs = BString::from_lua(value, lua)?;
|
||||
Vec::from(bs)
|
||||
.into_os_string()
|
||||
.map_err(|err| Error::FromLuaConversionError {
|
||||
from: ty,
|
||||
to: "OsString".into(),
|
||||
message: Some(err.to_string()),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoLua for &OsStr {
|
||||
#[inline]
|
||||
fn into_lua(self, lua: &Lua) -> Result<Value> {
|
||||
let s = <[u8]>::from_os_str(self).ok_or_else(|| Error::ToLuaConversionError {
|
||||
from: "OsStr".into(),
|
||||
to: "string",
|
||||
message: Some("invalid utf-8 encoding".into()),
|
||||
})?;
|
||||
Ok(Value::String(lua.create_string(s)?))
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoLua for PathBuf {
|
||||
#[inline]
|
||||
fn into_lua(self, lua: &Lua) -> Result<Value> {
|
||||
self.as_os_str().into_lua(lua)
|
||||
}
|
||||
}
|
||||
|
||||
impl FromLua for PathBuf {
|
||||
#[inline]
|
||||
fn from_lua(value: Value, lua: &Lua) -> Result<Self> {
|
||||
OsString::from_lua(value, lua).map(PathBuf::from)
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoLua for &Path {
|
||||
#[inline]
|
||||
fn into_lua(self, lua: &Lua) -> Result<Value> {
|
||||
self.as_os_str().into_lua(lua)
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoLua for char {
|
||||
#[inline]
|
||||
fn into_lua(self, lua: &Lua) -> Result<Value> {
|
||||
let mut char_bytes = [0; 4];
|
||||
self.encode_utf8(&mut char_bytes);
|
||||
Ok(Value::String(lua.create_string(&char_bytes[..self.len_utf8()])?))
|
||||
}
|
||||
}
|
||||
|
||||
impl FromLua for char {
|
||||
fn from_lua(value: Value, _lua: &Lua) -> Result<Self> {
|
||||
let ty = value.type_name();
|
||||
match value {
|
||||
Value::Integer(i) => {
|
||||
cast(i)
|
||||
.and_then(char::from_u32)
|
||||
.ok_or_else(|| Error::FromLuaConversionError {
|
||||
from: ty,
|
||||
to: "char".to_string(),
|
||||
message: Some("integer out of range when converting to char".to_string()),
|
||||
})
|
||||
}
|
||||
Value::String(s) => {
|
||||
let str = s.to_str()?;
|
||||
let mut str_iter = str.chars();
|
||||
match (str_iter.next(), str_iter.next()) {
|
||||
(Some(char), None) => Ok(char),
|
||||
_ => Err(Error::FromLuaConversionError {
|
||||
from: ty,
|
||||
to: "char".to_string(),
|
||||
message: Some(
|
||||
"expected string to have exactly one char when converting to char".to_string(),
|
||||
),
|
||||
}),
|
||||
}
|
||||
}
|
||||
_ => Err(Error::FromLuaConversionError {
|
||||
from: ty,
|
||||
to: Self::type_name(),
|
||||
message: Some("expected string or integer".to_string()),
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn push_bytes_into_stack<T>(this: T, lua: &RawLua) -> Result<()>
|
||||
where
|
||||
@@ -583,15 +808,9 @@ macro_rules! lua_convert_int {
|
||||
impl IntoLua for $x {
|
||||
#[inline]
|
||||
fn into_lua(self, _: &Lua) -> Result<Value> {
|
||||
cast(self)
|
||||
Ok(cast(self)
|
||||
.map(Value::Integer)
|
||||
.or_else(|| cast(self).map(Value::Number))
|
||||
// This is impossible error because conversion to Number never fails
|
||||
.ok_or_else(|| Error::ToLuaConversionError {
|
||||
from: stringify!($x),
|
||||
to: "number",
|
||||
message: Some("out of range".to_owned()),
|
||||
})
|
||||
.unwrap_or_else(|| Value::Number(self as ffi::lua_Number)))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@@ -619,7 +838,7 @@ macro_rules! lua_convert_int {
|
||||
lua.coerce_number(value)?
|
||||
.ok_or_else(|| Error::FromLuaConversionError {
|
||||
from: ty,
|
||||
to: stringify!($x),
|
||||
to: stringify!($x).to_string(),
|
||||
message: Some(
|
||||
"expected number or string coercible to number".to_string(),
|
||||
),
|
||||
@@ -630,7 +849,7 @@ macro_rules! lua_convert_int {
|
||||
})
|
||||
.ok_or_else(|| Error::FromLuaConversionError {
|
||||
from: ty,
|
||||
to: stringify!($x),
|
||||
to: stringify!($x).to_string(),
|
||||
message: Some("out of range".to_owned()),
|
||||
})
|
||||
}
|
||||
@@ -644,7 +863,7 @@ macro_rules! lua_convert_int {
|
||||
if ok != 0 {
|
||||
return cast(i).ok_or_else(|| Error::FromLuaConversionError {
|
||||
from: "integer",
|
||||
to: stringify!($x),
|
||||
to: stringify!($x).to_string(),
|
||||
message: Some("out of range".to_owned()),
|
||||
});
|
||||
}
|
||||
@@ -674,13 +893,7 @@ macro_rules! lua_convert_float {
|
||||
impl IntoLua for $x {
|
||||
#[inline]
|
||||
fn into_lua(self, _: &Lua) -> Result<Value> {
|
||||
cast(self)
|
||||
.ok_or_else(|| Error::ToLuaConversionError {
|
||||
from: stringify!($x),
|
||||
to: "number",
|
||||
message: Some("out of range".to_string()),
|
||||
})
|
||||
.map(Value::Number)
|
||||
Ok(Value::Number(self as _))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -689,33 +902,19 @@ macro_rules! lua_convert_float {
|
||||
fn from_lua(value: Value, lua: &Lua) -> Result<Self> {
|
||||
let ty = value.type_name();
|
||||
lua.coerce_number(value)?
|
||||
.map(|n| n as $x)
|
||||
.ok_or_else(|| Error::FromLuaConversionError {
|
||||
from: ty,
|
||||
to: stringify!($x),
|
||||
to: stringify!($x).to_string(),
|
||||
message: Some("expected number or string coercible to number".to_string()),
|
||||
})
|
||||
.and_then(|n| {
|
||||
cast(n).ok_or_else(|| Error::FromLuaConversionError {
|
||||
from: ty,
|
||||
to: stringify!($x),
|
||||
message: Some("number out of range".to_string()),
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
unsafe fn from_stack(idx: c_int, lua: &RawLua) -> Result<Self> {
|
||||
let state = lua.state();
|
||||
let type_id = ffi::lua_type(state, idx);
|
||||
if type_id == ffi::LUA_TNUMBER {
|
||||
let mut ok = 0;
|
||||
let i = ffi::lua_tonumberx(state, idx, &mut ok);
|
||||
if ok != 0 {
|
||||
return cast(i).ok_or_else(|| Error::FromLuaConversionError {
|
||||
from: "number",
|
||||
to: stringify!($x),
|
||||
message: Some("out of range".to_owned()),
|
||||
});
|
||||
}
|
||||
return Ok(ffi::lua_tonumber(state, idx) as _);
|
||||
}
|
||||
// Fallback to default
|
||||
Self::from_lua(lua.stack_value(idx, Some(type_id)), lua.lua())
|
||||
@@ -756,7 +955,7 @@ where
|
||||
match value {
|
||||
#[cfg(feature = "luau")]
|
||||
#[rustfmt::skip]
|
||||
Value::Vector(v) if N == crate::types::Vector::SIZE => unsafe {
|
||||
Value::Vector(v) if N == crate::Vector::SIZE => unsafe {
|
||||
use std::{mem, ptr};
|
||||
let mut arr: [mem::MaybeUninit<T>; N] = mem::MaybeUninit::uninit().assume_init();
|
||||
ptr::write(arr[0].as_mut_ptr() , T::from_lua(Value::Number(v.x() as _), _lua)?);
|
||||
@@ -771,13 +970,13 @@ where
|
||||
vec.try_into()
|
||||
.map_err(|vec: Vec<T>| Error::FromLuaConversionError {
|
||||
from: "table",
|
||||
to: "Array",
|
||||
message: Some(format!("expected table of length {}, got {}", N, vec.len())),
|
||||
to: Self::type_name(),
|
||||
message: Some(format!("expected table of length {N}, got {}", vec.len())),
|
||||
})
|
||||
}
|
||||
_ => Err(Error::FromLuaConversionError {
|
||||
from: value.type_name(),
|
||||
to: "Array",
|
||||
to: Self::type_name(),
|
||||
message: Some("expected table".to_string()),
|
||||
}),
|
||||
}
|
||||
@@ -812,7 +1011,7 @@ impl<T: FromLua> FromLua for Vec<T> {
|
||||
Value::Table(table) => table.sequence_values().collect(),
|
||||
_ => Err(Error::FromLuaConversionError {
|
||||
from: value.type_name(),
|
||||
to: "Vec",
|
||||
to: Self::type_name(),
|
||||
message: Some("expected table".to_string()),
|
||||
}),
|
||||
}
|
||||
@@ -834,7 +1033,7 @@ impl<K: Eq + Hash + FromLua, V: FromLua, S: BuildHasher + Default> FromLua for H
|
||||
} else {
|
||||
Err(Error::FromLuaConversionError {
|
||||
from: value.type_name(),
|
||||
to: "HashMap",
|
||||
to: Self::type_name(),
|
||||
message: Some("expected table".to_string()),
|
||||
})
|
||||
}
|
||||
@@ -856,7 +1055,7 @@ impl<K: Ord + FromLua, V: FromLua> FromLua for BTreeMap<K, V> {
|
||||
} else {
|
||||
Err(Error::FromLuaConversionError {
|
||||
from: value.type_name(),
|
||||
to: "BTreeMap",
|
||||
to: Self::type_name(),
|
||||
message: Some("expected table".to_string()),
|
||||
})
|
||||
}
|
||||
@@ -880,7 +1079,7 @@ impl<T: Eq + Hash + FromLua, S: BuildHasher + Default> FromLua for HashSet<T, S>
|
||||
Value::Table(table) => table.pairs::<T, Value>().map(|res| res.map(|(k, _)| k)).collect(),
|
||||
_ => Err(Error::FromLuaConversionError {
|
||||
from: value.type_name(),
|
||||
to: "HashSet",
|
||||
to: Self::type_name(),
|
||||
message: Some("expected table".to_string()),
|
||||
}),
|
||||
}
|
||||
@@ -904,7 +1103,7 @@ impl<T: Ord + FromLua> FromLua for BTreeSet<T> {
|
||||
Value::Table(table) => table.pairs::<T, Value>().map(|res| res.map(|(k, _)| k)).collect(),
|
||||
_ => Err(Error::FromLuaConversionError {
|
||||
from: value.type_name(),
|
||||
to: "BTreeSet",
|
||||
to: Self::type_name(),
|
||||
message: Some("expected table".to_string()),
|
||||
}),
|
||||
}
|
||||
@@ -947,3 +1146,59 @@ impl<T: FromLua> FromLua for Option<T> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<L: IntoLua, R: IntoLua> IntoLua for Either<L, R> {
|
||||
#[inline]
|
||||
fn into_lua(self, lua: &Lua) -> Result<Value> {
|
||||
match self {
|
||||
Either::Left(l) => l.into_lua(lua),
|
||||
Either::Right(r) => r.into_lua(lua),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn push_into_stack(self, lua: &RawLua) -> Result<()> {
|
||||
match self {
|
||||
Either::Left(l) => l.push_into_stack(lua),
|
||||
Either::Right(r) => r.push_into_stack(lua),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<L: FromLua, R: FromLua> FromLua for Either<L, R> {
|
||||
#[inline]
|
||||
fn from_lua(value: Value, lua: &Lua) -> Result<Self> {
|
||||
let value_type_name = value.type_name();
|
||||
// Try the left type first
|
||||
match L::from_lua(value.clone(), lua) {
|
||||
Ok(l) => Ok(Either::Left(l)),
|
||||
// Try the right type
|
||||
Err(_) => match R::from_lua(value, lua).map(Either::Right) {
|
||||
Ok(r) => Ok(r),
|
||||
Err(_) => Err(Error::FromLuaConversionError {
|
||||
from: value_type_name,
|
||||
to: Self::type_name(),
|
||||
message: None,
|
||||
}),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn from_stack(idx: c_int, lua: &RawLua) -> Result<Self> {
|
||||
match L::from_stack(idx, lua) {
|
||||
Ok(l) => Ok(Either::Left(l)),
|
||||
Err(_) => match R::from_stack(idx, lua).map(Either::Right) {
|
||||
Ok(r) => Ok(r),
|
||||
Err(_) => {
|
||||
let value_type_name = CStr::from_ptr(ffi::luaL_typename(lua.state(), idx));
|
||||
Err(Error::FromLuaConversionError {
|
||||
from: value_type_name.to_str().unwrap(),
|
||||
to: Self::type_name(),
|
||||
message: None,
|
||||
})
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+101
-109
@@ -1,72 +1,39 @@
|
||||
use std::borrow::Cow;
|
||||
use std::cell::UnsafeCell;
|
||||
use std::ops::Deref;
|
||||
#[cfg(not(feature = "luau"))]
|
||||
use std::ops::{BitOr, BitOrAssign};
|
||||
use std::os::raw::c_int;
|
||||
|
||||
use ffi::lua_Debug;
|
||||
use ffi::{lua_Debug, lua_State};
|
||||
|
||||
use crate::function::Function;
|
||||
use crate::state::RawLua;
|
||||
use crate::types::ReentrantMutexGuard;
|
||||
use crate::util::{linenumber_to_usize, ptr_to_lossy_str, ptr_to_str};
|
||||
use crate::util::{assert_stack, linenumber_to_usize, ptr_to_lossy_str, ptr_to_str, StackGuard};
|
||||
|
||||
/// Contains information about currently executing Lua code.
|
||||
///
|
||||
/// The `Debug` structure is provided as a parameter to the hook function set with
|
||||
/// [`Lua::set_hook`]. You may call the methods on this structure to retrieve information about the
|
||||
/// Lua code executing at the time that the hook function was called. Further information can be
|
||||
/// found in the Lua [documentation][lua_doc].
|
||||
/// You may call the methods on this structure to retrieve information about the Lua code executing
|
||||
/// at the specific level. Further information can be found in the Lua [documentation].
|
||||
///
|
||||
/// [lua_doc]: https://www.lua.org/manual/5.4/manual.html#lua_Debug
|
||||
/// [`Lua::set_hook`]: crate::Lua::set_hook
|
||||
/// [documentation]: https://www.lua.org/manual/5.4/manual.html#lua_Debug
|
||||
pub struct Debug<'a> {
|
||||
lua: EitherLua<'a>,
|
||||
ar: ActivationRecord,
|
||||
#[cfg(feature = "luau")]
|
||||
state: *mut lua_State,
|
||||
lua: &'a RawLua,
|
||||
#[cfg_attr(not(feature = "luau"), allow(unused))]
|
||||
level: c_int,
|
||||
}
|
||||
|
||||
enum EitherLua<'a> {
|
||||
Owned(ReentrantMutexGuard<'a, RawLua>),
|
||||
#[cfg(not(feature = "luau"))]
|
||||
Borrowed(&'a RawLua),
|
||||
}
|
||||
|
||||
impl Deref for EitherLua<'_> {
|
||||
type Target = RawLua;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
match self {
|
||||
EitherLua::Owned(guard) => guard,
|
||||
#[cfg(not(feature = "luau"))]
|
||||
EitherLua::Borrowed(lua) => lua,
|
||||
}
|
||||
}
|
||||
ar: *mut lua_Debug,
|
||||
}
|
||||
|
||||
impl<'a> Debug<'a> {
|
||||
// We assume the lock is held when this function is called.
|
||||
#[cfg(not(feature = "luau"))]
|
||||
pub(crate) fn new(lua: &'a RawLua, ar: *mut lua_Debug) -> Self {
|
||||
pub(crate) fn new(lua: &'a RawLua, level: c_int, ar: *mut lua_Debug) -> Self {
|
||||
Debug {
|
||||
lua: EitherLua::Borrowed(lua),
|
||||
ar: ActivationRecord::Borrowed(ar),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn new_owned(guard: ReentrantMutexGuard<'a, RawLua>, _level: c_int, ar: lua_Debug) -> Self {
|
||||
Debug {
|
||||
lua: EitherLua::Owned(guard),
|
||||
ar: ActivationRecord::Owned(UnsafeCell::new(ar)),
|
||||
#[cfg(feature = "luau")]
|
||||
level: _level,
|
||||
state: lua.state(),
|
||||
lua,
|
||||
ar,
|
||||
level,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the specific event that triggered the hook.
|
||||
///
|
||||
/// For [Lua 5.1] `DebugEvent::TailCall` is used for return events to indicate a return
|
||||
/// For [Lua 5.1] [`DebugEvent::TailCall`] is used for return events to indicate a return
|
||||
/// from a function that did a tail call.
|
||||
///
|
||||
/// [Lua 5.1]: https://www.lua.org/manual/5.1/manual.html#pdf-LUA_HOOKTAILRET
|
||||
@@ -74,7 +41,7 @@ impl<'a> Debug<'a> {
|
||||
#[cfg_attr(docsrs, doc(cfg(not(feature = "luau"))))]
|
||||
pub fn event(&self) -> DebugEvent {
|
||||
unsafe {
|
||||
match (*self.ar.get()).event {
|
||||
match (*self.ar).event {
|
||||
ffi::LUA_HOOKCALL => DebugEvent::Call,
|
||||
ffi::LUA_HOOKRET => DebugEvent::Ret,
|
||||
ffi::LUA_HOOKTAILCALL => DebugEvent::TailCall,
|
||||
@@ -85,24 +52,48 @@ impl<'a> Debug<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Corresponds to the `n` what mask.
|
||||
pub fn names(&self) -> DebugNames {
|
||||
/// Returns the function that is running at the given level.
|
||||
///
|
||||
/// Corresponds to the `f` "what" mask.
|
||||
pub fn function(&self) -> Function {
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(self.state);
|
||||
assert_stack(self.state, 1);
|
||||
|
||||
#[cfg(not(feature = "luau"))]
|
||||
mlua_assert!(
|
||||
ffi::lua_getinfo(self.state, cstr!("f"), self.ar) != 0,
|
||||
"lua_getinfo failed with `f`"
|
||||
);
|
||||
#[cfg(feature = "luau")]
|
||||
mlua_assert!(
|
||||
ffi::lua_getinfo(self.state, self.level, cstr!("f"), self.ar) != 0,
|
||||
"lua_getinfo failed with `f`"
|
||||
);
|
||||
|
||||
ffi::lua_xmove(self.state, self.lua.ref_thread(), 1);
|
||||
Function(self.lua.pop_ref_thread())
|
||||
}
|
||||
}
|
||||
|
||||
/// Corresponds to the `n` "what" mask.
|
||||
pub fn names(&self) -> DebugNames<'_> {
|
||||
unsafe {
|
||||
#[cfg(not(feature = "luau"))]
|
||||
mlua_assert!(
|
||||
ffi::lua_getinfo(self.lua.state(), cstr!("n"), self.ar.get()) != 0,
|
||||
ffi::lua_getinfo(self.state, cstr!("n"), self.ar) != 0,
|
||||
"lua_getinfo failed with `n`"
|
||||
);
|
||||
#[cfg(feature = "luau")]
|
||||
mlua_assert!(
|
||||
ffi::lua_getinfo(self.lua.state(), self.level, cstr!("n"), self.ar.get()) != 0,
|
||||
ffi::lua_getinfo(self.state, self.level, cstr!("n"), self.ar) != 0,
|
||||
"lua_getinfo failed with `n`"
|
||||
);
|
||||
|
||||
DebugNames {
|
||||
name: ptr_to_lossy_str((*self.ar.get()).name),
|
||||
name: ptr_to_lossy_str((*self.ar).name),
|
||||
#[cfg(not(feature = "luau"))]
|
||||
name_what: match ptr_to_str((*self.ar.get()).namewhat) {
|
||||
name_what: match ptr_to_str((*self.ar).namewhat) {
|
||||
Some("") => None,
|
||||
val => val,
|
||||
},
|
||||
@@ -112,118 +103,110 @@ impl<'a> Debug<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Corresponds to the `S` what mask.
|
||||
pub fn source(&self) -> DebugSource {
|
||||
/// Corresponds to the `S` "what" mask.
|
||||
pub fn source(&self) -> DebugSource<'_> {
|
||||
unsafe {
|
||||
#[cfg(not(feature = "luau"))]
|
||||
mlua_assert!(
|
||||
ffi::lua_getinfo(self.lua.state(), cstr!("S"), self.ar.get()) != 0,
|
||||
ffi::lua_getinfo(self.state, cstr!("S"), self.ar) != 0,
|
||||
"lua_getinfo failed with `S`"
|
||||
);
|
||||
#[cfg(feature = "luau")]
|
||||
mlua_assert!(
|
||||
ffi::lua_getinfo(self.lua.state(), self.level, cstr!("s"), self.ar.get()) != 0,
|
||||
ffi::lua_getinfo(self.state, self.level, cstr!("s"), self.ar) != 0,
|
||||
"lua_getinfo failed with `s`"
|
||||
);
|
||||
|
||||
DebugSource {
|
||||
source: ptr_to_lossy_str((*self.ar.get()).source),
|
||||
source: ptr_to_lossy_str((*self.ar).source),
|
||||
#[cfg(not(feature = "luau"))]
|
||||
short_src: ptr_to_lossy_str((*self.ar.get()).short_src.as_ptr()),
|
||||
short_src: ptr_to_lossy_str((*self.ar).short_src.as_ptr()),
|
||||
#[cfg(feature = "luau")]
|
||||
short_src: ptr_to_lossy_str((*self.ar.get()).short_src),
|
||||
line_defined: linenumber_to_usize((*self.ar.get()).linedefined),
|
||||
short_src: ptr_to_lossy_str((*self.ar).short_src),
|
||||
line_defined: linenumber_to_usize((*self.ar).linedefined),
|
||||
#[cfg(not(feature = "luau"))]
|
||||
last_line_defined: linenumber_to_usize((*self.ar.get()).lastlinedefined),
|
||||
last_line_defined: linenumber_to_usize((*self.ar).lastlinedefined),
|
||||
#[cfg(feature = "luau")]
|
||||
last_line_defined: None,
|
||||
what: ptr_to_str((*self.ar.get()).what).unwrap_or("main"),
|
||||
what: ptr_to_str((*self.ar).what).unwrap_or("main"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Corresponds to the `l` what mask. Returns the current line.
|
||||
#[doc(hidden)]
|
||||
#[deprecated(note = "Use `current_line` instead")]
|
||||
pub fn curr_line(&self) -> i32 {
|
||||
self.current_line().map(|n| n as i32).unwrap_or(-1)
|
||||
}
|
||||
|
||||
/// Corresponds to the `l` "what" mask. Returns the current line.
|
||||
pub fn current_line(&self) -> Option<usize> {
|
||||
unsafe {
|
||||
#[cfg(not(feature = "luau"))]
|
||||
mlua_assert!(
|
||||
ffi::lua_getinfo(self.lua.state(), cstr!("l"), self.ar.get()) != 0,
|
||||
ffi::lua_getinfo(self.state, cstr!("l"), self.ar) != 0,
|
||||
"lua_getinfo failed with `l`"
|
||||
);
|
||||
#[cfg(feature = "luau")]
|
||||
mlua_assert!(
|
||||
ffi::lua_getinfo(self.lua.state(), self.level, cstr!("l"), self.ar.get()) != 0,
|
||||
ffi::lua_getinfo(self.state, self.level, cstr!("l"), self.ar) != 0,
|
||||
"lua_getinfo failed with `l`"
|
||||
);
|
||||
|
||||
(*self.ar.get()).currentline
|
||||
linenumber_to_usize((*self.ar).currentline)
|
||||
}
|
||||
}
|
||||
|
||||
/// Corresponds to the `t` what mask. Returns true if the hook is in a function tail call, false
|
||||
/// otherwise.
|
||||
#[cfg(not(feature = "luau"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(not(feature = "luau"))))]
|
||||
/// Corresponds to the `t` "what" mask. Returns true if the hook is in a function tail call,
|
||||
/// false otherwise.
|
||||
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
|
||||
#[cfg_attr(
|
||||
docsrs,
|
||||
doc(cfg(any(feature = "lua54", feature = "lua53", feature = "lua52")))
|
||||
)]
|
||||
pub fn is_tail_call(&self) -> bool {
|
||||
unsafe {
|
||||
mlua_assert!(
|
||||
ffi::lua_getinfo(self.lua.state(), cstr!("t"), self.ar.get()) != 0,
|
||||
ffi::lua_getinfo(self.state, cstr!("t"), self.ar) != 0,
|
||||
"lua_getinfo failed with `t`"
|
||||
);
|
||||
(*self.ar.get()).currentline != 0
|
||||
(*self.ar).istailcall != 0
|
||||
}
|
||||
}
|
||||
|
||||
/// Corresponds to the `u` what mask.
|
||||
/// Corresponds to the `u` "what" mask.
|
||||
pub fn stack(&self) -> DebugStack {
|
||||
unsafe {
|
||||
#[cfg(not(feature = "luau"))]
|
||||
mlua_assert!(
|
||||
ffi::lua_getinfo(self.lua.state(), cstr!("u"), self.ar.get()) != 0,
|
||||
ffi::lua_getinfo(self.state, cstr!("u"), self.ar) != 0,
|
||||
"lua_getinfo failed with `u`"
|
||||
);
|
||||
#[cfg(feature = "luau")]
|
||||
mlua_assert!(
|
||||
ffi::lua_getinfo(self.lua.state(), self.level, cstr!("a"), self.ar.get()) != 0,
|
||||
"lua_getinfo failed with `a`"
|
||||
ffi::lua_getinfo(self.state, self.level, cstr!("au"), self.ar) != 0,
|
||||
"lua_getinfo failed with `au`"
|
||||
);
|
||||
|
||||
#[cfg(not(feature = "luau"))]
|
||||
let stack = DebugStack {
|
||||
num_ups: (*self.ar.get()).nups as _,
|
||||
num_ups: (*self.ar).nups as _,
|
||||
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
|
||||
num_params: (*self.ar.get()).nparams as _,
|
||||
num_params: (*self.ar).nparams as _,
|
||||
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
|
||||
is_vararg: (*self.ar.get()).isvararg != 0,
|
||||
is_vararg: (*self.ar).isvararg != 0,
|
||||
};
|
||||
#[cfg(feature = "luau")]
|
||||
let stack = DebugStack {
|
||||
num_ups: (*self.ar.get()).nupvals as i32,
|
||||
num_params: (*self.ar.get()).nparams as i32,
|
||||
is_vararg: (*self.ar.get()).isvararg != 0,
|
||||
num_ups: (*self.ar).nupvals,
|
||||
num_params: (*self.ar).nparams,
|
||||
is_vararg: (*self.ar).isvararg != 0,
|
||||
};
|
||||
stack
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum ActivationRecord {
|
||||
#[cfg(not(feature = "luau"))]
|
||||
Borrowed(*mut lua_Debug),
|
||||
Owned(UnsafeCell<lua_Debug>),
|
||||
}
|
||||
|
||||
impl ActivationRecord {
|
||||
#[inline]
|
||||
fn get(&self) -> *mut lua_Debug {
|
||||
match self {
|
||||
#[cfg(not(feature = "luau"))]
|
||||
ActivationRecord::Borrowed(x) => *x,
|
||||
ActivationRecord::Owned(x) => x.get(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents a specific event that triggered the hook.
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub enum DebugEvent {
|
||||
@@ -262,12 +245,21 @@ pub struct DebugSource<'a> {
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct DebugStack {
|
||||
pub num_ups: i32,
|
||||
/// Requires `feature = "lua54/lua53/lua52/luau"`
|
||||
/// Number of upvalues.
|
||||
pub num_ups: u8,
|
||||
/// Number of parameters.
|
||||
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52", feature = "luau"))]
|
||||
pub num_params: i32,
|
||||
/// Requires `feature = "lua54/lua53/lua52/luau"`
|
||||
#[cfg_attr(
|
||||
docsrs,
|
||||
doc(cfg(any(feature = "lua54", feature = "lua53", feature = "lua52", feature = "luau")))
|
||||
)]
|
||||
pub num_params: u8,
|
||||
/// Whether the function is a vararg function.
|
||||
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52", feature = "luau"))]
|
||||
#[cfg_attr(
|
||||
docsrs,
|
||||
doc(cfg(any(feature = "lua54", feature = "lua53", feature = "lua52", feature = "luau")))
|
||||
)]
|
||||
pub is_vararg: bool,
|
||||
}
|
||||
|
||||
@@ -373,7 +365,7 @@ impl HookTriggers {
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "luau"))]
|
||||
impl BitOr for HookTriggers {
|
||||
impl std::ops::BitOr for HookTriggers {
|
||||
type Output = Self;
|
||||
|
||||
fn bitor(mut self, rhs: Self) -> Self::Output {
|
||||
@@ -388,7 +380,7 @@ impl BitOr for HookTriggers {
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "luau"))]
|
||||
impl BitOrAssign for HookTriggers {
|
||||
impl std::ops::BitOrAssign for HookTriggers {
|
||||
fn bitor_assign(&mut self, rhs: Self) {
|
||||
*self = *self | rhs;
|
||||
}
|
||||
+139
-58
@@ -9,6 +9,12 @@ use std::sync::Arc;
|
||||
|
||||
use crate::private::Sealed;
|
||||
|
||||
#[cfg(feature = "error-send")]
|
||||
type DynStdError = dyn StdError + Send + Sync;
|
||||
|
||||
#[cfg(not(feature = "error-send"))]
|
||||
type DynStdError = dyn StdError;
|
||||
|
||||
/// Error type returned by `mlua` methods.
|
||||
#[derive(Debug, Clone)]
|
||||
#[non_exhaustive]
|
||||
@@ -42,11 +48,11 @@ pub enum Error {
|
||||
GarbageCollectorError(StdString),
|
||||
/// Potentially unsafe action in safe mode.
|
||||
SafetyError(StdString),
|
||||
/// Setting memory limit is not available.
|
||||
/// Memory control is not available.
|
||||
///
|
||||
/// This error can only happen when Lua state was not created by us and does not have the
|
||||
/// custom allocator attached.
|
||||
MemoryLimitNotAvailable,
|
||||
MemoryControlNotAvailable,
|
||||
/// A mutable callback has triggered Lua code that has called the same mutable callback again.
|
||||
///
|
||||
/// This is an error because a mutable callback can only be borrowed mutably once.
|
||||
@@ -61,10 +67,12 @@ pub enum Error {
|
||||
///
|
||||
/// Due to the way `mlua` works, it should not be directly possible to run out of stack space
|
||||
/// during normal use. The only way that this error can be triggered is if a `Function` is
|
||||
/// called with a huge number of arguments, or a rust callback returns a huge number of return
|
||||
/// called with a huge number of arguments, or a Rust callback returns a huge number of return
|
||||
/// values.
|
||||
StackError,
|
||||
/// Too many arguments to `Function::bind`.
|
||||
/// Too many arguments to [`Function::bind`].
|
||||
///
|
||||
/// [`Function::bind`]: crate::Function::bind
|
||||
BindError,
|
||||
/// Bad argument received from Lua (usually when calling a function).
|
||||
///
|
||||
@@ -83,7 +91,7 @@ pub enum Error {
|
||||
/// A Rust value could not be converted to a Lua value.
|
||||
ToLuaConversionError {
|
||||
/// Name of the Rust type that could not be converted.
|
||||
from: &'static str,
|
||||
from: String,
|
||||
/// Name of the Lua type that could not be created.
|
||||
to: &'static str,
|
||||
/// A message indicating why the conversion failed in more detail.
|
||||
@@ -94,7 +102,7 @@ pub enum Error {
|
||||
/// Name of the Lua type that could not be converted.
|
||||
from: &'static str,
|
||||
/// Name of the Rust type that could not be created.
|
||||
to: &'static str,
|
||||
to: String,
|
||||
/// A string containing more detailed error information.
|
||||
message: Option<StdString>,
|
||||
},
|
||||
@@ -175,12 +183,12 @@ pub enum Error {
|
||||
/// and returned again.
|
||||
PreviouslyResumedPanic,
|
||||
/// Serialization error.
|
||||
#[cfg(feature = "serialize")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "serialize")))]
|
||||
#[cfg(feature = "serde")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "serde")))]
|
||||
SerializeError(StdString),
|
||||
/// Deserialization error.
|
||||
#[cfg(feature = "serialize")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "serialize")))]
|
||||
#[cfg(feature = "serde")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "serde")))]
|
||||
DeserializeError(StdString),
|
||||
/// A custom error.
|
||||
///
|
||||
@@ -189,7 +197,7 @@ pub enum Error {
|
||||
/// Returning `Err(ExternalError(...))` from a Rust callback will raise the error as a Lua
|
||||
/// error. The Rust code that originally invoked the Lua code then receives a `CallbackError`,
|
||||
/// from which the original error (and a stack traceback) can be recovered.
|
||||
ExternalError(Arc<dyn StdError + Send + Sync>),
|
||||
ExternalError(Arc<DynStdError>),
|
||||
/// An error with additional context.
|
||||
WithContext {
|
||||
/// A string containing additional context.
|
||||
@@ -205,21 +213,21 @@ pub type Result<T> = StdResult<T, Error>;
|
||||
#[cfg(not(tarpaulin_include))]
|
||||
impl fmt::Display for Error {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
match *self {
|
||||
Error::SyntaxError { ref message, .. } => write!(fmt, "syntax error: {message}"),
|
||||
Error::RuntimeError(ref msg) => write!(fmt, "runtime error: {msg}"),
|
||||
Error::MemoryError(ref msg) => {
|
||||
match self {
|
||||
Error::SyntaxError { message, .. } => write!(fmt, "syntax error: {message}"),
|
||||
Error::RuntimeError(msg) => write!(fmt, "runtime error: {msg}"),
|
||||
Error::MemoryError(msg) => {
|
||||
write!(fmt, "memory error: {msg}")
|
||||
}
|
||||
#[cfg(any(feature = "lua53", feature = "lua52"))]
|
||||
Error::GarbageCollectorError(ref msg) => {
|
||||
Error::GarbageCollectorError(msg) => {
|
||||
write!(fmt, "garbage collector error: {msg}")
|
||||
}
|
||||
Error::SafetyError(ref msg) => {
|
||||
Error::SafetyError(msg) => {
|
||||
write!(fmt, "safety error: {msg}")
|
||||
},
|
||||
Error::MemoryLimitNotAvailable => {
|
||||
write!(fmt, "setting memory limit is not available")
|
||||
Error::MemoryControlNotAvailable => {
|
||||
write!(fmt, "memory control is not available")
|
||||
}
|
||||
Error::RecursiveMutCallback => write!(fmt, "mutable callback called recursively"),
|
||||
Error::CallbackDestructed => write!(
|
||||
@@ -234,7 +242,7 @@ impl fmt::Display for Error {
|
||||
fmt,
|
||||
"too many arguments to Function::bind"
|
||||
),
|
||||
Error::BadArgument { ref to, pos, ref name, ref cause } => {
|
||||
Error::BadArgument { to, pos, name, cause } => {
|
||||
if let Some(name) = name {
|
||||
write!(fmt, "bad argument `{name}`")?;
|
||||
} else {
|
||||
@@ -245,18 +253,18 @@ impl fmt::Display for Error {
|
||||
}
|
||||
write!(fmt, ": {cause}")
|
||||
},
|
||||
Error::ToLuaConversionError { from, to, ref message } => {
|
||||
Error::ToLuaConversionError { from, to, message } => {
|
||||
write!(fmt, "error converting {from} to Lua {to}")?;
|
||||
match *message {
|
||||
match message {
|
||||
None => Ok(()),
|
||||
Some(ref message) => write!(fmt, " ({message})"),
|
||||
Some(message) => write!(fmt, " ({message})"),
|
||||
}
|
||||
}
|
||||
Error::FromLuaConversionError { from, to, ref message } => {
|
||||
Error::FromLuaConversionError { from, to, message } => {
|
||||
write!(fmt, "error converting Lua {from} to {to}")?;
|
||||
match *message {
|
||||
match message {
|
||||
None => Ok(()),
|
||||
Some(ref message) => write!(fmt, " ({message})"),
|
||||
Some(message) => write!(fmt, " ({message})"),
|
||||
}
|
||||
}
|
||||
Error::CoroutineUnresumable => write!(fmt, "coroutine is non-resumable"),
|
||||
@@ -264,21 +272,21 @@ impl fmt::Display for Error {
|
||||
Error::UserDataDestructed => write!(fmt, "userdata has been destructed"),
|
||||
Error::UserDataBorrowError => write!(fmt, "error borrowing userdata"),
|
||||
Error::UserDataBorrowMutError => write!(fmt, "error mutably borrowing userdata"),
|
||||
Error::MetaMethodRestricted(ref method) => write!(fmt, "metamethod {method} is restricted"),
|
||||
Error::MetaMethodTypeError { ref method, type_name, ref message } => {
|
||||
Error::MetaMethodRestricted(method) => write!(fmt, "metamethod {method} is restricted"),
|
||||
Error::MetaMethodTypeError { method, type_name, message } => {
|
||||
write!(fmt, "metamethod {method} has unsupported type {type_name}")?;
|
||||
match *message {
|
||||
match message {
|
||||
None => Ok(()),
|
||||
Some(ref message) => write!(fmt, " ({message})"),
|
||||
Some(message) => write!(fmt, " ({message})"),
|
||||
}
|
||||
}
|
||||
Error::MismatchedRegistryKey => {
|
||||
write!(fmt, "RegistryKey used from different Lua state")
|
||||
}
|
||||
Error::CallbackError { ref cause, ref traceback } => {
|
||||
Error::CallbackError { cause, traceback } => {
|
||||
// Trace errors down to the root
|
||||
let (mut cause, mut full_traceback) = (cause, None);
|
||||
while let Error::CallbackError { cause: ref cause2, traceback: ref traceback2 } = **cause {
|
||||
while let Error::CallbackError { cause: cause2, traceback: traceback2 } = &**cause {
|
||||
cause = cause2;
|
||||
full_traceback = Some(traceback2);
|
||||
}
|
||||
@@ -301,16 +309,16 @@ impl fmt::Display for Error {
|
||||
Error::PreviouslyResumedPanic => {
|
||||
write!(fmt, "previously resumed panic returned again")
|
||||
}
|
||||
#[cfg(feature = "serialize")]
|
||||
Error::SerializeError(ref err) => {
|
||||
#[cfg(feature = "serde")]
|
||||
Error::SerializeError(err) => {
|
||||
write!(fmt, "serialize error: {err}")
|
||||
},
|
||||
#[cfg(feature = "serialize")]
|
||||
Error::DeserializeError(ref err) => {
|
||||
#[cfg(feature = "serde")]
|
||||
Error::DeserializeError(err) => {
|
||||
write!(fmt, "deserialize error: {err}")
|
||||
},
|
||||
Error::ExternalError(ref err) => write!(fmt, "{err}"),
|
||||
Error::WithContext { ref context, ref cause } => {
|
||||
Error::ExternalError(err) => err.fmt(fmt),
|
||||
Error::WithContext { context, cause } => {
|
||||
writeln!(fmt, "{context}")?;
|
||||
write!(fmt, "{cause}")
|
||||
}
|
||||
@@ -320,18 +328,15 @@ impl fmt::Display for Error {
|
||||
|
||||
impl StdError for Error {
|
||||
fn source(&self) -> Option<&(dyn StdError + 'static)> {
|
||||
match *self {
|
||||
match self {
|
||||
// An error type with a source error should either return that error via source or
|
||||
// include that source's error message in its own Display output, but never both.
|
||||
// https://blog.rust-lang.org/inside-rust/2021/07/01/What-the-error-handling-project-group-is-working-towards.html
|
||||
// Given that we include source to fmt::Display implementation for `CallbackError`, this call
|
||||
// returns nothing.
|
||||
Error::CallbackError { .. } => None,
|
||||
Error::ExternalError(ref err) => err.source(),
|
||||
Error::WithContext { ref cause, .. } => match cause.as_ref() {
|
||||
Error::ExternalError(err) => err.source(),
|
||||
_ => None,
|
||||
},
|
||||
Error::ExternalError(err) => err.source(),
|
||||
Error::WithContext { cause, .. } => Self::source(cause),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
@@ -346,7 +351,7 @@ impl Error {
|
||||
|
||||
/// Wraps an external error object.
|
||||
#[inline]
|
||||
pub fn external<T: Into<Box<dyn StdError + Send + Sync>>>(err: T) -> Self {
|
||||
pub fn external<T: Into<Box<DynStdError>>>(err: T) -> Self {
|
||||
Error::ExternalError(err.into().into())
|
||||
}
|
||||
|
||||
@@ -357,10 +362,25 @@ impl Error {
|
||||
{
|
||||
match self {
|
||||
Error::ExternalError(err) => err.downcast_ref(),
|
||||
Error::WithContext { cause, .. } => match cause.as_ref() {
|
||||
Error::ExternalError(err) => err.downcast_ref(),
|
||||
_ => None,
|
||||
},
|
||||
Error::WithContext { cause, .. } => Self::downcast_ref(cause),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// An iterator over the chain of nested errors wrapped by this Error.
|
||||
pub fn chain(&self) -> impl Iterator<Item = &(dyn StdError + 'static)> {
|
||||
Chain {
|
||||
root: self,
|
||||
current: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the parent of this error.
|
||||
#[doc(hidden)]
|
||||
pub fn parent(&self) -> Option<&Error> {
|
||||
match self {
|
||||
Error::CallbackError { cause, .. } => Some(cause.as_ref()),
|
||||
Error::WithContext { cause, .. } => Some(cause.as_ref()),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
@@ -374,15 +394,15 @@ impl Error {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn from_lua_conversion<'a>(
|
||||
pub(crate) fn from_lua_conversion(
|
||||
from: &'static str,
|
||||
to: &'static str,
|
||||
message: impl Into<Option<&'a str>>,
|
||||
to: impl ToString,
|
||||
message: impl Into<Option<String>>,
|
||||
) -> Self {
|
||||
Error::FromLuaConversionError {
|
||||
from,
|
||||
to,
|
||||
message: message.into().map(|s| s.into()),
|
||||
to: to.to_string(),
|
||||
message: message.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -392,7 +412,7 @@ pub trait ExternalError {
|
||||
fn into_lua_err(self) -> Error;
|
||||
}
|
||||
|
||||
impl<E: Into<Box<dyn StdError + Send + Sync>>> ExternalError for E {
|
||||
impl<E: Into<Box<DynStdError>>> ExternalError for E {
|
||||
fn into_lua_err(self) -> Error {
|
||||
Error::external(self)
|
||||
}
|
||||
@@ -446,7 +466,7 @@ impl ErrorContext for Error {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> ErrorContext for StdResult<T, Error> {
|
||||
impl<T> ErrorContext for Result<T> {
|
||||
fn context<C: fmt::Display>(self, context: C) -> Self {
|
||||
self.map_err(|err| err.context(context))
|
||||
}
|
||||
@@ -474,16 +494,77 @@ impl From<Utf8Error> for Error {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
#[cfg(feature = "serde")]
|
||||
impl serde::ser::Error for Error {
|
||||
fn custom<T: fmt::Display>(msg: T) -> Self {
|
||||
Self::SerializeError(msg.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
#[cfg(feature = "serde")]
|
||||
impl serde::de::Error for Error {
|
||||
fn custom<T: fmt::Display>(msg: T) -> Self {
|
||||
Self::DeserializeError(msg.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "anyhow")]
|
||||
impl From<anyhow::Error> for Error {
|
||||
fn from(err: anyhow::Error) -> Self {
|
||||
match err.downcast::<Self>() {
|
||||
Ok(err) => err,
|
||||
Err(err) => Error::external(err),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct Chain<'a> {
|
||||
root: &'a Error,
|
||||
current: Option<&'a (dyn StdError + 'static)>,
|
||||
}
|
||||
|
||||
impl<'a> Iterator for Chain<'a> {
|
||||
type Item = &'a (dyn StdError + 'static);
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
loop {
|
||||
let error: Option<&dyn StdError> = match self.current {
|
||||
None => {
|
||||
self.current = Some(self.root);
|
||||
self.current
|
||||
}
|
||||
Some(current) => match current.downcast_ref::<Error>()? {
|
||||
Error::BadArgument { cause, .. }
|
||||
| Error::CallbackError { cause, .. }
|
||||
| Error::WithContext { cause, .. } => {
|
||||
self.current = Some(&**cause);
|
||||
self.current
|
||||
}
|
||||
Error::ExternalError(err) => {
|
||||
self.current = Some(&**err);
|
||||
self.current
|
||||
}
|
||||
_ => None,
|
||||
},
|
||||
};
|
||||
|
||||
// Skip `ExternalError` as it only wraps the underlying error
|
||||
// without meaningful context
|
||||
if let Some(Error::ExternalError(_)) = error?.downcast_ref::<Error>() {
|
||||
continue;
|
||||
}
|
||||
|
||||
return self.current;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod assertions {
|
||||
use super::*;
|
||||
|
||||
#[cfg(not(feature = "error-send"))]
|
||||
static_assertions::assert_not_impl_any!(Error: Send, Sync);
|
||||
#[cfg(feature = "send")]
|
||||
static_assertions::assert_impl_all!(Error: Send, Sync);
|
||||
}
|
||||
|
||||
+155
-59
@@ -5,20 +5,25 @@ use std::{mem, ptr, slice};
|
||||
use crate::error::{Error, Result};
|
||||
use crate::state::Lua;
|
||||
use crate::table::Table;
|
||||
use crate::types::{Callback, MaybeSend, ValueRef};
|
||||
use crate::traits::{FromLuaMulti, IntoLua, IntoLuaMulti, LuaNativeFn, LuaNativeFnMut};
|
||||
use crate::types::{Callback, LuaType, MaybeSend, ValueRef};
|
||||
use crate::util::{
|
||||
assert_stack, check_stack, linenumber_to_usize, pop_error, ptr_to_lossy_str, ptr_to_str, StackGuard,
|
||||
};
|
||||
use crate::value::{FromLuaMulti, IntoLua, IntoLuaMulti, Value};
|
||||
use crate::value::Value;
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
use {
|
||||
crate::thread::AsyncThread,
|
||||
crate::traits::LuaNativeAsyncFn,
|
||||
crate::types::AsyncCallback,
|
||||
std::future::{self, Future},
|
||||
std::pin::Pin,
|
||||
std::task::{Context, Poll},
|
||||
};
|
||||
|
||||
/// Handle to an internal Lua function.
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct Function(pub(crate) ValueRef);
|
||||
|
||||
/// Contains information about a function.
|
||||
@@ -126,9 +131,8 @@ impl Function {
|
||||
/// 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`].
|
||||
///
|
||||
/// Requires `feature = "async"`
|
||||
/// Internally it wraps the function to an [`AsyncThread`]. The returned type implements
|
||||
/// `Future<Output = Result<R>>` and can be awaited.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
@@ -144,7 +148,7 @@ impl Function {
|
||||
/// Ok(())
|
||||
/// })?;
|
||||
///
|
||||
/// sleep.call_async(10).await?;
|
||||
/// sleep.call_async::<()>(10).await?;
|
||||
///
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
@@ -153,19 +157,18 @@ impl Function {
|
||||
/// [`AsyncThread`]: crate::AsyncThread
|
||||
#[cfg(feature = "async")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
|
||||
pub fn call_async<R>(&self, args: impl IntoLuaMulti) -> impl Future<Output = Result<R>>
|
||||
pub fn call_async<R>(&self, args: impl IntoLuaMulti) -> AsyncCallFuture<R>
|
||||
where
|
||||
R: FromLuaMulti,
|
||||
{
|
||||
let lua = self.0.lua.lock();
|
||||
let thread_res = unsafe {
|
||||
lua.create_recycled_thread(self).map(|th| {
|
||||
let mut th = th.into_async(args);
|
||||
AsyncCallFuture(unsafe {
|
||||
lua.create_recycled_thread(self).and_then(|th| {
|
||||
let mut th = th.into_async(args)?;
|
||||
th.set_recyclable(true);
|
||||
th
|
||||
Ok(th)
|
||||
})
|
||||
};
|
||||
async move { thread_res?.await }
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns a function that, when called, calls `self`, passing `args` as the first set of
|
||||
@@ -250,7 +253,7 @@ impl Function {
|
||||
"#,
|
||||
)
|
||||
.try_cache()
|
||||
.set_name("__mlua_bind")
|
||||
.set_name("=__mlua_bind")
|
||||
.call((self, args_wrapper))
|
||||
}
|
||||
|
||||
@@ -278,7 +281,7 @@ impl Function {
|
||||
// Traverse upvalues until we find the _ENV one
|
||||
match ffi::lua_getupvalue(state, -1, i) {
|
||||
s if s.is_null() => break,
|
||||
s if std::ffi::CStr::from_ptr(s as _).to_bytes() == b"_ENV" => break,
|
||||
s if std::ffi::CStr::from_ptr(s as _) == c"_ENV" => break,
|
||||
_ => ffi::lua_pop(state, 1),
|
||||
}
|
||||
}
|
||||
@@ -317,7 +320,7 @@ impl Function {
|
||||
for i in 1..=255 {
|
||||
match ffi::lua_getupvalue(state, -1, i) {
|
||||
s if s.is_null() => return Ok(false),
|
||||
s if std::ffi::CStr::from_ptr(s as _).to_bytes() == b"_ENV" => {
|
||||
s if std::ffi::CStr::from_ptr(s as _) == c"_ENV" => {
|
||||
ffi::lua_pop(state, 1);
|
||||
// Create an anonymous function with the new environment
|
||||
let f_with_env = lua
|
||||
@@ -387,9 +390,9 @@ impl Function {
|
||||
/// If `strip` is true, the binary representation may not include all debug information
|
||||
/// about the function, to save space.
|
||||
///
|
||||
/// For Luau a [Compiler] can be used to compile Lua chunks to bytecode.
|
||||
/// For Luau a [`Compiler`] can be used to compile Lua chunks to bytecode.
|
||||
///
|
||||
/// [Compiler]: crate::chunk::Compiler
|
||||
/// [`Compiler`]: crate::chunk::Compiler
|
||||
#[cfg(not(feature = "luau"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(not(feature = "luau"))))]
|
||||
pub fn dump(&self, strip: bool) -> Vec<u8> {
|
||||
@@ -428,12 +431,10 @@ impl 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", doc))]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
|
||||
pub fn coverage<F>(&self, mut func: F)
|
||||
pub fn coverage<F>(&self, func: F)
|
||||
where
|
||||
F: FnMut(CoverageInfo),
|
||||
{
|
||||
@@ -453,13 +454,16 @@ impl Function {
|
||||
} 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 rust_callback = &*(data as *const RefCell<F>);
|
||||
if let Ok(mut rust_callback) = rust_callback.try_borrow_mut() {
|
||||
// Call the Rust callback with CoverageInfo
|
||||
rust_callback(CoverageInfo {
|
||||
function,
|
||||
line_defined,
|
||||
depth,
|
||||
hits: slice::from_raw_parts(hits, size).to_vec(),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
let lua = self.0.lua.lock();
|
||||
@@ -469,7 +473,8 @@ impl Function {
|
||||
assert_stack(state, 1);
|
||||
|
||||
lua.push_ref(&self.0);
|
||||
let func_ptr = &mut func as *mut F as *mut c_void;
|
||||
let func = RefCell::new(func);
|
||||
let func_ptr = &func as *const RefCell<F> as *mut c_void;
|
||||
ffi::lua_getcoverage(state, -1, func_ptr, callback::<F>);
|
||||
}
|
||||
}
|
||||
@@ -488,65 +493,99 @@ impl Function {
|
||||
///
|
||||
/// Copies the function prototype and all its upvalues to the
|
||||
/// newly created function.
|
||||
///
|
||||
/// This function returns shallow clone (same handle) for Rust/C functions.
|
||||
/// Requires `feature = "luau"`
|
||||
#[cfg(feature = "luau")]
|
||||
#[cfg(any(feature = "luau", doc))]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
|
||||
pub fn deep_clone(&self) -> Self {
|
||||
pub fn deep_clone(&self) -> Result<Self> {
|
||||
let lua = self.0.lua.lock();
|
||||
let ref_thread = lua.ref_thread();
|
||||
let state = lua.state();
|
||||
unsafe {
|
||||
if ffi::lua_iscfunction(ref_thread, self.0.index) != 0 {
|
||||
return self.clone();
|
||||
let _sg = StackGuard::new(state);
|
||||
check_stack(state, 2)?;
|
||||
|
||||
lua.push_ref(&self.0);
|
||||
if ffi::lua_iscfunction(state, -1) != 0 {
|
||||
return Ok(self.clone());
|
||||
}
|
||||
|
||||
ffi::lua_clonefunction(ref_thread, self.0.index);
|
||||
Function(lua.pop_ref_thread())
|
||||
if lua.unlikely_memory_error() {
|
||||
ffi::lua_clonefunction(state, -1);
|
||||
} else {
|
||||
protect_lua!(state, 1, 1, fn(state) ffi::lua_clonefunction(state, -1))?;
|
||||
}
|
||||
Ok(Function(lua.pop_ref()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for Function {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.0 == other.0
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct WrappedFunction(pub(crate) Callback);
|
||||
struct WrappedFunction(pub(crate) Callback);
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
pub(crate) struct WrappedAsyncFunction(pub(crate) AsyncCallback);
|
||||
struct WrappedAsyncFunction(pub(crate) AsyncCallback);
|
||||
|
||||
impl Function {
|
||||
/// Wraps a Rust function or closure, returning an opaque type that implements [`IntoLua`]
|
||||
/// trait.
|
||||
#[inline]
|
||||
pub fn wrap<A, R, F>(func: F) -> impl IntoLua
|
||||
pub fn wrap<F, A, R>(func: F) -> impl IntoLua
|
||||
where
|
||||
F: LuaNativeFn<A, Output = Result<R>> + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
R: IntoLuaMulti,
|
||||
F: Fn(&Lua, A) -> Result<R> + MaybeSend + 'static,
|
||||
{
|
||||
WrappedFunction(Box::new(move |lua, nargs| unsafe {
|
||||
let args = A::from_stack_args(nargs, 1, None, lua)?;
|
||||
func(lua.lua(), args)?.push_into_stack_multi(lua)
|
||||
func.call(args)?.push_into_stack_multi(lua)
|
||||
}))
|
||||
}
|
||||
|
||||
/// Wraps a Rust mutable closure, returning an opaque type that implements [`IntoLua`] trait.
|
||||
#[inline]
|
||||
pub fn wrap_mut<A, R, F>(func: F) -> impl IntoLua
|
||||
pub fn wrap_mut<F, A, R>(func: F) -> impl IntoLua
|
||||
where
|
||||
F: LuaNativeFnMut<A, Output = Result<R>> + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
R: IntoLuaMulti,
|
||||
F: FnMut(&Lua, A) -> Result<R> + MaybeSend + 'static,
|
||||
{
|
||||
let func = RefCell::new(func);
|
||||
WrappedFunction(Box::new(move |lua, nargs| unsafe {
|
||||
let mut func = func.try_borrow_mut().map_err(|_| Error::RecursiveMutCallback)?;
|
||||
let args = A::from_stack_args(nargs, 1, None, lua)?;
|
||||
func(lua.lua(), args)?.push_into_stack_multi(lua)
|
||||
func.call(args)?.push_into_stack_multi(lua)
|
||||
}))
|
||||
}
|
||||
|
||||
/// Wraps a Rust function or closure, returning an opaque type that implements [`IntoLua`]
|
||||
/// trait.
|
||||
///
|
||||
/// This function is similar to [`Function::wrap`] but any returned `Result` will be converted
|
||||
/// to a `ok, err` tuple without throwing an exception.
|
||||
#[inline]
|
||||
pub fn wrap_raw<F, A>(func: F) -> impl IntoLua
|
||||
where
|
||||
F: LuaNativeFn<A> + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
{
|
||||
WrappedFunction(Box::new(move |lua, nargs| unsafe {
|
||||
let args = A::from_stack_args(nargs, 1, None, lua)?;
|
||||
func.call(args).push_into_stack_multi(lua)
|
||||
}))
|
||||
}
|
||||
|
||||
/// Wraps a Rust mutable closure, returning an opaque type that implements [`IntoLua`] trait.
|
||||
///
|
||||
/// This function is similar to [`Function::wrap_mut`] but any returned `Result` will be
|
||||
/// converted to a `ok, err` tuple without throwing an exception.
|
||||
#[inline]
|
||||
pub fn wrap_raw_mut<F, A>(func: F) -> impl IntoLua
|
||||
where
|
||||
F: LuaNativeFnMut<A> + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
{
|
||||
let func = RefCell::new(func);
|
||||
WrappedFunction(Box::new(move |lua, nargs| unsafe {
|
||||
let mut func = func.try_borrow_mut().map_err(|_| Error::RecursiveMutCallback)?;
|
||||
let args = A::from_stack_args(nargs, 1, None, lua)?;
|
||||
func.call(args).push_into_stack_multi(lua)
|
||||
}))
|
||||
}
|
||||
|
||||
@@ -554,23 +593,45 @@ impl Function {
|
||||
/// trait.
|
||||
#[cfg(feature = "async")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
|
||||
pub fn wrap_async<A, R, F, FR>(func: F) -> impl IntoLua
|
||||
pub fn wrap_async<F, A, R>(func: F) -> impl IntoLua
|
||||
where
|
||||
F: LuaNativeAsyncFn<A, Output = Result<R>> + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
R: IntoLuaMulti,
|
||||
F: Fn(Lua, A) -> FR + MaybeSend + 'static,
|
||||
FR: Future<Output = Result<R>> + MaybeSend + 'static,
|
||||
{
|
||||
WrappedAsyncFunction(Box::new(move |rawlua, nargs| unsafe {
|
||||
let args = match A::from_stack_args(nargs, 1, None, rawlua) {
|
||||
Ok(args) => args,
|
||||
Err(e) => return Box::pin(future::ready(Err(e))),
|
||||
};
|
||||
let lua = rawlua.lua().clone();
|
||||
let fut = func(lua.clone(), args);
|
||||
let lua = rawlua.lua();
|
||||
let fut = func.call(args);
|
||||
Box::pin(async move { fut.await?.push_into_stack_multi(lua.raw_lua()) })
|
||||
}))
|
||||
}
|
||||
|
||||
/// Wraps a Rust async function or closure, returning an opaque type that implements [`IntoLua`]
|
||||
/// trait.
|
||||
///
|
||||
/// This function is similar to [`Function::wrap_async`] but any returned `Result` will be
|
||||
/// converted to a `ok, err` tuple without throwing an exception.
|
||||
#[cfg(feature = "async")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
|
||||
pub fn wrap_raw_async<F, A>(func: F) -> impl IntoLua
|
||||
where
|
||||
F: LuaNativeAsyncFn<A> + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
{
|
||||
WrappedAsyncFunction(Box::new(move |rawlua, nargs| unsafe {
|
||||
let args = match A::from_stack_args(nargs, 1, None, rawlua) {
|
||||
Ok(args) => args,
|
||||
Err(e) => return Box::pin(future::ready(Err(e))),
|
||||
};
|
||||
let lua = rawlua.lua();
|
||||
let fut = func.call(args);
|
||||
Box::pin(async move { fut.await.push_into_stack_multi(lua.raw_lua()) })
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoLua for WrappedFunction {
|
||||
@@ -588,6 +649,38 @@ impl IntoLua for WrappedAsyncFunction {
|
||||
}
|
||||
}
|
||||
|
||||
impl LuaType for Function {
|
||||
const TYPE_ID: c_int = ffi::LUA_TFUNCTION;
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
#[must_use = "futures do nothing unless you `.await` or poll them"]
|
||||
pub struct AsyncCallFuture<R: FromLuaMulti>(Result<AsyncThread<R>>);
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
impl<R: FromLuaMulti> AsyncCallFuture<R> {
|
||||
pub(crate) fn error(err: Error) -> Self {
|
||||
AsyncCallFuture(Err(err))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
impl<R: FromLuaMulti> Future for AsyncCallFuture<R> {
|
||||
type Output = Result<R>;
|
||||
|
||||
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
// Safety: We're not moving any pinned data
|
||||
let this = unsafe { self.get_unchecked_mut() };
|
||||
match &mut this.0 {
|
||||
Ok(thread) => {
|
||||
let pinned_thread = unsafe { Pin::new_unchecked(thread) };
|
||||
pinned_thread.poll(cx)
|
||||
}
|
||||
Err(err) => Poll::Ready(Err(err.clone())),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod assertions {
|
||||
use super::*;
|
||||
@@ -596,4 +689,7 @@ mod assertions {
|
||||
static_assertions::assert_not_impl_any!(Function: Send);
|
||||
#[cfg(feature = "send")]
|
||||
static_assertions::assert_impl_all!(Function: Send, Sync);
|
||||
|
||||
#[cfg(all(feature = "async", feature = "send"))]
|
||||
static_assertions::assert_impl_all!(AsyncCallFuture<()>: Send);
|
||||
}
|
||||
|
||||
+44
-45
@@ -32,41 +32,32 @@
|
||||
//! [`serde::Serialize`] or [`serde::Deserialize`] can be converted.
|
||||
//! For convenience, additional functionality to handle `NULL` values and arrays is provided.
|
||||
//!
|
||||
//! The [`Value`] enum implements [`serde::Serialize`] trait to support serializing Lua values
|
||||
//! (including [`UserData`]) into Rust values.
|
||||
//! The [`Value`] enum and other types implement [`serde::Serialize`] trait to support serializing
|
||||
//! Lua values into Rust values.
|
||||
//!
|
||||
//! Requires `feature = "serialize"`.
|
||||
//! Requires `feature = "serde"`.
|
||||
//!
|
||||
//! # Async/await support
|
||||
//!
|
||||
//! The [`create_async_function`] allows creating non-blocking functions that returns [`Future`].
|
||||
//! Lua code with async capabilities can be executed by [`call_async`] family of functions or
|
||||
//! polling [`AsyncThread`] using any runtime (eg. Tokio).
|
||||
//! The [`Lua::create_async_function`] allows creating non-blocking functions that returns
|
||||
//! [`Future`]. Lua code with async capabilities can be executed by [`Function::call_async`] family
|
||||
//! of functions or polling [`AsyncThread`] using any runtime (eg. Tokio).
|
||||
//!
|
||||
//! Requires `feature = "async"`.
|
||||
//!
|
||||
//! # `Send` requirement
|
||||
//! # `Send` and `Sync` support
|
||||
//!
|
||||
//! By default `mlua` is `!Send`. This can be changed by enabling `feature = "send"` that adds
|
||||
//! `Send` requirement to [`Function`]s and [`UserData`].
|
||||
//! `Send` requirement to Rust functions and [`UserData`] types.
|
||||
//!
|
||||
//! In this case [`Lua`] object and their types can be send or used from other threads. Internally
|
||||
//! access to Lua VM is synchronized using a reentrant mutex that can be locked many times within
|
||||
//! the same thread.
|
||||
//!
|
||||
//! [Lua programming language]: https://www.lua.org/
|
||||
//! [`Lua`]: crate::Lua
|
||||
//! [executing]: crate::Chunk::exec
|
||||
//! [evaluating]: crate::Chunk::eval
|
||||
//! [globals]: crate::Lua::globals
|
||||
//! [`IntoLua`]: crate::IntoLua
|
||||
//! [`FromLua`]: crate::FromLua
|
||||
//! [`IntoLuaMulti`]: crate::IntoLuaMulti
|
||||
//! [`FromLuaMulti`]: crate::FromLuaMulti
|
||||
//! [`Function`]: crate::Function
|
||||
//! [`UserData`]: crate::UserData
|
||||
//! [`UserDataFields`]: crate::UserDataFields
|
||||
//! [`UserDataMethods`]: crate::UserDataMethods
|
||||
//! [`LuaSerdeExt`]: crate::LuaSerdeExt
|
||||
//! [`Value`]: crate::Value
|
||||
//! [`create_async_function`]: crate::Lua::create_async_function
|
||||
//! [`call_async`]: crate::Function::call_async
|
||||
//! [`AsyncThread`]: crate::AsyncThread
|
||||
//! [`Future`]: std::future::Future
|
||||
//! [`serde::Serialize`]: https://docs.serde.rs/serde/ser/trait.Serialize.html
|
||||
//! [`serde::Deserialize`]: https://docs.serde.rs/serde/de/trait.Deserialize.html
|
||||
@@ -74,20 +65,24 @@
|
||||
// Deny warnings inside doc tests / examples. When this isn't present, rustdoc doesn't show *any*
|
||||
// warnings at all.
|
||||
#![cfg_attr(docsrs, feature(doc_cfg))]
|
||||
#![cfg_attr(not(send), allow(clippy::arc_with_non_send_sync))]
|
||||
#![allow(clippy::ptr_eq)]
|
||||
#![allow(unsafe_op_in_unsafe_fn)]
|
||||
|
||||
#[macro_use]
|
||||
mod macros;
|
||||
|
||||
mod buffer;
|
||||
mod chunk;
|
||||
mod conversion;
|
||||
mod debug;
|
||||
mod error;
|
||||
mod function;
|
||||
mod hook;
|
||||
#[cfg(feature = "luau")]
|
||||
#[cfg(any(feature = "luau", doc))]
|
||||
mod luau;
|
||||
mod memory;
|
||||
mod multi;
|
||||
// mod scope;
|
||||
mod scope;
|
||||
mod state;
|
||||
mod stdlib;
|
||||
mod string;
|
||||
@@ -98,6 +93,7 @@ mod types;
|
||||
mod userdata;
|
||||
mod util;
|
||||
mod value;
|
||||
mod vector;
|
||||
|
||||
pub mod prelude;
|
||||
|
||||
@@ -105,44 +101,51 @@ pub use bstr::BString;
|
||||
pub use ffi::{self, lua_CFunction, lua_State};
|
||||
|
||||
pub use crate::chunk::{AsChunk, Chunk, ChunkMode};
|
||||
pub use crate::debug::{Debug, DebugEvent, DebugNames, DebugSource, DebugStack};
|
||||
pub use crate::error::{Error, ErrorContext, ExternalError, ExternalResult, Result};
|
||||
pub use crate::function::{Function, FunctionInfo};
|
||||
pub use crate::hook::{Debug, DebugEvent, DebugNames, DebugSource, DebugStack};
|
||||
pub use crate::multi::Variadic;
|
||||
pub use crate::state::{GCMode, Lua, LuaOptions};
|
||||
// pub use crate::scope::Scope;
|
||||
pub use crate::multi::{MultiValue, Variadic};
|
||||
pub use crate::scope::Scope;
|
||||
pub use crate::state::{GCMode, Lua, LuaOptions, WeakLua};
|
||||
pub use crate::stdlib::StdLib;
|
||||
pub use crate::string::{BorrowedBytes, BorrowedStr, String};
|
||||
pub use crate::table::{Table, TablePairs, TableSequence};
|
||||
pub use crate::thread::{Thread, ThreadStatus};
|
||||
pub use crate::traits::ObjectLike;
|
||||
pub use crate::types::{AppDataRef, AppDataRefMut, Integer, LightUserData, MaybeSend, Number, RegistryKey};
|
||||
pub use crate::traits::{
|
||||
FromLua, FromLuaMulti, IntoLua, IntoLuaMulti, LuaNativeFn, LuaNativeFnMut, ObjectLike,
|
||||
};
|
||||
pub use crate::types::{
|
||||
AppDataRef, AppDataRefMut, Either, Integer, LightUserData, MaybeSend, Number, RegistryKey, VmState,
|
||||
};
|
||||
pub use crate::userdata::{
|
||||
AnyUserData, MetaMethod, UserData, UserDataFields, UserDataMetatable, UserDataMethods, UserDataRef,
|
||||
UserDataRefMut, UserDataRegistry,
|
||||
};
|
||||
pub use crate::value::{FromLua, FromLuaMulti, IntoLua, IntoLuaMulti, MultiValue, Nil, Value};
|
||||
pub use crate::value::{Nil, Value};
|
||||
|
||||
#[cfg(not(feature = "luau"))]
|
||||
pub use crate::hook::HookTriggers;
|
||||
pub use crate::debug::HookTriggers;
|
||||
|
||||
#[cfg(any(feature = "luau", doc))]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
|
||||
pub use crate::{
|
||||
chunk::Compiler,
|
||||
buffer::Buffer,
|
||||
chunk::{CompileConstant, Compiler},
|
||||
function::CoverageInfo,
|
||||
types::{Vector, VmState},
|
||||
luau::{NavigateError, Require, TextRequirer},
|
||||
vector::Vector,
|
||||
};
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
pub use crate::thread::AsyncThread;
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
|
||||
pub use crate::{thread::AsyncThread, traits::LuaNativeAsyncFn};
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
#[cfg(feature = "serde")]
|
||||
#[doc(inline)]
|
||||
pub use crate::serde::{de::Options as DeserializeOptions, ser::Options as SerializeOptions, LuaSerdeExt};
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "serialize")))]
|
||||
#[cfg(feature = "serde")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "serde")))]
|
||||
pub mod serde;
|
||||
|
||||
#[cfg(feature = "mlua_derive")]
|
||||
@@ -198,10 +201,6 @@ extern crate mlua_derive;
|
||||
/// - The `//` (floor division) operator is unusable, as its start a comment.
|
||||
///
|
||||
/// Everything else should work.
|
||||
///
|
||||
/// [`AsChunk`]: crate::AsChunk
|
||||
/// [`UserData`]: crate::UserData
|
||||
/// [`IntoLua`]: crate::IntoLua
|
||||
#[cfg(feature = "macros")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "macros")))]
|
||||
pub use mlua_derive::chunk;
|
||||
@@ -218,7 +217,7 @@ pub use mlua_derive::FromLua;
|
||||
///
|
||||
/// You can register multiple entrypoints as required.
|
||||
///
|
||||
/// ```
|
||||
/// ```ignore
|
||||
/// use mlua::{Lua, Result, Table};
|
||||
///
|
||||
/// #[mlua::lua_module]
|
||||
@@ -255,7 +254,7 @@ pub use mlua_derive::FromLua;
|
||||
/// ...
|
||||
/// }
|
||||
/// ```
|
||||
#[cfg(any(feature = "module", docsrs))]
|
||||
#[cfg(all(feature = "mlua_derive", any(feature = "module", doc)))]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "module")))]
|
||||
pub use mlua_derive::lua_module;
|
||||
|
||||
|
||||
+43
-29
@@ -1,17 +1,32 @@
|
||||
use std::ffi::CStr;
|
||||
use std::os::raw::{c_float, c_int};
|
||||
use std::os::raw::c_int;
|
||||
use std::ptr;
|
||||
|
||||
use crate::chunk::ChunkMode;
|
||||
use crate::error::Result;
|
||||
use crate::state::Lua;
|
||||
use crate::function::Function;
|
||||
use crate::state::{callback_error_ext, ExtraData, Lua};
|
||||
use crate::traits::{FromLuaMulti, IntoLua};
|
||||
use crate::types::MaybeSend;
|
||||
|
||||
pub use require::{NavigateError, Require, TextRequirer};
|
||||
|
||||
// Since Luau has some missing standard functions, we re-implement them here
|
||||
|
||||
impl Lua {
|
||||
/// Create a custom Luau `require` function using provided [`Require`] implementation to find
|
||||
/// and load modules.
|
||||
#[cfg(any(feature = "luau", doc))]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
|
||||
pub fn create_require_function<R: Require + MaybeSend + 'static>(&self, require: R) -> Result<Function> {
|
||||
require::create_require_function(self, require)
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn configure_luau(&self) -> Result<()> {
|
||||
let globals = self.globals();
|
||||
|
||||
globals.raw_set("collectgarbage", self.create_c_function(lua_collectgarbage)?)?;
|
||||
globals.raw_set("vector", self.create_c_function(lua_vector)?)?;
|
||||
globals.raw_set("loadstring", self.create_c_function(lua_loadstring)?)?;
|
||||
|
||||
// Set `_VERSION` global to include version number
|
||||
// The environment variable `LUAU_VERSION` set by the build script
|
||||
@@ -19,11 +34,10 @@ impl Lua {
|
||||
globals.raw_set("_VERSION", format!("Luau {version}"))?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
// Enable default `require` implementation
|
||||
let require = self.create_require_function(require::TextRequirer::new())?;
|
||||
self.globals().raw_set("require", require)?;
|
||||
|
||||
pub(crate) fn disable_c_modules(&self) -> Result<()> {
|
||||
package::disable_dylibs(self);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -32,16 +46,17 @@ unsafe extern "C-unwind" fn lua_collectgarbage(state: *mut ffi::lua_State) -> c_
|
||||
let option = ffi::luaL_optstring(state, 1, cstr!("collect"));
|
||||
let option = CStr::from_ptr(option);
|
||||
let arg = ffi::luaL_optinteger(state, 2, 0);
|
||||
let is_sandboxed = (*ExtraData::get(state)).sandboxed;
|
||||
match option.to_str() {
|
||||
Ok("collect") => {
|
||||
Ok("collect") if !is_sandboxed => {
|
||||
ffi::lua_gc(state, ffi::LUA_GCCOLLECT, 0);
|
||||
0
|
||||
}
|
||||
Ok("stop") => {
|
||||
Ok("stop") if !is_sandboxed => {
|
||||
ffi::lua_gc(state, ffi::LUA_GCSTOP, 0);
|
||||
0
|
||||
}
|
||||
Ok("restart") => {
|
||||
Ok("restart") if !is_sandboxed => {
|
||||
ffi::lua_gc(state, ffi::LUA_GCRESTART, 0);
|
||||
0
|
||||
}
|
||||
@@ -51,12 +66,12 @@ unsafe extern "C-unwind" fn lua_collectgarbage(state: *mut ffi::lua_State) -> c_
|
||||
ffi::lua_pushnumber(state, kbytes + kbytes_rem / 1024.0);
|
||||
1
|
||||
}
|
||||
Ok("step") => {
|
||||
let res = ffi::lua_gc(state, ffi::LUA_GCSTEP, arg);
|
||||
Ok("step") if !is_sandboxed => {
|
||||
let res = ffi::lua_gc(state, ffi::LUA_GCSTEP, arg as _);
|
||||
ffi::lua_pushboolean(state, res);
|
||||
1
|
||||
}
|
||||
Ok("isrunning") => {
|
||||
Ok("isrunning") if !is_sandboxed => {
|
||||
let res = ffi::lua_gc(state, ffi::LUA_GCISRUNNING, 0);
|
||||
ffi::lua_pushboolean(state, res);
|
||||
1
|
||||
@@ -65,21 +80,20 @@ unsafe extern "C-unwind" fn lua_collectgarbage(state: *mut ffi::lua_State) -> c_
|
||||
}
|
||||
}
|
||||
|
||||
// Luau vector datatype constructor
|
||||
unsafe extern "C-unwind" fn lua_vector(state: *mut ffi::lua_State) -> c_int {
|
||||
let x = ffi::luaL_checknumber(state, 1) as c_float;
|
||||
let y = ffi::luaL_checknumber(state, 2) as c_float;
|
||||
let z = ffi::luaL_checknumber(state, 3) as c_float;
|
||||
#[cfg(feature = "luau-vector4")]
|
||||
let w = ffi::luaL_checknumber(state, 4) as c_float;
|
||||
|
||||
#[cfg(not(feature = "luau-vector4"))]
|
||||
ffi::lua_pushvector(state, x, y, z);
|
||||
#[cfg(feature = "luau-vector4")]
|
||||
ffi::lua_pushvector(state, x, y, z, w);
|
||||
1
|
||||
unsafe extern "C-unwind" fn lua_loadstring(state: *mut ffi::lua_State) -> c_int {
|
||||
callback_error_ext(state, ptr::null_mut(), false, move |extra, nargs| {
|
||||
let rawlua = (*extra).raw_lua();
|
||||
let (chunk, chunk_name) =
|
||||
<(String, Option<String>)>::from_stack_args(nargs, 1, Some("loadstring"), rawlua)?;
|
||||
let chunk_name = chunk_name.as_deref().unwrap_or("=(loadstring)");
|
||||
(rawlua.lua())
|
||||
.load(chunk)
|
||||
.set_name(chunk_name)
|
||||
.set_mode(ChunkMode::Text)
|
||||
.into_function()?
|
||||
.push_into_stack(rawlua)?;
|
||||
Ok(1)
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) use package::register_package_module;
|
||||
|
||||
mod package;
|
||||
mod require;
|
||||
|
||||
@@ -1,270 +0,0 @@
|
||||
use std::ffi::CStr;
|
||||
use std::fmt::Write;
|
||||
use std::os::raw::c_int;
|
||||
use std::path::{PathBuf, MAIN_SEPARATOR_STR};
|
||||
use std::string::String as StdString;
|
||||
use std::{env, fs};
|
||||
|
||||
use crate::chunk::ChunkMode;
|
||||
use crate::error::Result;
|
||||
use crate::state::Lua;
|
||||
use crate::table::Table;
|
||||
use crate::value::{IntoLua, Value};
|
||||
|
||||
#[cfg(unix)]
|
||||
use {libloading::Library, rustc_hash::FxHashMap};
|
||||
|
||||
//
|
||||
// Luau package module
|
||||
//
|
||||
|
||||
#[cfg(unix)]
|
||||
const TARGET_MLUA_LUAU_ABI_VERSION: u32 = 1;
|
||||
|
||||
#[cfg(all(unix, feature = "module"))]
|
||||
#[no_mangle]
|
||||
#[used]
|
||||
pub static MLUA_LUAU_ABI_VERSION: u32 = TARGET_MLUA_LUAU_ABI_VERSION;
|
||||
|
||||
// We keep reference to the loaded dylibs in application data
|
||||
#[cfg(unix)]
|
||||
struct LoadedDylibs(FxHashMap<PathBuf, Library>);
|
||||
|
||||
#[cfg(unix)]
|
||||
impl std::ops::Deref for LoadedDylibs {
|
||||
type Target = FxHashMap<PathBuf, Library>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
impl std::ops::DerefMut for LoadedDylibs {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.0
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn register_package_module(lua: &Lua) -> Result<()> {
|
||||
// Create the package table
|
||||
let package = lua.create_table()?;
|
||||
|
||||
// Set `package.path`
|
||||
let mut search_path = env::var("LUAU_PATH")
|
||||
.or_else(|_| env::var("LUA_PATH"))
|
||||
.unwrap_or_default();
|
||||
if search_path.is_empty() {
|
||||
search_path = "?.luau;?.lua".to_string();
|
||||
}
|
||||
package.raw_set("path", search_path)?;
|
||||
|
||||
// Set `package.cpath`
|
||||
#[cfg(unix)]
|
||||
{
|
||||
let mut search_cpath = env::var("LUAU_CPATH")
|
||||
.or_else(|_| env::var("LUA_CPATH"))
|
||||
.unwrap_or_default();
|
||||
if search_cpath.is_empty() {
|
||||
if cfg!(any(target_os = "macos", target_os = "ios")) {
|
||||
search_cpath = "?.dylib".to_string();
|
||||
} else {
|
||||
search_cpath = "?.so".to_string();
|
||||
}
|
||||
}
|
||||
package.raw_set("cpath", search_cpath)?;
|
||||
}
|
||||
|
||||
// Set `package.loaded` (table with a list of loaded modules)
|
||||
let loaded = if let Ok(Some(loaded)) = lua.named_registry_value::<Option<Table>>("_LOADED") {
|
||||
package.raw_set("loaded", &loaded)?;
|
||||
loaded
|
||||
} else {
|
||||
let loaded = lua.create_table()?;
|
||||
package.raw_set("loaded", &loaded)?;
|
||||
lua.set_named_registry_value("_LOADED", &loaded)?;
|
||||
loaded
|
||||
};
|
||||
|
||||
// Set `package.loaders`
|
||||
let loaders = lua.create_sequence_from([lua.create_function(lua_loader)?])?;
|
||||
package.raw_set("loaders", &loaders)?;
|
||||
#[cfg(unix)]
|
||||
{
|
||||
loaders.push(lua.create_function(dylib_loader)?)?;
|
||||
lua.set_app_data(LoadedDylibs(FxHashMap::default()));
|
||||
}
|
||||
lua.set_named_registry_value("_LOADERS", loaders)?;
|
||||
|
||||
// Register the module and `require` function in globals
|
||||
let globals = lua.globals();
|
||||
globals.raw_set("package", &package)?;
|
||||
loaded.raw_set("package", package)?;
|
||||
globals.raw_set("require", unsafe { lua.create_c_function(lua_require)? })?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[allow(unused_variables)]
|
||||
pub(crate) fn disable_dylibs(lua: &Lua) {
|
||||
// Presence of `LoadedDylibs` in app data is used as a flag
|
||||
// to check whether binary modules are enabled
|
||||
#[cfg(unix)]
|
||||
lua.remove_app_data::<LoadedDylibs>();
|
||||
}
|
||||
|
||||
unsafe extern "C-unwind" fn lua_require(state: *mut ffi::lua_State) -> c_int {
|
||||
ffi::lua_settop(state, 1);
|
||||
let name = ffi::luaL_checkstring(state, 1);
|
||||
ffi::luaL_getsubtable(state, ffi::LUA_REGISTRYINDEX, cstr!("_LOADED")); // _LOADED is at index 2
|
||||
if ffi::lua_rawgetfield(state, 2, name) != ffi::LUA_TNIL {
|
||||
return 1; // module is already loaded
|
||||
}
|
||||
ffi::lua_pop(state, 1); // remove nil
|
||||
|
||||
// load the module
|
||||
let err_buf = ffi::lua_newuserdata_t::<StdString>(state);
|
||||
err_buf.write(StdString::new());
|
||||
ffi::luaL_getsubtable(state, ffi::LUA_REGISTRYINDEX, cstr!("_LOADERS")); // _LOADERS is at index 3
|
||||
for i in 1.. {
|
||||
if ffi::lua_rawgeti(state, -1, i) == ffi::LUA_TNIL {
|
||||
// no more loaders?
|
||||
if (*err_buf).is_empty() {
|
||||
ffi::luaL_error(state, cstr!("module '%s' not found"), name);
|
||||
} else {
|
||||
let bytes = (*err_buf).as_bytes();
|
||||
let extra = ffi::lua_pushlstring(state, bytes.as_ptr() as *const _, bytes.len());
|
||||
ffi::luaL_error(state, cstr!("module '%s' not found:%s"), name, extra);
|
||||
}
|
||||
}
|
||||
ffi::lua_pushvalue(state, 1); // name arg
|
||||
ffi::lua_call(state, 1, 2); // call loader
|
||||
match ffi::lua_type(state, -2) {
|
||||
ffi::LUA_TFUNCTION => break, // loader found
|
||||
ffi::LUA_TSTRING => {
|
||||
// error message
|
||||
let msg = ffi::lua_tostring(state, -2);
|
||||
let msg = CStr::from_ptr(msg).to_string_lossy();
|
||||
_ = write!(&mut *err_buf, "\n\t{msg}");
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
ffi::lua_pop(state, 2); // remove both results
|
||||
}
|
||||
ffi::lua_pushvalue(state, 1); // name is 1st argument to module loader
|
||||
ffi::lua_rotate(state, -2, 1); // loader data <-> name
|
||||
|
||||
// stack: ...; loader function; module name; loader data
|
||||
ffi::lua_call(state, 2, 1);
|
||||
// stack: ...; result from loader function
|
||||
if ffi::lua_isnil(state, -1) != 0 {
|
||||
ffi::lua_pop(state, 1);
|
||||
ffi::lua_pushboolean(state, 1); // use true as result
|
||||
}
|
||||
ffi::lua_pushvalue(state, -1); // make copy of entrypoint result
|
||||
ffi::lua_setfield(state, 2, name); /* _LOADED[name] = returned value */
|
||||
1
|
||||
}
|
||||
|
||||
/// Searches for the given `name` in the given `path`.
|
||||
///
|
||||
/// `path` is a string containing a sequence of templates separated by semicolons.
|
||||
fn package_searchpath(name: &str, search_path: &str, try_prefix: bool) -> Option<PathBuf> {
|
||||
let mut names = vec![name.replace('.', MAIN_SEPARATOR_STR)];
|
||||
if try_prefix && name.contains('.') {
|
||||
let prefix = name.split_once('.').map(|(prefix, _)| prefix).unwrap();
|
||||
names.push(prefix.to_string());
|
||||
}
|
||||
for path in search_path.split(';') {
|
||||
for name in &names {
|
||||
let file_path = PathBuf::from(path.replace('?', name));
|
||||
if let Ok(true) = fs::metadata(&file_path).map(|m| m.is_file()) {
|
||||
return Some(file_path);
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
//
|
||||
// Module loaders
|
||||
//
|
||||
|
||||
/// Tries to load a lua (text) file
|
||||
fn lua_loader(lua: &Lua, modname: StdString) -> Result<Value> {
|
||||
let package = {
|
||||
let loaded = lua.named_registry_value::<Table>("_LOADED")?;
|
||||
loaded.raw_get::<Table>("package")
|
||||
}?;
|
||||
let search_path = package.get::<StdString>("path").unwrap_or_default();
|
||||
|
||||
if let Some(file_path) = package_searchpath(&modname, &search_path, false) {
|
||||
match fs::read(&file_path) {
|
||||
Ok(buf) => {
|
||||
return lua
|
||||
.load(&buf)
|
||||
.set_name(format!("={}", file_path.display()))
|
||||
.set_mode(ChunkMode::Text)
|
||||
.into_function()
|
||||
.map(Value::Function);
|
||||
}
|
||||
Err(err) => {
|
||||
return format!("cannot open '{}': {err}", file_path.display()).into_lua(lua);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Value::Nil)
|
||||
}
|
||||
|
||||
/// Tries to load a dynamic library
|
||||
#[cfg(unix)]
|
||||
fn dylib_loader(lua: &Lua, modname: StdString) -> Result<Value> {
|
||||
let package = {
|
||||
let loaded = lua.named_registry_value::<Table>("_LOADED")?;
|
||||
loaded.raw_get::<Table>("package")
|
||||
}?;
|
||||
let search_cpath = package.get::<StdString>("cpath").unwrap_or_default();
|
||||
|
||||
let find_symbol = |lib: &Library| unsafe {
|
||||
if let Ok(entry) = lib.get::<ffi::lua_CFunction>(format!("luaopen_{modname}\0").as_bytes()) {
|
||||
return lua.create_c_function(*entry).map(Value::Function);
|
||||
}
|
||||
// Try all in one mode
|
||||
if let Ok(entry) =
|
||||
lib.get::<ffi::lua_CFunction>(format!("luaopen_{}\0", modname.replace('.', "_")).as_bytes())
|
||||
{
|
||||
return lua.create_c_function(*entry).map(Value::Function);
|
||||
}
|
||||
"cannot find module entrypoint".into_lua(lua)
|
||||
};
|
||||
|
||||
if let Some(file_path) = package_searchpath(&modname, &search_cpath, true) {
|
||||
let file_path = file_path.canonicalize()?;
|
||||
// Load the library and check for symbol
|
||||
unsafe {
|
||||
let mut loaded_dylibs = match lua.app_data_mut::<LoadedDylibs>() {
|
||||
Some(loaded_dylibs) => loaded_dylibs,
|
||||
None => return "dynamic libraries are disabled in safe mode".into_lua(lua),
|
||||
};
|
||||
// Check if it's already loaded
|
||||
if let Some(lib) = loaded_dylibs.get(&file_path) {
|
||||
return find_symbol(lib);
|
||||
}
|
||||
if let Ok(lib) = Library::new(&file_path) {
|
||||
// Check version
|
||||
let mod_version = lib.get::<*const u32>(b"MLUA_LUAU_ABI_VERSION");
|
||||
let mod_version = mod_version.map(|v| **v).unwrap_or_default();
|
||||
if mod_version != TARGET_MLUA_LUAU_ABI_VERSION {
|
||||
let err = format!("wrong module ABI version (expected {TARGET_MLUA_LUAU_ABI_VERSION}, got {mod_version})");
|
||||
return err.into_lua(lua);
|
||||
}
|
||||
let symbol = find_symbol(&lib);
|
||||
loaded_dylibs.insert(file_path, lib);
|
||||
return symbol;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Value::Nil)
|
||||
}
|
||||
@@ -0,0 +1,688 @@
|
||||
use std::cell::RefCell;
|
||||
use std::collections::VecDeque;
|
||||
use std::ffi::CStr;
|
||||
use std::io::Result as IoResult;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use std::os::raw::{c_char, c_int, c_void};
|
||||
use std::path::{Component, Path, PathBuf};
|
||||
use std::result::Result as StdResult;
|
||||
use std::{env, fmt, fs, mem, ptr};
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
use crate::function::Function;
|
||||
use crate::state::{callback_error_ext, Lua};
|
||||
use crate::table::Table;
|
||||
use crate::types::MaybeSend;
|
||||
|
||||
/// An error that can occur during navigation in the Luau `require-by-string` system.
|
||||
#[cfg(any(feature = "luau", doc))]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum NavigateError {
|
||||
Ambiguous,
|
||||
NotFound,
|
||||
Other(Error),
|
||||
}
|
||||
|
||||
#[cfg(feature = "luau")]
|
||||
trait IntoNavigateResult {
|
||||
fn into_nav_result(self) -> Result<ffi::luarequire_NavigateResult>;
|
||||
}
|
||||
|
||||
#[cfg(feature = "luau")]
|
||||
impl IntoNavigateResult for StdResult<(), NavigateError> {
|
||||
fn into_nav_result(self) -> Result<ffi::luarequire_NavigateResult> {
|
||||
match self {
|
||||
Ok(()) => Ok(ffi::luarequire_NavigateResult::Success),
|
||||
Err(NavigateError::Ambiguous) => Ok(ffi::luarequire_NavigateResult::Ambiguous),
|
||||
Err(NavigateError::NotFound) => Ok(ffi::luarequire_NavigateResult::NotFound),
|
||||
Err(NavigateError::Other(err)) => Err(err),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Error> for NavigateError {
|
||||
fn from(err: Error) -> Self {
|
||||
NavigateError::Other(err)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "luau")]
|
||||
type WriteResult = ffi::luarequire_WriteResult;
|
||||
|
||||
/// A trait for handling modules loading and navigation in the Luau `require-by-string` system.
|
||||
#[cfg(any(feature = "luau", doc))]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
|
||||
pub trait Require {
|
||||
/// Returns `true` if "require" is permitted for the given chunk name.
|
||||
fn is_require_allowed(&self, chunk_name: &str) -> bool;
|
||||
|
||||
/// Resets the internal state to point at the requirer module.
|
||||
fn reset(&mut self, chunk_name: &str) -> StdResult<(), NavigateError>;
|
||||
|
||||
/// Resets the internal state to point at an aliased module.
|
||||
///
|
||||
/// This function received an exact path from a configuration file.
|
||||
/// It's only called when an alias's path cannot be resolved relative to its
|
||||
/// configuration file.
|
||||
fn jump_to_alias(&mut self, path: &str) -> StdResult<(), NavigateError>;
|
||||
|
||||
// Navigate to parent directory
|
||||
fn to_parent(&mut self) -> StdResult<(), NavigateError>;
|
||||
|
||||
/// Navigate to the given child directory.
|
||||
fn to_child(&mut self, name: &str) -> StdResult<(), NavigateError>;
|
||||
|
||||
/// Returns whether the context is currently pointing at a module
|
||||
fn has_module(&self) -> bool;
|
||||
|
||||
/// Provides a cache key representing the current module.
|
||||
///
|
||||
/// This function is only called if `has_module` returns true.
|
||||
fn cache_key(&self) -> String;
|
||||
|
||||
/// Returns whether a configuration is present in the current context.
|
||||
fn has_config(&self) -> bool;
|
||||
|
||||
/// Returns the contents of the configuration file in the current context.
|
||||
///
|
||||
/// This function is only called if `has_config` returns true.
|
||||
fn config(&self) -> IoResult<Vec<u8>>;
|
||||
|
||||
/// Returns a loader function for the current module, that when called, loads the module
|
||||
/// and returns the result.
|
||||
///
|
||||
/// Loader can be sync or async.
|
||||
/// This function is only called if `has_module` returns true.
|
||||
fn loader(&self, lua: &Lua) -> Result<Function>;
|
||||
}
|
||||
|
||||
impl fmt::Debug for dyn Require {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "<dyn Require>")
|
||||
}
|
||||
}
|
||||
|
||||
/// The standard implementation of Luau `require-by-string` navigation.
|
||||
#[derive(Default, Debug)]
|
||||
pub struct TextRequirer {
|
||||
/// An absolute path to the current Luau module (not mapped to a physical file)
|
||||
abs_path: PathBuf,
|
||||
/// A relative path to the current Luau module (not mapped to a physical file)
|
||||
rel_path: PathBuf,
|
||||
/// A physical path to the current Luau module, which is a file or a directory with an
|
||||
/// `init.lua(u)` file
|
||||
resolved_path: Option<PathBuf>,
|
||||
}
|
||||
|
||||
impl TextRequirer {
|
||||
/// The prefix used for chunk names in the require system.
|
||||
/// Only chunk names starting with this prefix are allowed to be used in `require`.
|
||||
const CHUNK_PREFIX: &str = "@";
|
||||
|
||||
/// The file extensions that are considered valid for Luau modules.
|
||||
const FILE_EXTENSIONS: &[&str] = &["luau", "lua"];
|
||||
|
||||
/// Creates a new `TextRequirer` instance.
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
fn normalize_chunk_name(chunk_name: &str) -> &str {
|
||||
if let Some((path, line)) = chunk_name.rsplit_once(':') {
|
||||
if line.parse::<u32>().is_ok() {
|
||||
return path;
|
||||
}
|
||||
}
|
||||
chunk_name
|
||||
}
|
||||
|
||||
// Normalizes the path by removing unnecessary components
|
||||
fn normalize_path(path: &Path) -> PathBuf {
|
||||
let mut components = VecDeque::new();
|
||||
|
||||
for comp in path.components() {
|
||||
match comp {
|
||||
Component::Prefix(..) | Component::RootDir => {
|
||||
components.push_back(comp);
|
||||
}
|
||||
Component::CurDir => {}
|
||||
Component::ParentDir => {
|
||||
if matches!(components.back(), None | Some(Component::ParentDir)) {
|
||||
components.push_back(Component::ParentDir);
|
||||
} else if matches!(components.back(), Some(Component::Normal(..))) {
|
||||
components.pop_back();
|
||||
}
|
||||
}
|
||||
Component::Normal(..) => components.push_back(comp),
|
||||
}
|
||||
}
|
||||
|
||||
if matches!(components.front(), None | Some(Component::Normal(..))) {
|
||||
components.push_front(Component::CurDir);
|
||||
}
|
||||
|
||||
// Join the components back together
|
||||
components.into_iter().collect()
|
||||
}
|
||||
|
||||
/// Resolve a Luau module path to a physical file or directory.
|
||||
///
|
||||
/// Empty directories without init files are considered valid as "intermediate" directories.
|
||||
fn resolve_module(path: &Path) -> StdResult<Option<PathBuf>, NavigateError> {
|
||||
let mut found_path = None;
|
||||
|
||||
if path.components().next_back() != Some(Component::Normal("init".as_ref())) {
|
||||
let current_ext = (path.extension().and_then(|s| s.to_str()))
|
||||
.map(|s| format!("{s}."))
|
||||
.unwrap_or_default();
|
||||
for ext in Self::FILE_EXTENSIONS {
|
||||
let candidate = path.with_extension(format!("{current_ext}{ext}"));
|
||||
if candidate.is_file() && found_path.replace(candidate).is_some() {
|
||||
return Err(NavigateError::Ambiguous);
|
||||
}
|
||||
}
|
||||
}
|
||||
if path.is_dir() {
|
||||
for component in Self::FILE_EXTENSIONS.iter().map(|ext| format!("init.{ext}")) {
|
||||
let candidate = path.join(component);
|
||||
if candidate.is_file() && found_path.replace(candidate).is_some() {
|
||||
return Err(NavigateError::Ambiguous);
|
||||
}
|
||||
}
|
||||
|
||||
if found_path.is_none() {
|
||||
// Directories without init files are considered valid "intermediate" path
|
||||
return Ok(None);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Some(found_path.ok_or(NavigateError::NotFound)?))
|
||||
}
|
||||
}
|
||||
|
||||
impl Require for TextRequirer {
|
||||
fn is_require_allowed(&self, chunk_name: &str) -> bool {
|
||||
chunk_name.starts_with(Self::CHUNK_PREFIX)
|
||||
}
|
||||
|
||||
fn reset(&mut self, chunk_name: &str) -> StdResult<(), NavigateError> {
|
||||
if !chunk_name.starts_with(Self::CHUNK_PREFIX) {
|
||||
return Err(NavigateError::NotFound);
|
||||
}
|
||||
let chunk_name = Self::normalize_chunk_name(&chunk_name[1..]);
|
||||
let chunk_path = Self::normalize_path(chunk_name.as_ref());
|
||||
|
||||
if chunk_path.extension() == Some("rs".as_ref()) {
|
||||
// Special case for Rust source files, reset to the current directory
|
||||
let chunk_filename = chunk_path.file_name().unwrap();
|
||||
let cwd = env::current_dir().map_err(|_| NavigateError::NotFound)?;
|
||||
self.abs_path = Self::normalize_path(&cwd.join(chunk_filename));
|
||||
self.rel_path = ([Component::CurDir, Component::Normal(chunk_filename)].into_iter()).collect();
|
||||
self.resolved_path = None;
|
||||
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
if chunk_path.is_absolute() {
|
||||
let resolved_path = Self::resolve_module(&chunk_path)?;
|
||||
self.abs_path = chunk_path.clone();
|
||||
self.rel_path = chunk_path;
|
||||
self.resolved_path = resolved_path;
|
||||
} else {
|
||||
// Relative path
|
||||
let cwd = env::current_dir().map_err(|_| NavigateError::NotFound)?;
|
||||
let abs_path = Self::normalize_path(&cwd.join(&chunk_path));
|
||||
let resolved_path = Self::resolve_module(&abs_path)?;
|
||||
self.abs_path = abs_path;
|
||||
self.rel_path = chunk_path;
|
||||
self.resolved_path = resolved_path;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn jump_to_alias(&mut self, path: &str) -> StdResult<(), NavigateError> {
|
||||
let path = Self::normalize_path(path.as_ref());
|
||||
let resolved_path = Self::resolve_module(&path)?;
|
||||
|
||||
self.abs_path = path.clone();
|
||||
self.rel_path = path;
|
||||
self.resolved_path = resolved_path;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn to_parent(&mut self) -> StdResult<(), NavigateError> {
|
||||
let mut abs_path = self.abs_path.clone();
|
||||
if !abs_path.pop() {
|
||||
// It's important to return `NotFound` if we reached the root, as it's a "recoverable" error if we
|
||||
// cannot go beyond the root directory.
|
||||
// Luau "require-by-string` has a special logic to search for config file to resolve aliases.
|
||||
return Err(NavigateError::NotFound);
|
||||
}
|
||||
let mut rel_parent = self.rel_path.clone();
|
||||
rel_parent.pop();
|
||||
let resolved_path = Self::resolve_module(&abs_path)?;
|
||||
|
||||
self.abs_path = abs_path;
|
||||
self.rel_path = Self::normalize_path(&rel_parent);
|
||||
self.resolved_path = resolved_path;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn to_child(&mut self, name: &str) -> StdResult<(), NavigateError> {
|
||||
let abs_path = self.abs_path.join(name);
|
||||
let rel_path = self.rel_path.join(name);
|
||||
let resolved_path = Self::resolve_module(&abs_path)?;
|
||||
|
||||
self.abs_path = abs_path;
|
||||
self.rel_path = rel_path;
|
||||
self.resolved_path = resolved_path;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn has_module(&self) -> bool {
|
||||
(self.resolved_path.as_deref())
|
||||
.map(Path::is_file)
|
||||
.unwrap_or(false)
|
||||
}
|
||||
|
||||
fn cache_key(&self) -> String {
|
||||
self.resolved_path.as_deref().unwrap().display().to_string()
|
||||
}
|
||||
|
||||
fn has_config(&self) -> bool {
|
||||
self.abs_path.is_dir() && self.abs_path.join(".luaurc").is_file()
|
||||
}
|
||||
|
||||
fn config(&self) -> IoResult<Vec<u8>> {
|
||||
fs::read(self.abs_path.join(".luaurc"))
|
||||
}
|
||||
|
||||
fn loader(&self, lua: &Lua) -> Result<Function> {
|
||||
let name = format!("@{}", self.rel_path.display());
|
||||
lua.load(self.resolved_path.as_deref().unwrap())
|
||||
.set_name(name)
|
||||
.into_function()
|
||||
}
|
||||
}
|
||||
|
||||
struct Context(Box<dyn Require>);
|
||||
|
||||
impl Deref for Context {
|
||||
type Target = dyn Require;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&*self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl DerefMut for Context {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut *self.0
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! try_borrow {
|
||||
($state:expr, $ctx:expr) => {
|
||||
match (*($ctx as *const RefCell<Context>)).try_borrow() {
|
||||
Ok(ctx) => ctx,
|
||||
Err(_) => ffi::luaL_error($state, cstr!("require context is already borrowed")),
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! try_borrow_mut {
|
||||
($state:expr, $ctx:expr) => {
|
||||
match (*($ctx as *const RefCell<Context>)).try_borrow_mut() {
|
||||
Ok(ctx) => ctx,
|
||||
Err(_) => ffi::luaL_error($state, cstr!("require context is already borrowed")),
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(feature = "luau")]
|
||||
pub(super) unsafe extern "C-unwind" fn init_config(config: *mut ffi::luarequire_Configuration) {
|
||||
if config.is_null() {
|
||||
return;
|
||||
}
|
||||
|
||||
unsafe extern "C-unwind" fn is_require_allowed(
|
||||
state: *mut ffi::lua_State,
|
||||
ctx: *mut c_void,
|
||||
requirer_chunkname: *const c_char,
|
||||
) -> bool {
|
||||
if requirer_chunkname.is_null() {
|
||||
return false;
|
||||
}
|
||||
|
||||
let this = try_borrow!(state, ctx);
|
||||
let chunk_name = CStr::from_ptr(requirer_chunkname).to_string_lossy();
|
||||
this.is_require_allowed(&chunk_name)
|
||||
}
|
||||
|
||||
unsafe extern "C-unwind" fn reset(
|
||||
state: *mut ffi::lua_State,
|
||||
ctx: *mut c_void,
|
||||
requirer_chunkname: *const c_char,
|
||||
) -> ffi::luarequire_NavigateResult {
|
||||
let mut this = try_borrow_mut!(state, ctx);
|
||||
let chunk_name = CStr::from_ptr(requirer_chunkname).to_string_lossy();
|
||||
callback_error_ext(state, ptr::null_mut(), true, move |_, _| {
|
||||
this.reset(&chunk_name).into_nav_result()
|
||||
})
|
||||
}
|
||||
|
||||
unsafe extern "C-unwind" fn jump_to_alias(
|
||||
state: *mut ffi::lua_State,
|
||||
ctx: *mut c_void,
|
||||
path: *const c_char,
|
||||
) -> ffi::luarequire_NavigateResult {
|
||||
let mut this = try_borrow_mut!(state, ctx);
|
||||
let path = CStr::from_ptr(path).to_string_lossy();
|
||||
callback_error_ext(state, ptr::null_mut(), true, move |_, _| {
|
||||
this.jump_to_alias(&path).into_nav_result()
|
||||
})
|
||||
}
|
||||
|
||||
unsafe extern "C-unwind" fn to_parent(
|
||||
state: *mut ffi::lua_State,
|
||||
ctx: *mut c_void,
|
||||
) -> ffi::luarequire_NavigateResult {
|
||||
let mut this = try_borrow_mut!(state, ctx);
|
||||
callback_error_ext(state, ptr::null_mut(), true, move |_, _| {
|
||||
this.to_parent().into_nav_result()
|
||||
})
|
||||
}
|
||||
|
||||
unsafe extern "C-unwind" fn to_child(
|
||||
state: *mut ffi::lua_State,
|
||||
ctx: *mut c_void,
|
||||
name: *const c_char,
|
||||
) -> ffi::luarequire_NavigateResult {
|
||||
let mut this = try_borrow_mut!(state, ctx);
|
||||
let name = CStr::from_ptr(name).to_string_lossy();
|
||||
callback_error_ext(state, ptr::null_mut(), true, move |_, _| {
|
||||
this.to_child(&name).into_nav_result()
|
||||
})
|
||||
}
|
||||
|
||||
unsafe extern "C-unwind" fn is_module_present(state: *mut ffi::lua_State, ctx: *mut c_void) -> bool {
|
||||
let this = try_borrow!(state, ctx);
|
||||
this.has_module()
|
||||
}
|
||||
|
||||
unsafe extern "C-unwind" fn get_chunkname(
|
||||
_state: *mut ffi::lua_State,
|
||||
_ctx: *mut c_void,
|
||||
buffer: *mut c_char,
|
||||
buffer_size: usize,
|
||||
size_out: *mut usize,
|
||||
) -> WriteResult {
|
||||
write_to_buffer(buffer, buffer_size, size_out, &[])
|
||||
}
|
||||
|
||||
unsafe extern "C-unwind" fn get_loadname(
|
||||
_state: *mut ffi::lua_State,
|
||||
_ctx: *mut c_void,
|
||||
buffer: *mut c_char,
|
||||
buffer_size: usize,
|
||||
size_out: *mut usize,
|
||||
) -> WriteResult {
|
||||
write_to_buffer(buffer, buffer_size, size_out, &[])
|
||||
}
|
||||
|
||||
unsafe extern "C-unwind" fn get_cache_key(
|
||||
state: *mut ffi::lua_State,
|
||||
ctx: *mut c_void,
|
||||
buffer: *mut c_char,
|
||||
buffer_size: usize,
|
||||
size_out: *mut usize,
|
||||
) -> WriteResult {
|
||||
let this = try_borrow!(state, ctx);
|
||||
let cache_key = this.cache_key();
|
||||
write_to_buffer(buffer, buffer_size, size_out, cache_key.as_bytes())
|
||||
}
|
||||
|
||||
unsafe extern "C-unwind" fn is_config_present(state: *mut ffi::lua_State, ctx: *mut c_void) -> bool {
|
||||
let this = try_borrow!(state, ctx);
|
||||
this.has_config()
|
||||
}
|
||||
|
||||
unsafe extern "C-unwind" fn get_config(
|
||||
state: *mut ffi::lua_State,
|
||||
ctx: *mut c_void,
|
||||
buffer: *mut c_char,
|
||||
buffer_size: usize,
|
||||
size_out: *mut usize,
|
||||
) -> WriteResult {
|
||||
let this = try_borrow!(state, ctx);
|
||||
let config = callback_error_ext(state, ptr::null_mut(), true, move |_, _| Ok(this.config()?));
|
||||
write_to_buffer(buffer, buffer_size, size_out, &config)
|
||||
}
|
||||
|
||||
unsafe extern "C-unwind" fn load(
|
||||
state: *mut ffi::lua_State,
|
||||
ctx: *mut c_void,
|
||||
_path: *const c_char,
|
||||
_chunkname: *const c_char,
|
||||
_loadname: *const c_char,
|
||||
) -> c_int {
|
||||
let this = try_borrow!(state, ctx);
|
||||
callback_error_ext(state, ptr::null_mut(), true, move |extra, _| {
|
||||
let rawlua = (*extra).raw_lua();
|
||||
let loader = this.loader(rawlua.lua())?;
|
||||
rawlua.push(loader)?;
|
||||
Ok(1)
|
||||
})
|
||||
}
|
||||
|
||||
(*config).is_require_allowed = is_require_allowed;
|
||||
(*config).reset = reset;
|
||||
(*config).jump_to_alias = jump_to_alias;
|
||||
(*config).to_parent = to_parent;
|
||||
(*config).to_child = to_child;
|
||||
(*config).is_module_present = is_module_present;
|
||||
(*config).get_chunkname = get_chunkname;
|
||||
(*config).get_loadname = get_loadname;
|
||||
(*config).get_cache_key = get_cache_key;
|
||||
(*config).is_config_present = is_config_present;
|
||||
(*config).get_alias = None;
|
||||
(*config).get_config = Some(get_config);
|
||||
(*config).load = load;
|
||||
}
|
||||
|
||||
/// Helper function to write data to a buffer
|
||||
#[cfg(feature = "luau")]
|
||||
unsafe fn write_to_buffer(
|
||||
buffer: *mut c_char,
|
||||
buffer_size: usize,
|
||||
size_out: *mut usize,
|
||||
data: &[u8],
|
||||
) -> WriteResult {
|
||||
// the buffer must be null terminated as it's a c++ `std::string` data() buffer
|
||||
let is_null_terminated = data.last() == Some(&0);
|
||||
*size_out = data.len() + if is_null_terminated { 0 } else { 1 };
|
||||
if *size_out > buffer_size {
|
||||
return WriteResult::BufferTooSmall;
|
||||
}
|
||||
ptr::copy_nonoverlapping(data.as_ptr(), buffer as *mut _, data.len());
|
||||
if !is_null_terminated {
|
||||
*buffer.add(data.len()) = 0;
|
||||
}
|
||||
WriteResult::Success
|
||||
}
|
||||
|
||||
#[cfg(feature = "luau")]
|
||||
pub(super) fn create_require_function<R: Require + MaybeSend + 'static>(
|
||||
lua: &Lua,
|
||||
require: R,
|
||||
) -> Result<Function> {
|
||||
unsafe extern "C-unwind" fn find_current_file(state: *mut ffi::lua_State) -> c_int {
|
||||
let mut ar: ffi::lua_Debug = mem::zeroed();
|
||||
for level in 2.. {
|
||||
if ffi::lua_getinfo(state, level, cstr!("s"), &mut ar) == 0 {
|
||||
ffi::luaL_error(state, cstr!("require is not supported in this context"));
|
||||
}
|
||||
if CStr::from_ptr(ar.what) != c"C" {
|
||||
break;
|
||||
}
|
||||
}
|
||||
ffi::lua_pushstring(state, ar.source);
|
||||
1
|
||||
}
|
||||
|
||||
unsafe extern "C-unwind" fn get_cache_key(state: *mut ffi::lua_State) -> c_int {
|
||||
let ctx = ffi::lua_touserdata(state, ffi::lua_upvalueindex(1));
|
||||
let ctx = try_borrow!(state, ctx);
|
||||
let cache_key = ctx.cache_key();
|
||||
ffi::lua_pushlstring(state, cache_key.as_ptr() as *const _, cache_key.len());
|
||||
1
|
||||
}
|
||||
|
||||
let (get_cache_key, find_current_file, proxyrequire, registered_modules, loader_cache) = unsafe {
|
||||
lua.exec_raw::<(Function, Function, Function, Table, Table)>((), move |state| {
|
||||
let context = Context(Box::new(require));
|
||||
let context_ptr = ffi::lua_newuserdata_t(state, RefCell::new(context));
|
||||
ffi::lua_pushcclosured(state, get_cache_key, cstr!("get_cache_key"), 1);
|
||||
ffi::lua_pushcfunctiond(state, find_current_file, cstr!("find_current_file"));
|
||||
ffi::luarequire_pushproxyrequire(state, init_config, context_ptr as *mut _);
|
||||
ffi::luaL_getsubtable(state, ffi::LUA_REGISTRYINDEX, ffi::LUA_REGISTERED_MODULES_TABLE);
|
||||
ffi::luaL_getsubtable(state, ffi::LUA_REGISTRYINDEX, cstr!("__MLUA_LOADER_CACHE"));
|
||||
})
|
||||
}?;
|
||||
|
||||
unsafe extern "C-unwind" fn error(state: *mut ffi::lua_State) -> c_int {
|
||||
ffi::luaL_where(state, 1);
|
||||
ffi::lua_pushvalue(state, 1);
|
||||
ffi::lua_concat(state, 2);
|
||||
ffi::lua_error(state);
|
||||
}
|
||||
|
||||
unsafe extern "C-unwind" fn r#type(state: *mut ffi::lua_State) -> c_int {
|
||||
ffi::lua_pushstring(state, ffi::lua_typename(state, ffi::lua_type(state, 1)));
|
||||
1
|
||||
}
|
||||
|
||||
unsafe extern "C-unwind" fn to_lowercase(state: *mut ffi::lua_State) -> c_int {
|
||||
let s = ffi::luaL_checkstring(state, 1);
|
||||
let s = CStr::from_ptr(s);
|
||||
if !s.to_bytes().iter().any(|&c| c.is_ascii_uppercase()) {
|
||||
// If the string does not contain any uppercase ASCII letters, return it as is
|
||||
return 1;
|
||||
}
|
||||
callback_error_ext(state, ptr::null_mut(), true, |extra, _| {
|
||||
let s = (s.to_bytes().iter())
|
||||
.map(|&c| c.to_ascii_lowercase())
|
||||
.collect::<bstr::BString>();
|
||||
(*extra).raw_lua().push(s).map(|_| 1)
|
||||
})
|
||||
}
|
||||
|
||||
let (error, r#type, to_lowercase) = unsafe {
|
||||
lua.exec_raw::<(Function, Function, Function)>((), move |state| {
|
||||
ffi::lua_pushcfunctiond(state, error, cstr!("error"));
|
||||
ffi::lua_pushcfunctiond(state, r#type, cstr!("type"));
|
||||
ffi::lua_pushcfunctiond(state, to_lowercase, cstr!("to_lowercase"));
|
||||
})
|
||||
}?;
|
||||
|
||||
// Prepare environment for the "require" function
|
||||
let env = lua.create_table_with_capacity(0, 7)?;
|
||||
env.raw_set("get_cache_key", get_cache_key)?;
|
||||
env.raw_set("find_current_file", find_current_file)?;
|
||||
env.raw_set("proxyrequire", proxyrequire)?;
|
||||
env.raw_set("REGISTERED_MODULES", registered_modules)?;
|
||||
env.raw_set("LOADER_CACHE", loader_cache)?;
|
||||
env.raw_set("error", error)?;
|
||||
env.raw_set("type", r#type)?;
|
||||
env.raw_set("to_lowercase", to_lowercase)?;
|
||||
|
||||
lua.load(
|
||||
r#"
|
||||
local path = ...
|
||||
if type(path) ~= "string" then
|
||||
error("bad argument #1 to 'require' (string expected, got " .. type(path) .. ")")
|
||||
end
|
||||
|
||||
-- Check if the module (path) is explicitly registered
|
||||
local maybe_result = REGISTERED_MODULES[to_lowercase(path)]
|
||||
if maybe_result ~= nil then
|
||||
return maybe_result
|
||||
end
|
||||
|
||||
local loader = proxyrequire(path, find_current_file())
|
||||
local cache_key = get_cache_key()
|
||||
-- Check if the loader result is already cached
|
||||
local result = LOADER_CACHE[cache_key]
|
||||
if result ~= nil then
|
||||
return result
|
||||
end
|
||||
|
||||
-- Call the loader function and cache the result
|
||||
result = loader()
|
||||
if result == nil then
|
||||
result = true
|
||||
end
|
||||
LOADER_CACHE[cache_key] = result
|
||||
return result
|
||||
"#,
|
||||
)
|
||||
.try_cache()
|
||||
.set_name("=__mlua_require")
|
||||
.set_environment(env)
|
||||
.into_function()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::path::Path;
|
||||
|
||||
use super::TextRequirer;
|
||||
|
||||
#[test]
|
||||
fn test_path_normalize() {
|
||||
for (input, expected) in [
|
||||
// Basic formatting checks
|
||||
("", "./"),
|
||||
(".", "./"),
|
||||
("a/relative/path", "./a/relative/path"),
|
||||
// Paths containing extraneous '.' and '/' symbols
|
||||
("./remove/extraneous/symbols/", "./remove/extraneous/symbols"),
|
||||
("./remove/extraneous//symbols", "./remove/extraneous/symbols"),
|
||||
("./remove/extraneous/symbols/.", "./remove/extraneous/symbols"),
|
||||
("./remove/extraneous/./symbols", "./remove/extraneous/symbols"),
|
||||
("../remove/extraneous/symbols/", "../remove/extraneous/symbols"),
|
||||
("../remove/extraneous//symbols", "../remove/extraneous/symbols"),
|
||||
("../remove/extraneous/symbols/.", "../remove/extraneous/symbols"),
|
||||
("../remove/extraneous/./symbols", "../remove/extraneous/symbols"),
|
||||
("/remove/extraneous/symbols/", "/remove/extraneous/symbols"),
|
||||
("/remove/extraneous//symbols", "/remove/extraneous/symbols"),
|
||||
("/remove/extraneous/symbols/.", "/remove/extraneous/symbols"),
|
||||
("/remove/extraneous/./symbols", "/remove/extraneous/symbols"),
|
||||
// Paths containing '..'
|
||||
("./remove/me/..", "./remove"),
|
||||
("./remove/me/../", "./remove"),
|
||||
("../remove/me/..", "../remove"),
|
||||
("../remove/me/../", "../remove"),
|
||||
("/remove/me/..", "/remove"),
|
||||
("/remove/me/../", "/remove"),
|
||||
("./..", "../"),
|
||||
("./../", "../"),
|
||||
("../..", "../../"),
|
||||
("../../", "../../"),
|
||||
// '..' disappears if path is absolute and component is non-erasable
|
||||
("/../", "/"),
|
||||
] {
|
||||
let path = TextRequirer::normalize_path(input.as_ref());
|
||||
assert_eq!(
|
||||
&path,
|
||||
expected.as_ref() as &Path,
|
||||
"wrong normalization for {input}"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
+23
-6
@@ -18,15 +18,32 @@ pub(crate) struct MemoryState {
|
||||
}
|
||||
|
||||
impl MemoryState {
|
||||
#[cfg(feature = "luau")]
|
||||
#[inline]
|
||||
pub(crate) unsafe fn get(state: *mut ffi::lua_State) -> *mut Self {
|
||||
let mut mem_state = ptr::null_mut();
|
||||
#[cfg(feature = "luau")]
|
||||
{
|
||||
ffi::lua_getallocf(state, &mut mem_state);
|
||||
mlua_assert!(!mem_state.is_null(), "Luau state has no allocator userdata");
|
||||
ffi::lua_getallocf(state, &mut mem_state);
|
||||
mlua_assert!(!mem_state.is_null(), "Luau state has no allocator userdata");
|
||||
mem_state as *mut MemoryState
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "luau"))]
|
||||
#[rustversion::since(1.85)]
|
||||
#[inline]
|
||||
#[allow(clippy::incompatible_msrv)]
|
||||
pub(crate) unsafe fn get(state: *mut ffi::lua_State) -> *mut Self {
|
||||
let mut mem_state = ptr::null_mut();
|
||||
if !ptr::fn_addr_eq(ffi::lua_getallocf(state, &mut mem_state), ALLOCATOR) {
|
||||
mem_state = ptr::null_mut();
|
||||
}
|
||||
#[cfg(not(feature = "luau"))]
|
||||
mem_state as *mut MemoryState
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "luau"))]
|
||||
#[rustversion::before(1.85)]
|
||||
#[inline]
|
||||
pub(crate) unsafe fn get(state: *mut ffi::lua_State) -> *mut Self {
|
||||
let mut mem_state = ptr::null_mut();
|
||||
if ffi::lua_getallocf(state, &mut mem_state) != ALLOCATOR {
|
||||
mem_state = ptr::null_mut();
|
||||
}
|
||||
@@ -80,7 +97,7 @@ impl MemoryState {
|
||||
}
|
||||
}
|
||||
|
||||
unsafe extern "C-unwind" fn allocator(
|
||||
unsafe extern "C" fn allocator(
|
||||
extra: *mut c_void,
|
||||
ptr: *mut c_void,
|
||||
osize: usize,
|
||||
|
||||
+161
-27
@@ -1,14 +1,17 @@
|
||||
use std::collections::{vec_deque, VecDeque};
|
||||
use std::iter::FromIterator;
|
||||
use std::mem;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use std::os::raw::c_int;
|
||||
use std::result::Result as StdResult;
|
||||
|
||||
use crate::error::Result;
|
||||
use crate::state::{Lua, RawLua};
|
||||
use crate::traits::{FromLua, FromLuaMulti, IntoLua, IntoLuaMulti};
|
||||
use crate::util::check_stack;
|
||||
use crate::value::{FromLua, FromLuaMulti, IntoLua, IntoLuaMulti, MultiValue, Nil};
|
||||
use crate::value::{Nil, Value};
|
||||
|
||||
/// Result is convertible to `MultiValue` following the common Lua idiom of returning the result
|
||||
/// 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<T: IntoLua, E: IntoLua> IntoLuaMulti for StdResult<T, E> {
|
||||
#[inline]
|
||||
@@ -89,6 +92,111 @@ impl<T: FromLua> FromLuaMulti for T {
|
||||
}
|
||||
}
|
||||
|
||||
/// Multiple Lua values used for both argument passing and also for multiple return values.
|
||||
#[derive(Default, Debug, Clone)]
|
||||
pub struct MultiValue(VecDeque<Value>);
|
||||
|
||||
impl Deref for MultiValue {
|
||||
type Target = VecDeque<Value>;
|
||||
|
||||
#[inline]
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl DerefMut for MultiValue {
|
||||
#[inline]
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl MultiValue {
|
||||
/// Creates an empty `MultiValue` containing no values.
|
||||
#[inline]
|
||||
pub const fn new() -> MultiValue {
|
||||
MultiValue(VecDeque::new())
|
||||
}
|
||||
|
||||
/// Creates an empty `MultiValue` container with space for at least `capacity` elements.
|
||||
pub fn with_capacity(capacity: usize) -> MultiValue {
|
||||
MultiValue(VecDeque::with_capacity(capacity))
|
||||
}
|
||||
|
||||
/// Creates a `MultiValue` container from vector of values.
|
||||
///
|
||||
/// This method works in *O*(1) time and does not allocate any additional memory.
|
||||
#[inline]
|
||||
pub fn from_vec(vec: Vec<Value>) -> MultiValue {
|
||||
vec.into()
|
||||
}
|
||||
|
||||
/// Consumes the `MultiValue` and returns a vector of values.
|
||||
///
|
||||
/// This method needs *O*(*n*) data movement if the circular buffer doesn't happen to be at the
|
||||
/// beginning of the allocation.
|
||||
#[inline]
|
||||
pub fn into_vec(self) -> Vec<Value> {
|
||||
self.into()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn from_lua_iter<T: IntoLua>(lua: &Lua, iter: impl IntoIterator<Item = T>) -> Result<Self> {
|
||||
let iter = iter.into_iter();
|
||||
let mut multi_value = MultiValue::with_capacity(iter.size_hint().0);
|
||||
for value in iter {
|
||||
multi_value.push_back(value.into_lua(lua)?);
|
||||
}
|
||||
Ok(multi_value)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Vec<Value>> for MultiValue {
|
||||
#[inline]
|
||||
fn from(value: Vec<Value>) -> Self {
|
||||
MultiValue(value.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<MultiValue> for Vec<Value> {
|
||||
#[inline]
|
||||
fn from(value: MultiValue) -> Self {
|
||||
value.0.into()
|
||||
}
|
||||
}
|
||||
|
||||
impl FromIterator<Value> for MultiValue {
|
||||
#[inline]
|
||||
fn from_iter<I: IntoIterator<Item = Value>>(iter: I) -> Self {
|
||||
let mut multi_value = MultiValue::new();
|
||||
multi_value.extend(iter);
|
||||
multi_value
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoIterator for MultiValue {
|
||||
type Item = Value;
|
||||
type IntoIter = vec_deque::IntoIter<Value>;
|
||||
|
||||
#[inline]
|
||||
fn into_iter(mut self) -> Self::IntoIter {
|
||||
let deque = mem::take(&mut self.0);
|
||||
mem::forget(self);
|
||||
deque.into_iter()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> IntoIterator for &'a MultiValue {
|
||||
type Item = &'a Value;
|
||||
type IntoIter = vec_deque::Iter<'a, Value>;
|
||||
|
||||
#[inline]
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.0.iter()
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoLuaMulti for MultiValue {
|
||||
#[inline]
|
||||
fn into_lua_multi(self, _: &Lua) -> Result<MultiValue> {
|
||||
@@ -126,10 +234,7 @@ impl FromLuaMulti for MultiValue {
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
///
|
||||
/// [`FromLua`]: crate::FromLua
|
||||
/// [`MultiValue`]: crate::MultiValue
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Default, Debug, Clone)]
|
||||
pub struct Variadic<T>(Vec<T>);
|
||||
|
||||
impl<T> Variadic<T> {
|
||||
@@ -137,11 +242,38 @@ impl<T> Variadic<T> {
|
||||
pub const fn new() -> Variadic<T> {
|
||||
Variadic(Vec::new())
|
||||
}
|
||||
|
||||
/// Creates an empty `Variadic` container with space for at least `capacity` elements.
|
||||
pub fn with_capacity(capacity: usize) -> Variadic<T> {
|
||||
Variadic(Vec::with_capacity(capacity))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Default for Variadic<T> {
|
||||
fn default() -> Variadic<T> {
|
||||
const { Variadic::new() }
|
||||
impl<T> Deref for Variadic<T> {
|
||||
type Target = Vec<T>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> DerefMut for Variadic<T> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> From<Vec<T>> for Variadic<T> {
|
||||
#[inline]
|
||||
fn from(vec: Vec<T>) -> Self {
|
||||
Variadic(vec)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> From<Variadic<T>> for Vec<T> {
|
||||
#[inline]
|
||||
fn from(value: Variadic<T>) -> Self {
|
||||
value.0
|
||||
}
|
||||
}
|
||||
|
||||
@@ -160,25 +292,20 @@ impl<T> IntoIterator for Variadic<T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Deref for Variadic<T> {
|
||||
type Target = Vec<T>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> DerefMut for Variadic<T> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: IntoLua> IntoLuaMulti for Variadic<T> {
|
||||
#[inline]
|
||||
fn into_lua_multi(self, lua: &Lua) -> Result<MultiValue> {
|
||||
MultiValue::from_lua_iter(lua, self)
|
||||
}
|
||||
|
||||
unsafe fn push_into_stack_multi(self, lua: &RawLua) -> Result<c_int> {
|
||||
let nresults = self.len() as i32;
|
||||
check_stack(lua.state(), nresults + 1)?;
|
||||
for value in self.0 {
|
||||
value.push_into_stack(lua)?;
|
||||
}
|
||||
Ok(nresults)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: FromLua> FromLuaMulti for Variadic<T> {
|
||||
@@ -213,10 +340,7 @@ macro_rules! impl_tuple {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn from_stack_multi(nvals: c_int, lua: &RawLua) -> Result<Self> {
|
||||
if nvals > 0 {
|
||||
ffi::lua_pop(lua.state(), nvals);
|
||||
}
|
||||
unsafe fn from_stack_multi(_nvals: c_int, _lua: &RawLua) -> Result<Self> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -342,3 +466,13 @@ impl_tuple!(A B C D E F G H I J K L M);
|
||||
impl_tuple!(A B C D E F G H I J K L M N);
|
||||
impl_tuple!(A B C D E F G H I J K L M N O);
|
||||
impl_tuple!(A B C D E F G H I J K L M N O P);
|
||||
|
||||
#[cfg(test)]
|
||||
mod assertions {
|
||||
use super::*;
|
||||
|
||||
#[cfg(not(feature = "send"))]
|
||||
static_assertions::assert_not_impl_any!(MultiValue: Send);
|
||||
#[cfg(feature = "send")]
|
||||
static_assertions::assert_impl_all!(MultiValue: Send, Sync);
|
||||
}
|
||||
|
||||
+18
-12
@@ -2,17 +2,19 @@
|
||||
|
||||
#[doc(no_inline)]
|
||||
pub use crate::{
|
||||
AnyUserData as LuaAnyUserData, Chunk as LuaChunk, Error as LuaError, ErrorContext as LuaErrorContext,
|
||||
AnyUserData as LuaAnyUserData, BorrowedBytes as LuaBorrowedBytes, BorrowedStr as LuaBorrowedStr,
|
||||
Chunk as LuaChunk, Either as LuaEither, Error as LuaError, ErrorContext as LuaErrorContext,
|
||||
ExternalError as LuaExternalError, ExternalResult as LuaExternalResult, FromLua, FromLuaMulti,
|
||||
Function as LuaFunction, FunctionInfo as LuaFunctionInfo, GCMode as LuaGCMode, Integer as LuaInteger,
|
||||
IntoLua, IntoLuaMulti, LightUserData as LuaLightUserData, Lua, LuaOptions, MetaMethod as LuaMetaMethod,
|
||||
MultiValue as LuaMultiValue, Nil as LuaNil, Number as LuaNumber, ObjectLike as LuaObjectLike,
|
||||
RegistryKey as LuaRegistryKey, Result as LuaResult, StdLib as LuaStdLib, String as LuaString,
|
||||
Table as LuaTable, TablePairs as LuaTablePairs, TableSequence as LuaTableSequence, Thread as LuaThread,
|
||||
ThreadStatus as LuaThreadStatus, UserData as LuaUserData, UserDataFields as LuaUserDataFields,
|
||||
UserDataMetatable as LuaUserDataMetatable, UserDataMethods as LuaUserDataMethods,
|
||||
UserDataRef as LuaUserDataRef, UserDataRefMut as LuaUserDataRefMut,
|
||||
UserDataRegistry as LuaUserDataRegistry, Value as LuaValue,
|
||||
IntoLua, IntoLuaMulti, LightUserData as LuaLightUserData, Lua, LuaNativeFn, LuaNativeFnMut, LuaOptions,
|
||||
MetaMethod as LuaMetaMethod, MultiValue as LuaMultiValue, Nil as LuaNil, Number as LuaNumber,
|
||||
ObjectLike as LuaObjectLike, RegistryKey as LuaRegistryKey, Result as LuaResult, StdLib as LuaStdLib,
|
||||
String as LuaString, Table as LuaTable, TablePairs as LuaTablePairs, TableSequence as LuaTableSequence,
|
||||
Thread as LuaThread, ThreadStatus as LuaThreadStatus, UserData as LuaUserData,
|
||||
UserDataFields as LuaUserDataFields, UserDataMetatable as LuaUserDataMetatable,
|
||||
UserDataMethods as LuaUserDataMethods, UserDataRef as LuaUserDataRef,
|
||||
UserDataRefMut as LuaUserDataRefMut, UserDataRegistry as LuaUserDataRegistry, Value as LuaValue,
|
||||
Variadic as LuaVariadic, VmState as LuaVmState, WeakLua,
|
||||
};
|
||||
|
||||
#[cfg(not(feature = "luau"))]
|
||||
@@ -21,13 +23,17 @@ pub use crate::HookTriggers as LuaHookTriggers;
|
||||
|
||||
#[cfg(feature = "luau")]
|
||||
#[doc(no_inline)]
|
||||
pub use crate::{CoverageInfo as LuaCoverageInfo, Vector as LuaVector, VmState as LuaVmState};
|
||||
pub use crate::{
|
||||
CompileConstant as LuaCompileConstant, CoverageInfo as LuaCoverageInfo,
|
||||
NavigateError as LuaNavigateError, Require as LuaRequire, TextRequirer as LuaTextRequirer,
|
||||
Vector as LuaVector,
|
||||
};
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
#[doc(no_inline)]
|
||||
pub use crate::AsyncThread as LuaAsyncThread;
|
||||
pub use crate::{AsyncThread as LuaAsyncThread, LuaNativeAsyncFn};
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
#[cfg(feature = "serde")]
|
||||
#[doc(no_inline)]
|
||||
pub use crate::{
|
||||
DeserializeOptions as LuaDeserializeOptions, LuaSerdeExt, SerializeOptions as LuaSerializeOptions,
|
||||
|
||||
+200
-821
File diff suppressed because it is too large
Load Diff
+32
-15
@@ -1,3 +1,5 @@
|
||||
//! Deserialize Lua values to a Rust data structure.
|
||||
|
||||
use std::cell::RefCell;
|
||||
use std::os::raw::c_void;
|
||||
use std::rc::Rc;
|
||||
@@ -47,6 +49,11 @@ pub struct Options {
|
||||
///
|
||||
/// Default: **false**
|
||||
pub sort_keys: bool,
|
||||
|
||||
/// If true, empty Lua tables will be encoded as array, instead of map.
|
||||
///
|
||||
/// Default: **false**
|
||||
pub encode_empty_tables_as_array: bool,
|
||||
}
|
||||
|
||||
impl Default for Options {
|
||||
@@ -62,6 +69,7 @@ impl Options {
|
||||
deny_unsupported_types: true,
|
||||
deny_recursive_tables: true,
|
||||
sort_keys: false,
|
||||
encode_empty_tables_as_array: false,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -91,15 +99,24 @@ impl Options {
|
||||
self.sort_keys = enabled;
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets [`encode_empty_tables_as_array`] option.
|
||||
///
|
||||
/// [`encode_empty_tables_as_array`]: #structfield.encode_empty_tables_as_array
|
||||
#[must_use]
|
||||
pub const fn encode_empty_tables_as_array(mut self, enabled: bool) -> Self {
|
||||
self.encode_empty_tables_as_array = enabled;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl Deserializer {
|
||||
/// Creates a new Lua Deserializer for the `Value`.
|
||||
/// Creates a new Lua Deserializer for the [`Value`].
|
||||
pub fn new(value: Value) -> Self {
|
||||
Self::new_with_options(value, Options::default())
|
||||
}
|
||||
|
||||
/// Creates a new Lua Deserializer for the `Value` with custom options.
|
||||
/// Creates a new Lua Deserializer for the [`Value`] with custom options.
|
||||
pub fn new_with_options(value: Value, options: Options) -> Self {
|
||||
Deserializer {
|
||||
value,
|
||||
@@ -139,25 +156,25 @@ impl<'de> serde::Deserializer<'de> for Deserializer {
|
||||
Err(_) => visitor.visit_bytes(&s.as_bytes()),
|
||||
},
|
||||
Value::Table(ref t) if t.raw_len() > 0 || t.is_array() => self.deserialize_seq(visitor),
|
||||
Value::Table(ref t) if self.options.encode_empty_tables_as_array && t.is_empty() => {
|
||||
self.deserialize_seq(visitor)
|
||||
}
|
||||
Value::Table(_) => self.deserialize_map(visitor),
|
||||
Value::LightUserData(ud) if ud.0.is_null() => visitor.visit_none(),
|
||||
Value::UserData(ud) if ud.is_serializable() => {
|
||||
serde_userdata(ud, |value| value.deserialize_any(visitor))
|
||||
}
|
||||
#[cfg(feature = "luau")]
|
||||
Value::UserData(ud) if ud.1 == crate::types::SubtypeId::Buffer => unsafe {
|
||||
let lua = ud.0.lua.lock();
|
||||
let mut size = 0usize;
|
||||
let buf = ffi::lua_tobuffer(lua.ref_thread(), ud.0.index, &mut size);
|
||||
mlua_assert!(!buf.is_null(), "invalid Luau buffer");
|
||||
let buf = std::slice::from_raw_parts(buf as *const u8, size);
|
||||
visitor.visit_bytes(buf)
|
||||
},
|
||||
Value::Buffer(buf) => {
|
||||
let lua = buf.0.lua.lock();
|
||||
visitor.visit_bytes(buf.as_slice(&lua))
|
||||
}
|
||||
Value::Function(_)
|
||||
| Value::Thread(_)
|
||||
| Value::UserData(_)
|
||||
| Value::LightUserData(_)
|
||||
| Value::Error(_) => {
|
||||
| Value::Error(_)
|
||||
| Value::Other(_) => {
|
||||
if self.options.deny_unsupported_types {
|
||||
let msg = format!("unsupported value type `{}`", self.value.type_name());
|
||||
Err(de::Error::custom(msg))
|
||||
@@ -424,7 +441,7 @@ impl<'de> de::SeqAccess<'de> for SeqDeserializer<'_> {
|
||||
|
||||
#[cfg(feature = "luau")]
|
||||
struct VecDeserializer {
|
||||
vec: crate::types::Vector,
|
||||
vec: crate::Vector,
|
||||
next: usize,
|
||||
options: Options,
|
||||
visited: Rc<RefCell<FxHashSet<*const c_void>>>,
|
||||
@@ -450,7 +467,7 @@ impl<'de> de::SeqAccess<'de> for VecDeserializer {
|
||||
}
|
||||
|
||||
fn size_hint(&self) -> Option<usize> {
|
||||
Some(crate::types::Vector::SIZE)
|
||||
Some(crate::Vector::SIZE)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -463,7 +480,7 @@ impl<'a> MapPairs<'a> {
|
||||
pub(crate) fn new(t: &'a Table, sort_keys: bool) -> Result<Self> {
|
||||
if sort_keys {
|
||||
let mut pairs = t.pairs::<Value, Value>().collect::<Result<Vec<_>>>()?;
|
||||
pairs.sort_by(|(a, _), (b, _)| b.cmp(a)); // reverse order as we pop values from the end
|
||||
pairs.sort_by(|(a, _), (b, _)| b.sort_cmp(a)); // reverse order as we pop values from the end
|
||||
Ok(MapPairs::Vec(pairs))
|
||||
} else {
|
||||
Ok(MapPairs::Iter(t.pairs::<Value, Value>()))
|
||||
@@ -504,7 +521,7 @@ struct MapDeserializer<'a> {
|
||||
processed: usize,
|
||||
}
|
||||
|
||||
impl<'a> MapDeserializer<'a> {
|
||||
impl MapDeserializer<'_> {
|
||||
fn next_key_deserializer(&mut self) -> Result<Option<Deserializer>> {
|
||||
loop {
|
||||
match self.pairs.next() {
|
||||
|
||||
+1
-19
@@ -13,12 +13,10 @@ use crate::util::check_stack;
|
||||
use crate::value::Value;
|
||||
|
||||
/// Trait for serializing/deserializing Lua values using Serde.
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "serialize")))]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "serde")))]
|
||||
pub trait LuaSerdeExt: Sealed {
|
||||
/// A special value (lightuserdata) to encode/decode optional (none) values.
|
||||
///
|
||||
/// Requires `feature = "serialize"`
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
@@ -42,8 +40,6 @@ pub trait LuaSerdeExt: Sealed {
|
||||
/// As result, encoded Array will contain only sequence part of the table, with the same length
|
||||
/// as the `#` operator on that table.
|
||||
///
|
||||
/// Requires `feature = "serialize"`
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
@@ -71,8 +67,6 @@ pub trait LuaSerdeExt: Sealed {
|
||||
|
||||
/// Converts `T` into a [`Value`] instance.
|
||||
///
|
||||
/// Requires `feature = "serialize"`
|
||||
///
|
||||
/// [`Value`]: crate::Value
|
||||
///
|
||||
/// # Example
|
||||
@@ -104,10 +98,6 @@ pub trait LuaSerdeExt: Sealed {
|
||||
|
||||
/// Converts `T` into a [`Value`] instance with options.
|
||||
///
|
||||
/// Requires `feature = "serialize"`
|
||||
///
|
||||
/// [`Value`]: crate::Value
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
@@ -131,10 +121,6 @@ pub trait LuaSerdeExt: Sealed {
|
||||
|
||||
/// Deserializes a [`Value`] into any serde deserializable object.
|
||||
///
|
||||
/// Requires `feature = "serialize"`
|
||||
///
|
||||
/// [`Value`]: crate::Value
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
@@ -162,10 +148,6 @@ pub trait LuaSerdeExt: Sealed {
|
||||
|
||||
/// Deserializes a [`Value`] into any serde deserializable object with options.
|
||||
///
|
||||
/// Requires `feature = "serialize"`
|
||||
///
|
||||
/// [`Value`]: crate::Value
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
|
||||
+10
-7
@@ -1,10 +1,13 @@
|
||||
//! Serialize a Rust data structure into Lua value.
|
||||
|
||||
use serde::{ser, Serialize};
|
||||
|
||||
use super::LuaSerdeExt;
|
||||
use crate::error::{Error, Result};
|
||||
use crate::state::Lua;
|
||||
use crate::table::Table;
|
||||
use crate::value::{IntoLua, Value};
|
||||
use crate::traits::IntoLua;
|
||||
use crate::value::Value;
|
||||
|
||||
/// A struct for serializing Rust values into Lua values.
|
||||
#[derive(Debug)]
|
||||
@@ -253,7 +256,7 @@ impl<'a> ser::Serializer for Serializer<'a> {
|
||||
fn serialize_seq(self, len: Option<usize>) -> Result<Self::SerializeSeq> {
|
||||
let table = self.lua.create_table_with_capacity(len.unwrap_or(0), 0)?;
|
||||
if self.options.set_array_metatable {
|
||||
table.set_metatable(Some(self.lua.array_metatable()));
|
||||
table.set_metatable(Some(self.lua.array_metatable()))?;
|
||||
}
|
||||
Ok(SerializeSeq::new(self.lua, table, self.options))
|
||||
}
|
||||
@@ -266,7 +269,7 @@ impl<'a> ser::Serializer for Serializer<'a> {
|
||||
#[inline]
|
||||
fn serialize_tuple_struct(self, name: &'static str, len: usize) -> Result<Self::SerializeTupleStruct> {
|
||||
#[cfg(feature = "luau")]
|
||||
if name == "Vector" && len == crate::types::Vector::SIZE {
|
||||
if name == "Vector" && len == crate::Vector::SIZE {
|
||||
return Ok(SerializeSeq::new_vector(self.lua, self.options));
|
||||
}
|
||||
_ = name;
|
||||
@@ -340,7 +343,7 @@ impl<'a> ser::Serializer for Serializer<'a> {
|
||||
pub struct SerializeSeq<'a> {
|
||||
lua: &'a Lua,
|
||||
#[cfg(feature = "luau")]
|
||||
vector: Option<crate::types::Vector>,
|
||||
vector: Option<crate::Vector>,
|
||||
table: Option<Table>,
|
||||
next: usize,
|
||||
options: Options,
|
||||
@@ -362,7 +365,7 @@ impl<'a> SerializeSeq<'a> {
|
||||
const fn new_vector(lua: &'a Lua, options: Options) -> Self {
|
||||
Self {
|
||||
lua,
|
||||
vector: Some(crate::types::Vector::zero()),
|
||||
vector: Some(crate::Vector::zero()),
|
||||
table: None,
|
||||
next: 0,
|
||||
options,
|
||||
@@ -526,8 +529,8 @@ impl ser::SerializeStruct for SerializeStruct<'_> {
|
||||
fn end(self) -> Result<Value> {
|
||||
match self.inner {
|
||||
Some(table @ Value::Table(_)) => Ok(table),
|
||||
Some(value) if self.options.detect_serde_json_arbitrary_precision => {
|
||||
let number_s = value.as_str().expect("not an arbitrary precision number");
|
||||
Some(value @ Value::String(_)) if self.options.detect_serde_json_arbitrary_precision => {
|
||||
let number_s = value.to_string()?;
|
||||
if number_s.contains(['.', 'e', 'E']) {
|
||||
if let Ok(number) = number_s.parse().map(Value::Number) {
|
||||
return Ok(number);
|
||||
|
||||
+651
-355
File diff suppressed because it is too large
Load Diff
+58
-14
@@ -13,6 +13,7 @@ use crate::error::Result;
|
||||
use crate::state::RawLua;
|
||||
use crate::stdlib::StdLib;
|
||||
use crate::types::{AppData, ReentrantMutex, XRc};
|
||||
use crate::userdata::RawUserDataRegistry;
|
||||
use crate::util::{get_internal_metatable, push_internal_userdata, TypeKey, WrappedFailure};
|
||||
|
||||
#[cfg(any(feature = "luau", doc))]
|
||||
@@ -26,8 +27,8 @@ use super::{Lua, WeakLua};
|
||||
// Unique key to store `ExtraData` in the registry
|
||||
static EXTRA_REGISTRY_KEY: u8 = 0;
|
||||
|
||||
const WRAPPED_FAILURE_POOL_SIZE: usize = 64;
|
||||
const REF_STACK_RESERVE: c_int = 1;
|
||||
const WRAPPED_FAILURE_POOL_DEFAULT_CAPACITY: usize = 64;
|
||||
const REF_STACK_RESERVE: c_int = 3;
|
||||
|
||||
/// Data associated with the Lua state.
|
||||
pub(crate) struct ExtraData {
|
||||
@@ -35,15 +36,17 @@ pub(crate) struct ExtraData {
|
||||
pub(super) weak: MaybeUninit<WeakLua>,
|
||||
pub(super) owned: bool,
|
||||
|
||||
pub(super) registered_userdata: FxHashMap<TypeId, c_int>,
|
||||
pub(super) pending_userdata_reg: FxHashMap<TypeId, RawUserDataRegistry>,
|
||||
pub(super) registered_userdata_t: FxHashMap<TypeId, c_int>,
|
||||
pub(super) registered_userdata_mt: FxHashMap<*const c_void, Option<TypeId>>,
|
||||
pub(super) last_checked_userdata_mt: (*const c_void, Option<TypeId>),
|
||||
|
||||
// When Lua instance dropped, setting `None` would prevent collecting `RegistryKey`s
|
||||
pub(super) registry_unref_list: Arc<Mutex<Option<Vec<c_int>>>>,
|
||||
|
||||
// Container to store arbitrary data (extensions)
|
||||
// Containers to store arbitrary data (extensions)
|
||||
pub(super) app_data: AppData,
|
||||
pub(super) app_data_priv: AppData,
|
||||
|
||||
pub(super) safe: bool,
|
||||
pub(super) libs: StdLib,
|
||||
@@ -58,6 +61,7 @@ pub(crate) struct ExtraData {
|
||||
|
||||
// Pool of `WrappedFailure` enums in the ref thread (as userdata)
|
||||
pub(super) wrapped_failure_pool: Vec<c_int>,
|
||||
pub(super) wrapped_failure_top: usize,
|
||||
// Pool of `Thread`s (coroutines) for async execution
|
||||
#[cfg(feature = "async")]
|
||||
pub(super) thread_pool: Vec<c_int>,
|
||||
@@ -72,14 +76,20 @@ pub(crate) struct ExtraData {
|
||||
#[cfg(not(feature = "luau"))]
|
||||
pub(super) hook_callback: Option<crate::types::HookCallback>,
|
||||
#[cfg(not(feature = "luau"))]
|
||||
pub(super) hook_thread: *mut ffi::lua_State,
|
||||
pub(super) hook_triggers: crate::debug::HookTriggers,
|
||||
#[cfg(feature = "lua54")]
|
||||
pub(super) warn_callback: Option<crate::types::WarnCallback>,
|
||||
#[cfg(feature = "luau")]
|
||||
pub(super) interrupt_callback: Option<crate::types::InterruptCallback>,
|
||||
#[cfg(feature = "luau")]
|
||||
pub(super) thread_creation_callback: Option<crate::types::ThreadCreationCallback>,
|
||||
#[cfg(feature = "luau")]
|
||||
pub(super) thread_collection_callback: Option<crate::types::ThreadCollectionCallback>,
|
||||
|
||||
#[cfg(feature = "luau")]
|
||||
pub(super) sandboxed: bool,
|
||||
pub(crate) running_gc: bool,
|
||||
#[cfg(feature = "luau")]
|
||||
pub(crate) sandboxed: bool,
|
||||
#[cfg(feature = "luau")]
|
||||
pub(super) compiler: Option<Compiler>,
|
||||
#[cfg(feature = "luau-jit")]
|
||||
@@ -144,11 +154,13 @@ impl ExtraData {
|
||||
lua: MaybeUninit::uninit(),
|
||||
weak: MaybeUninit::uninit(),
|
||||
owned,
|
||||
registered_userdata: FxHashMap::default(),
|
||||
pending_userdata_reg: FxHashMap::default(),
|
||||
registered_userdata_t: FxHashMap::default(),
|
||||
registered_userdata_mt: FxHashMap::default(),
|
||||
last_checked_userdata_mt: (ptr::null(), None),
|
||||
registry_unref_list: Arc::new(Mutex::new(Some(Vec::new()))),
|
||||
app_data: AppData::default(),
|
||||
app_data_priv: AppData::default(),
|
||||
safe: false,
|
||||
libs: StdLib::NONE,
|
||||
skip_memory_check: false,
|
||||
@@ -157,7 +169,8 @@ impl ExtraData {
|
||||
ref_stack_size: ffi::LUA_MINSTACK - REF_STACK_RESERVE,
|
||||
ref_stack_top: ffi::lua_gettop(ref_thread),
|
||||
ref_free: Vec::new(),
|
||||
wrapped_failure_pool: Vec::with_capacity(WRAPPED_FAILURE_POOL_SIZE),
|
||||
wrapped_failure_pool: Vec::with_capacity(WRAPPED_FAILURE_POOL_DEFAULT_CAPACITY),
|
||||
wrapped_failure_top: 0,
|
||||
#[cfg(feature = "async")]
|
||||
thread_pool: Vec::new(),
|
||||
wrapped_failure_mt_ptr,
|
||||
@@ -166,17 +179,23 @@ impl ExtraData {
|
||||
#[cfg(not(feature = "luau"))]
|
||||
hook_callback: None,
|
||||
#[cfg(not(feature = "luau"))]
|
||||
hook_thread: ptr::null_mut(),
|
||||
hook_triggers: Default::default(),
|
||||
#[cfg(feature = "lua54")]
|
||||
warn_callback: None,
|
||||
#[cfg(feature = "luau")]
|
||||
interrupt_callback: None,
|
||||
#[cfg(feature = "luau")]
|
||||
thread_creation_callback: None,
|
||||
#[cfg(feature = "luau")]
|
||||
thread_collection_callback: None,
|
||||
#[cfg(feature = "luau")]
|
||||
sandboxed: false,
|
||||
#[cfg(feature = "luau")]
|
||||
compiler: None,
|
||||
#[cfg(feature = "luau-jit")]
|
||||
enable_jit: true,
|
||||
#[cfg(feature = "luau")]
|
||||
running_gc: false,
|
||||
}));
|
||||
|
||||
// Store it in the registry
|
||||
@@ -190,13 +209,10 @@ impl ExtraData {
|
||||
raw: XRc::clone(raw),
|
||||
collect_garbage: false,
|
||||
});
|
||||
if self.owned {
|
||||
XRc::decrement_strong_count(XRc::as_ptr(raw));
|
||||
}
|
||||
self.weak.write(WeakLua(XRc::downgrade(raw)));
|
||||
}
|
||||
|
||||
pub(super) unsafe fn get(state: *mut ffi::lua_State) -> *mut Self {
|
||||
pub(crate) unsafe fn get(state: *mut ffi::lua_State) -> *mut Self {
|
||||
#[cfg(feature = "luau")]
|
||||
if cfg!(not(feature = "module")) {
|
||||
// In the main app we can use `lua_callbacks` to access ExtraData
|
||||
@@ -235,7 +251,7 @@ impl ExtraData {
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub(super) unsafe fn raw_lua(&self) -> &RawLua {
|
||||
pub(crate) unsafe fn raw_lua(&self) -> &RawLua {
|
||||
&*self.lua.assume_init_ref().raw.data_ptr()
|
||||
}
|
||||
|
||||
@@ -243,4 +259,32 @@ impl ExtraData {
|
||||
pub(super) unsafe fn weak(&self) -> &WeakLua {
|
||||
self.weak.assume_init_ref()
|
||||
}
|
||||
|
||||
/// Pops a reference from top of the auxiliary stack and move it to a first free slot.
|
||||
pub(super) unsafe fn ref_stack_pop(&mut self) -> c_int {
|
||||
if let Some(free) = self.ref_free.pop() {
|
||||
ffi::lua_replace(self.ref_thread, free);
|
||||
return free;
|
||||
}
|
||||
|
||||
// Try to grow max stack size
|
||||
if self.ref_stack_top >= self.ref_stack_size {
|
||||
let mut inc = self.ref_stack_size; // Try to double stack size
|
||||
while inc > 0 && ffi::lua_checkstack(self.ref_thread, inc + REF_STACK_RESERVE) == 0 {
|
||||
inc /= 2;
|
||||
}
|
||||
if inc == 0 {
|
||||
// Pop item on top of the stack to avoid stack leaking and successfully run destructors
|
||||
// during unwinding.
|
||||
ffi::lua_pop(self.ref_thread, 1);
|
||||
let top = self.ref_stack_top;
|
||||
// It is a user error to create too many references to exhaust the Lua max stack size
|
||||
// for the ref thread.
|
||||
panic!("cannot create a Lua reference, out of auxiliary stack space (used {top} slots)");
|
||||
}
|
||||
self.ref_stack_size += inc;
|
||||
}
|
||||
self.ref_stack_top += 1;
|
||||
self.ref_stack_top
|
||||
}
|
||||
}
|
||||
|
||||
+416
-268
File diff suppressed because it is too large
Load Diff
+41
-69
@@ -7,28 +7,27 @@ use crate::error::{Error, Result};
|
||||
use crate::state::{ExtraData, RawLua};
|
||||
use crate::util::{self, get_internal_metatable, WrappedFailure};
|
||||
|
||||
const WRAPPED_FAILURE_POOL_SIZE: usize = 64;
|
||||
|
||||
pub(super) struct StateGuard<'a>(&'a RawLua, *mut ffi::lua_State);
|
||||
struct StateGuard<'a>(&'a RawLua, *mut ffi::lua_State);
|
||||
|
||||
impl<'a> StateGuard<'a> {
|
||||
pub(super) fn new(inner: &'a RawLua, mut state: *mut ffi::lua_State) -> Self {
|
||||
fn new(inner: &'a RawLua, mut state: *mut ffi::lua_State) -> Self {
|
||||
state = inner.state.replace(state);
|
||||
Self(inner, state)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Drop for StateGuard<'a> {
|
||||
impl Drop for StateGuard<'_> {
|
||||
fn drop(&mut self) {
|
||||
self.0.state.set(self.1);
|
||||
}
|
||||
}
|
||||
|
||||
// An optimized version of `callback_error` that does not allocate `WrappedFailure` userdata
|
||||
// and instead reuses unsed values from previous calls (or allocates new).
|
||||
pub(super) unsafe fn callback_error_ext<F, R>(
|
||||
// and instead reuses unused values from previous calls (or allocates new).
|
||||
pub(crate) unsafe fn callback_error_ext<F, R>(
|
||||
state: *mut ffi::lua_State,
|
||||
mut extra: *mut ExtraData,
|
||||
wrap_error: bool,
|
||||
f: F,
|
||||
) -> R
|
||||
where
|
||||
@@ -42,26 +41,27 @@ where
|
||||
|
||||
enum PreallocatedFailure {
|
||||
New(*mut WrappedFailure),
|
||||
Existing(i32),
|
||||
Reserved,
|
||||
}
|
||||
|
||||
impl PreallocatedFailure {
|
||||
unsafe fn reserve(state: *mut ffi::lua_State, extra: *mut ExtraData) -> Self {
|
||||
match (*extra).wrapped_failure_pool.pop() {
|
||||
Some(index) => PreallocatedFailure::Existing(index),
|
||||
None => {
|
||||
// We need to check stack for Luau in case when callback is called from interrupt
|
||||
// See https://github.com/Roblox/luau/issues/446 and mlua #142 and #153
|
||||
#[cfg(feature = "luau")]
|
||||
ffi::lua_rawcheckstack(state, 2);
|
||||
// Place it to the beginning of the stack
|
||||
let ud = WrappedFailure::new_userdata(state);
|
||||
ffi::lua_insert(state, 1);
|
||||
PreallocatedFailure::New(ud)
|
||||
}
|
||||
if (*extra).wrapped_failure_top > 0 {
|
||||
(*extra).wrapped_failure_top -= 1;
|
||||
return PreallocatedFailure::Reserved;
|
||||
}
|
||||
|
||||
// We need to check stack for Luau in case when callback is called from interrupt
|
||||
// See https://github.com/luau-lang/luau/issues/446 and mlua #142 and #153
|
||||
#[cfg(feature = "luau")]
|
||||
ffi::lua_rawcheckstack(state, 2);
|
||||
// Place it to the beginning of the stack
|
||||
let ud = WrappedFailure::new_userdata(state);
|
||||
ffi::lua_insert(state, 1);
|
||||
PreallocatedFailure::New(ud)
|
||||
}
|
||||
|
||||
#[cold]
|
||||
unsafe fn r#use(&self, state: *mut ffi::lua_State, extra: *mut ExtraData) -> *mut WrappedFailure {
|
||||
let ref_thread = (*extra).ref_thread;
|
||||
match *self {
|
||||
@@ -69,12 +69,12 @@ where
|
||||
ffi::lua_settop(state, 1);
|
||||
ud
|
||||
}
|
||||
PreallocatedFailure::Existing(index) => {
|
||||
PreallocatedFailure::Reserved => {
|
||||
let index = (*extra).wrapped_failure_pool.pop().unwrap();
|
||||
ffi::lua_settop(state, 0);
|
||||
#[cfg(feature = "luau")]
|
||||
ffi::lua_rawcheckstack(state, 2);
|
||||
ffi::lua_pushvalue(ref_thread, index);
|
||||
ffi::lua_xmove(ref_thread, state, 1);
|
||||
ffi::lua_xpush(ref_thread, state, index);
|
||||
ffi::lua_pushnil(ref_thread);
|
||||
ffi::lua_replace(ref_thread, index);
|
||||
(*extra).ref_free.push(index);
|
||||
@@ -87,24 +87,13 @@ where
|
||||
let ref_thread = (*extra).ref_thread;
|
||||
match self {
|
||||
PreallocatedFailure::New(_) => {
|
||||
if (*extra).wrapped_failure_pool.len() < WRAPPED_FAILURE_POOL_SIZE {
|
||||
ffi::lua_rotate(state, 1, -1);
|
||||
ffi::lua_xmove(state, ref_thread, 1);
|
||||
let index = ref_stack_pop(extra);
|
||||
(*extra).wrapped_failure_pool.push(index);
|
||||
} else {
|
||||
ffi::lua_remove(state, 1);
|
||||
}
|
||||
}
|
||||
PreallocatedFailure::Existing(index) => {
|
||||
if (*extra).wrapped_failure_pool.len() < WRAPPED_FAILURE_POOL_SIZE {
|
||||
(*extra).wrapped_failure_pool.push(index);
|
||||
} else {
|
||||
ffi::lua_pushnil(ref_thread);
|
||||
ffi::lua_replace(ref_thread, index);
|
||||
(*extra).ref_free.push(index);
|
||||
}
|
||||
ffi::lua_rotate(state, 1, -1);
|
||||
ffi::lua_xmove(state, ref_thread, 1);
|
||||
let index = (*extra).ref_stack_pop();
|
||||
(*extra).wrapped_failure_pool.push(index);
|
||||
(*extra).wrapped_failure_top += 1;
|
||||
}
|
||||
PreallocatedFailure::Reserved => (*extra).wrapped_failure_top += 1,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -113,7 +102,11 @@ where
|
||||
// to store a wrapped failure (error or panic) *before* we proceed.
|
||||
let prealloc_failure = PreallocatedFailure::reserve(state, extra);
|
||||
|
||||
match catch_unwind(AssertUnwindSafe(|| f(extra, nargs))) {
|
||||
match catch_unwind(AssertUnwindSafe(|| {
|
||||
let rawlua = (*extra).raw_lua();
|
||||
let _guard = StateGuard::new(rawlua, state);
|
||||
f(extra, nargs)
|
||||
})) {
|
||||
Ok(Ok(r)) => {
|
||||
// Return unused `WrappedFailure` to the pool
|
||||
prealloc_failure.release(state, extra);
|
||||
@@ -122,6 +115,13 @@ where
|
||||
Ok(Err(err)) => {
|
||||
let wrapped_error = prealloc_failure.r#use(state, extra);
|
||||
|
||||
if !wrap_error {
|
||||
ptr::write(wrapped_error, WrappedFailure::Error(err));
|
||||
get_internal_metatable::<WrappedFailure>(state);
|
||||
ffi::lua_setmetatable(state, -2);
|
||||
ffi::lua_error(state)
|
||||
}
|
||||
|
||||
// Build `CallbackError` with traceback
|
||||
let traceback = if ffi::lua_checkstack(state, ffi::LUA_TRACEBACK_STACK) != 0 {
|
||||
ffi::luaL_traceback(state, state, ptr::null(), 0);
|
||||
@@ -150,31 +150,3 @@ where
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) unsafe fn ref_stack_pop(extra: *mut ExtraData) -> c_int {
|
||||
let extra = &mut *extra;
|
||||
if let Some(free) = extra.ref_free.pop() {
|
||||
ffi::lua_replace(extra.ref_thread, free);
|
||||
return free;
|
||||
}
|
||||
|
||||
// Try to grow max stack size
|
||||
if extra.ref_stack_top >= extra.ref_stack_size {
|
||||
let mut inc = extra.ref_stack_size; // Try to double stack size
|
||||
while inc > 0 && ffi::lua_checkstack(extra.ref_thread, inc) == 0 {
|
||||
inc /= 2;
|
||||
}
|
||||
if inc == 0 {
|
||||
// Pop item on top of the stack to avoid stack leaking and successfully run destructors
|
||||
// during unwinding.
|
||||
ffi::lua_pop(extra.ref_thread, 1);
|
||||
let top = extra.ref_stack_top;
|
||||
// It is a user error to create enough references to exhaust the Lua max stack size for
|
||||
// the ref thread.
|
||||
panic!("cannot create a Lua reference, out of auxiliary stack space (used {top} slots)");
|
||||
}
|
||||
extra.ref_stack_size += inc;
|
||||
}
|
||||
extra.ref_stack_top += 1;
|
||||
extra.ref_stack_top
|
||||
}
|
||||
|
||||
+18
-12
@@ -6,9 +6,11 @@ pub struct StdLib(u32);
|
||||
|
||||
impl StdLib {
|
||||
/// [`coroutine`](https://www.lua.org/manual/5.4/manual.html#6.2) library
|
||||
///
|
||||
/// Requires `feature = "lua54/lua53/lua52/luau"`
|
||||
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52", feature = "luau"))]
|
||||
#[cfg_attr(
|
||||
docsrs,
|
||||
doc(cfg(any(feature = "lua54", feature = "lua53", feature = "lua52", feature = "luau")))
|
||||
)]
|
||||
pub const COROUTINE: StdLib = StdLib(1);
|
||||
|
||||
/// [`table`](https://www.lua.org/manual/5.4/manual.html#6.6) library
|
||||
@@ -26,38 +28,42 @@ impl StdLib {
|
||||
pub const STRING: StdLib = StdLib(1 << 4);
|
||||
|
||||
/// [`utf8`](https://www.lua.org/manual/5.4/manual.html#6.5) library
|
||||
///
|
||||
/// Requires `feature = "lua54/lua53/luau"`
|
||||
#[cfg(any(feature = "lua54", feature = "lua53", feature = "luau"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(any(feature = "lua54", feature = "lua53", feature = "luau"))))]
|
||||
pub const UTF8: StdLib = StdLib(1 << 5);
|
||||
|
||||
/// [`bit`](https://www.lua.org/manual/5.2/manual.html#6.7) library
|
||||
///
|
||||
/// Requires `feature = "lua52/luajit/luau"`
|
||||
#[cfg(any(feature = "lua52", feature = "luajit", feature = "luau", doc))]
|
||||
#[cfg_attr(
|
||||
docsrs,
|
||||
doc(cfg(any(feature = "lua52", feature = "luajit", feature = "luau")))
|
||||
)]
|
||||
pub const BIT: StdLib = StdLib(1 << 6);
|
||||
|
||||
/// [`math`](https://www.lua.org/manual/5.4/manual.html#6.7) library
|
||||
pub const MATH: StdLib = StdLib(1 << 7);
|
||||
|
||||
/// [`package`](https://www.lua.org/manual/5.4/manual.html#6.3) library
|
||||
#[cfg(not(feature = "luau"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(not(feature = "luau"))))]
|
||||
pub const PACKAGE: StdLib = StdLib(1 << 8);
|
||||
|
||||
/// [`buffer`](https://luau-lang.org/library#buffer-library) library
|
||||
/// [`buffer`](https://luau.org/library#buffer-library) library
|
||||
#[cfg(any(feature = "luau", doc))]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
|
||||
pub const BUFFER: StdLib = StdLib(1 << 9);
|
||||
|
||||
/// [`vector`](https://luau.org/library#vector-library) library
|
||||
#[cfg(any(feature = "luau", doc))]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
|
||||
pub const VECTOR: StdLib = StdLib(1 << 10);
|
||||
|
||||
/// [`jit`](http://luajit.org/ext_jit.html) library
|
||||
///
|
||||
/// Requires `feature = "luajit"`
|
||||
#[cfg(any(feature = "luajit", doc))]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "luajit")))]
|
||||
pub const JIT: StdLib = StdLib(1 << 9);
|
||||
pub const JIT: StdLib = StdLib(1 << 11);
|
||||
|
||||
/// (**unsafe**) [`ffi`](http://luajit.org/ext_ffi.html) library
|
||||
///
|
||||
/// Requires `feature = "luajit"`
|
||||
#[cfg(any(feature = "luajit", doc))]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "luajit")))]
|
||||
pub const FFI: StdLib = StdLib(1 << 30);
|
||||
|
||||
+164
-72
@@ -1,20 +1,22 @@
|
||||
use std::borrow::Borrow;
|
||||
use std::borrow::{Borrow, Cow};
|
||||
use std::hash::{Hash, Hasher};
|
||||
use std::ops::Deref;
|
||||
use std::os::raw::c_void;
|
||||
use std::os::raw::{c_int, c_void};
|
||||
use std::string::String as StdString;
|
||||
use std::{cmp, fmt, slice, str};
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
use crate::error::{Error, Result};
|
||||
use crate::state::Lua;
|
||||
use crate::traits::IntoLua;
|
||||
use crate::types::{LuaType, ValueRef};
|
||||
use crate::value::Value;
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
use {
|
||||
serde::ser::{Serialize, Serializer},
|
||||
std::result::Result as StdResult,
|
||||
};
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
use crate::state::Lua;
|
||||
use crate::types::ValueRef;
|
||||
|
||||
/// Handle to an internal Lua string.
|
||||
///
|
||||
/// Unlike Rust strings, Lua strings may not be valid UTF-8.
|
||||
@@ -41,21 +43,19 @@ impl String {
|
||||
/// # }
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn to_str(&self) -> Result<BorrowedStr> {
|
||||
let BorrowedBytes(bytes, guard) = self.as_bytes();
|
||||
let s = str::from_utf8(bytes).map_err(|e| Error::FromLuaConversionError {
|
||||
from: "string",
|
||||
to: "&str",
|
||||
message: Some(e.to_string()),
|
||||
})?;
|
||||
Ok(BorrowedStr(s, guard))
|
||||
pub fn to_str(&self) -> Result<BorrowedStr<'_>> {
|
||||
BorrowedStr::try_from(self)
|
||||
}
|
||||
|
||||
/// Converts this string to a [`StdString`].
|
||||
///
|
||||
/// Any non-Unicode sequences are replaced with [`U+FFFD REPLACEMENT CHARACTER`][U+FFFD].
|
||||
///
|
||||
/// This method returns [`StdString`] instead of [`Cow<'_, str>`] because lifetime cannot be
|
||||
/// bound to a weak Lua object.
|
||||
///
|
||||
/// [U+FFFD]: std::char::REPLACEMENT_CHARACTER
|
||||
/// [`Cow<'_, str>`]: std::borrow::Cow
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
@@ -74,9 +74,19 @@ impl String {
|
||||
StdString::from_utf8_lossy(&self.as_bytes()).into_owned()
|
||||
}
|
||||
|
||||
/// Returns an object that implements [`Display`] for safely printing a Lua [`String`] that may
|
||||
/// contain non-Unicode data.
|
||||
///
|
||||
/// This may perform lossy conversion.
|
||||
///
|
||||
/// [`Display`]: fmt::Display
|
||||
pub fn display(&self) -> impl fmt::Display + '_ {
|
||||
Display(self)
|
||||
}
|
||||
|
||||
/// Get the bytes that make up this string.
|
||||
///
|
||||
/// The returned slice will not contain the terminating nul byte, but will contain any nul
|
||||
/// The returned slice will not contain the terminating null byte, but will contain any null
|
||||
/// bytes embedded into the Lua string.
|
||||
///
|
||||
/// # Examples
|
||||
@@ -92,35 +102,37 @@ impl String {
|
||||
/// # }
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn as_bytes(&self) -> BorrowedBytes {
|
||||
let (bytes, guard) = unsafe { self.to_slice() };
|
||||
BorrowedBytes(&bytes[..bytes.len() - 1], guard)
|
||||
pub fn as_bytes(&self) -> BorrowedBytes<'_> {
|
||||
BorrowedBytes::from(self)
|
||||
}
|
||||
|
||||
/// Get the bytes that make up this string, including the trailing nul byte.
|
||||
pub fn as_bytes_with_nul(&self) -> BorrowedBytes {
|
||||
let (bytes, guard) = unsafe { self.to_slice() };
|
||||
BorrowedBytes(bytes, guard)
|
||||
/// Get the bytes that make up this string, including the trailing null byte.
|
||||
pub fn as_bytes_with_nul(&self) -> BorrowedBytes<'_> {
|
||||
let BorrowedBytes { buf, borrow, _lua } = BorrowedBytes::from(self);
|
||||
// Include the trailing null byte (it's always present but excluded by default)
|
||||
let buf = unsafe { slice::from_raw_parts((*buf).as_ptr(), (*buf).len() + 1) };
|
||||
BorrowedBytes { buf, borrow, _lua }
|
||||
}
|
||||
|
||||
// Does not return the terminating null byte
|
||||
unsafe fn to_slice(&self) -> (&[u8], Lua) {
|
||||
let lua = self.0.lua.upgrade();
|
||||
let rawlua = lua.lock();
|
||||
let ref_thread = rawlua.ref_thread();
|
||||
unsafe {
|
||||
let slice = {
|
||||
let rawlua = lua.lock();
|
||||
let ref_thread = rawlua.ref_thread();
|
||||
|
||||
mlua_debug_assert!(
|
||||
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 mut size = 0;
|
||||
let data = ffi::lua_tolstring(ref_thread, self.0.index, &mut size);
|
||||
|
||||
drop(rawlua);
|
||||
(slice::from_raw_parts(data as *const u8, size + 1), lua)
|
||||
}
|
||||
slice::from_raw_parts(data as *const u8, size)
|
||||
};
|
||||
(slice, lua)
|
||||
}
|
||||
|
||||
/// Converts this string to a generic C pointer.
|
||||
@@ -143,27 +155,12 @@ impl fmt::Debug for String {
|
||||
}
|
||||
|
||||
// Format as bytes
|
||||
write!(f, "b\"")?;
|
||||
for &b in bytes {
|
||||
// https://doc.rust-lang.org/reference/tokens.html#byte-escapes
|
||||
match b {
|
||||
b'\n' => write!(f, "\\n")?,
|
||||
b'\r' => write!(f, "\\r")?,
|
||||
b'\t' => write!(f, "\\t")?,
|
||||
b'\\' | b'"' => write!(f, "\\{}", b as char)?,
|
||||
b'\0' => write!(f, "\\0")?,
|
||||
// ASCII printable
|
||||
0x20..=0x7e => write!(f, "{}", b as char)?,
|
||||
_ => write!(f, "\\x{b:02x}")?,
|
||||
}
|
||||
}
|
||||
write!(f, "\"")?;
|
||||
|
||||
Ok(())
|
||||
write!(f, "b")?;
|
||||
<bstr::BStr as fmt::Debug>::fmt(bstr::BStr::new(&bytes), f)
|
||||
}
|
||||
}
|
||||
|
||||
// Lua strings are basically &[u8] slices, so implement PartialEq for anything resembling that.
|
||||
// Lua strings are basically `&[u8]` slices, so implement `PartialEq` for anything resembling that.
|
||||
//
|
||||
// This makes our `String` comparable with `Vec<u8>`, `[u8]`, `&str` and `String`.
|
||||
//
|
||||
@@ -179,19 +176,34 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq<String> for String {
|
||||
impl PartialEq for String {
|
||||
fn eq(&self, other: &String) -> bool {
|
||||
self.as_bytes() == other.as_bytes()
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq<&String> for String {
|
||||
fn eq(&self, other: &&String) -> bool {
|
||||
self.as_bytes() == other.as_bytes()
|
||||
impl Eq for String {}
|
||||
|
||||
impl<T> PartialOrd<T> for String
|
||||
where
|
||||
T: AsRef<[u8]> + ?Sized,
|
||||
{
|
||||
fn partial_cmp(&self, other: &T) -> Option<cmp::Ordering> {
|
||||
self.as_bytes().partial_cmp(&other.as_ref())
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for String {}
|
||||
impl PartialOrd for String {
|
||||
fn partial_cmp(&self, other: &String) -> Option<cmp::Ordering> {
|
||||
Some(self.cmp(other))
|
||||
}
|
||||
}
|
||||
|
||||
impl Ord for String {
|
||||
fn cmp(&self, other: &String) -> cmp::Ordering {
|
||||
self.as_bytes().cmp(&other.as_bytes())
|
||||
}
|
||||
}
|
||||
|
||||
impl Hash for String {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
@@ -199,7 +211,7 @@ impl Hash for String {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
#[cfg(feature = "serde")]
|
||||
impl Serialize for String {
|
||||
fn serialize<S>(&self, serializer: S) -> StdResult<S::Ok, S::Error>
|
||||
where
|
||||
@@ -212,41 +224,55 @@ impl Serialize for String {
|
||||
}
|
||||
}
|
||||
|
||||
struct Display<'a>(&'a String);
|
||||
|
||||
impl fmt::Display for Display<'_> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
let bytes = self.0.as_bytes();
|
||||
<bstr::BStr as fmt::Display>::fmt(bstr::BStr::new(&bytes), f)
|
||||
}
|
||||
}
|
||||
|
||||
/// A borrowed string (`&str`) that holds a strong reference to the Lua state.
|
||||
pub struct BorrowedStr<'a>(&'a str, #[allow(unused)] Lua);
|
||||
pub struct BorrowedStr<'a> {
|
||||
// `buf` points to a readonly memory managed by Lua
|
||||
pub(crate) buf: &'a str,
|
||||
pub(crate) borrow: Cow<'a, String>,
|
||||
pub(crate) _lua: Lua,
|
||||
}
|
||||
|
||||
impl Deref for BorrowedStr<'_> {
|
||||
type Target = str;
|
||||
|
||||
#[inline(always)]
|
||||
fn deref(&self) -> &str {
|
||||
self.0
|
||||
self.buf
|
||||
}
|
||||
}
|
||||
|
||||
impl Borrow<str> for BorrowedStr<'_> {
|
||||
#[inline(always)]
|
||||
fn borrow(&self) -> &str {
|
||||
self.0
|
||||
self.buf
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<str> for BorrowedStr<'_> {
|
||||
#[inline(always)]
|
||||
fn as_ref(&self) -> &str {
|
||||
self.0
|
||||
self.buf
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for BorrowedStr<'_> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
self.0.fmt(f)
|
||||
self.buf.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for BorrowedStr<'_> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
self.0.fmt(f)
|
||||
self.buf.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -255,48 +281,76 @@ where
|
||||
T: AsRef<str>,
|
||||
{
|
||||
fn eq(&self, other: &T) -> bool {
|
||||
self.0 == other.as_ref()
|
||||
self.buf == other.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for BorrowedStr<'_> {}
|
||||
|
||||
impl<T> PartialOrd<T> for BorrowedStr<'_>
|
||||
where
|
||||
T: AsRef<str>,
|
||||
{
|
||||
fn partial_cmp(&self, other: &T) -> Option<cmp::Ordering> {
|
||||
self.0.partial_cmp(other.as_ref())
|
||||
self.buf.partial_cmp(other.as_ref())
|
||||
}
|
||||
}
|
||||
|
||||
impl Ord for BorrowedStr<'_> {
|
||||
fn cmp(&self, other: &Self) -> cmp::Ordering {
|
||||
self.buf.cmp(other.buf)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> TryFrom<&'a String> for BorrowedStr<'a> {
|
||||
type Error = Error;
|
||||
|
||||
#[inline]
|
||||
fn try_from(value: &'a String) -> Result<Self> {
|
||||
let BorrowedBytes { buf, borrow, _lua } = BorrowedBytes::from(value);
|
||||
let buf = str::from_utf8(buf).map_err(|e| Error::FromLuaConversionError {
|
||||
from: "string",
|
||||
to: "&str".to_string(),
|
||||
message: Some(e.to_string()),
|
||||
})?;
|
||||
Ok(Self { buf, borrow, _lua })
|
||||
}
|
||||
}
|
||||
|
||||
/// A borrowed byte slice (`&[u8]`) that holds a strong reference to the Lua state.
|
||||
pub struct BorrowedBytes<'a>(&'a [u8], #[allow(unused)] Lua);
|
||||
pub struct BorrowedBytes<'a> {
|
||||
// `buf` points to a readonly memory managed by Lua
|
||||
pub(crate) buf: &'a [u8],
|
||||
pub(crate) borrow: Cow<'a, String>,
|
||||
pub(crate) _lua: Lua,
|
||||
}
|
||||
|
||||
impl Deref for BorrowedBytes<'_> {
|
||||
type Target = [u8];
|
||||
|
||||
#[inline(always)]
|
||||
fn deref(&self) -> &[u8] {
|
||||
self.0
|
||||
self.buf
|
||||
}
|
||||
}
|
||||
|
||||
impl Borrow<[u8]> for BorrowedBytes<'_> {
|
||||
#[inline(always)]
|
||||
fn borrow(&self) -> &[u8] {
|
||||
self.0
|
||||
self.buf
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<[u8]> for BorrowedBytes<'_> {
|
||||
#[inline(always)]
|
||||
fn as_ref(&self) -> &[u8] {
|
||||
self.0
|
||||
self.buf
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for BorrowedBytes<'_> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
self.0.fmt(f)
|
||||
self.buf.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -305,28 +359,66 @@ where
|
||||
T: AsRef<[u8]>,
|
||||
{
|
||||
fn eq(&self, other: &T) -> bool {
|
||||
self.0 == other.as_ref()
|
||||
self.buf == other.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for BorrowedBytes<'_> {}
|
||||
|
||||
impl<T> PartialOrd<T> for BorrowedBytes<'_>
|
||||
where
|
||||
T: AsRef<[u8]>,
|
||||
{
|
||||
fn partial_cmp(&self, other: &T) -> Option<cmp::Ordering> {
|
||||
self.0.partial_cmp(other.as_ref())
|
||||
self.buf.partial_cmp(other.as_ref())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> IntoIterator for BorrowedBytes<'a> {
|
||||
impl Ord for BorrowedBytes<'_> {
|
||||
fn cmp(&self, other: &Self) -> cmp::Ordering {
|
||||
self.buf.cmp(other.buf)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> IntoIterator for &'a BorrowedBytes<'_> {
|
||||
type Item = &'a u8;
|
||||
type IntoIter = slice::Iter<'a, u8>;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.0.iter()
|
||||
self.iter()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a String> for BorrowedBytes<'a> {
|
||||
#[inline]
|
||||
fn from(value: &'a String) -> Self {
|
||||
let (buf, _lua) = unsafe { value.to_slice() };
|
||||
let borrow = Cow::Borrowed(value);
|
||||
Self { buf, borrow, _lua }
|
||||
}
|
||||
}
|
||||
|
||||
struct WrappedString<T: AsRef<[u8]>>(T);
|
||||
|
||||
impl String {
|
||||
/// Wraps bytes, returning an opaque type that implements [`IntoLua`] trait.
|
||||
///
|
||||
/// This function uses [`Lua::create_string`] under the hood.
|
||||
pub fn wrap(data: impl AsRef<[u8]>) -> impl IntoLua {
|
||||
WrappedString(data)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: AsRef<[u8]>> IntoLua for WrappedString<T> {
|
||||
fn into_lua(self, lua: &Lua) -> Result<Value> {
|
||||
lua.create_string(self.0).map(Value::String)
|
||||
}
|
||||
}
|
||||
|
||||
impl LuaType for String {
|
||||
const TYPE_ID: c_int = ffi::LUA_TSTRING;
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod assertions {
|
||||
use super::*;
|
||||
|
||||
+143
-140
@@ -1,29 +1,29 @@
|
||||
use std::collections::HashSet;
|
||||
use std::fmt;
|
||||
use std::marker::PhantomData;
|
||||
use std::os::raw::c_void;
|
||||
use std::os::raw::{c_int, c_void};
|
||||
use std::string::String as StdString;
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
use crate::error::{Error, Result};
|
||||
use crate::function::Function;
|
||||
use crate::state::{LuaGuard, RawLua};
|
||||
use crate::traits::{FromLua, FromLuaMulti, IntoLua, IntoLuaMulti, ObjectLike};
|
||||
use crate::types::{Integer, LuaType, ValueRef};
|
||||
use crate::util::{assert_stack, check_stack, get_metatable_ptr, StackGuard};
|
||||
use crate::value::{Nil, Value};
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
use crate::function::AsyncCallFuture;
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
use {
|
||||
rustc_hash::FxHashSet,
|
||||
serde::ser::{Serialize, SerializeMap, SerializeSeq, Serializer},
|
||||
std::{cell::RefCell, rc::Rc, result::Result as StdResult},
|
||||
};
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
use crate::function::Function;
|
||||
use crate::state::{LuaGuard, RawLua};
|
||||
use crate::traits::ObjectLike;
|
||||
use crate::types::{Integer, ValueRef};
|
||||
use crate::util::{assert_stack, check_stack, StackGuard};
|
||||
use crate::value::{FromLua, FromLuaMulti, IntoLua, IntoLuaMulti, Nil, Value};
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
use futures_util::future::{self, Either, Future};
|
||||
|
||||
/// Handle to an internal Lua table.
|
||||
#[derive(Clone)]
|
||||
#[derive(Clone, PartialEq)]
|
||||
pub struct Table(pub(crate) ValueRef);
|
||||
|
||||
impl Table {
|
||||
@@ -59,7 +59,7 @@ impl Table {
|
||||
/// # }
|
||||
/// ```
|
||||
///
|
||||
/// [`raw_set`]: #method.raw_set
|
||||
/// [`raw_set`]: Table::raw_set
|
||||
pub fn set(&self, key: impl IntoLua, value: impl IntoLua) -> Result<()> {
|
||||
// Fast track (skip protected call)
|
||||
if !self.has_metatable() {
|
||||
@@ -106,7 +106,7 @@ impl Table {
|
||||
/// # }
|
||||
/// ```
|
||||
///
|
||||
/// [`raw_get`]: #method.raw_get
|
||||
/// [`raw_get`]: Table::raw_get
|
||||
pub fn get<V: FromLua>(&self, key: impl IntoLua) -> Result<V> {
|
||||
// Fast track (skip protected call)
|
||||
if !self.has_metatable() {
|
||||
@@ -211,28 +211,27 @@ impl Table {
|
||||
///
|
||||
/// let always_equals_mt = lua.create_table()?;
|
||||
/// always_equals_mt.set("__eq", lua.create_function(|_, (_t1, _t2): (Table, Table)| Ok(true))?)?;
|
||||
/// table2.set_metatable(Some(always_equals_mt));
|
||||
/// table2.set_metatable(Some(always_equals_mt))?;
|
||||
///
|
||||
/// assert!(table1.equals(&table1.clone())?);
|
||||
/// assert!(table1.equals(&table2)?);
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn equals<T: AsRef<Self>>(&self, other: T) -> Result<bool> {
|
||||
let other = other.as_ref();
|
||||
pub fn equals(&self, other: &Self) -> Result<bool> {
|
||||
if self == other {
|
||||
return Ok(true);
|
||||
}
|
||||
|
||||
// Compare using __eq metamethod if exists
|
||||
// Compare using `__eq` metamethod if exists
|
||||
// First, check the self for the metamethod.
|
||||
// If self does not define it, then check the other table.
|
||||
if let Some(mt) = self.get_metatable() {
|
||||
if let Some(mt) = self.metatable() {
|
||||
if mt.contains_key("__eq")? {
|
||||
return mt.get::<Function>("__eq")?.call((self, other));
|
||||
}
|
||||
}
|
||||
if let Some(mt) = other.get_metatable() {
|
||||
if let Some(mt) = other.metatable() {
|
||||
if mt.contains_key("__eq")? {
|
||||
return mt.get::<Function>("__eq")?.call((self, other));
|
||||
}
|
||||
@@ -243,12 +242,12 @@ impl Table {
|
||||
|
||||
/// Sets a key-value pair without invoking metamethods.
|
||||
pub fn raw_set(&self, key: impl IntoLua, value: impl IntoLua) -> Result<()> {
|
||||
#[cfg(feature = "luau")]
|
||||
self.check_readonly_write()?;
|
||||
|
||||
let lua = self.0.lua.lock();
|
||||
let state = lua.state();
|
||||
unsafe {
|
||||
#[cfg(feature = "luau")]
|
||||
self.check_readonly_write(&lua)?;
|
||||
|
||||
let _sg = StackGuard::new(state);
|
||||
check_stack(state, 5)?;
|
||||
|
||||
@@ -283,7 +282,9 @@ impl Table {
|
||||
}
|
||||
|
||||
/// Inserts element value at position `idx` to the table, shifting up the elements from
|
||||
/// `table[idx]`. The worst case complexity is O(n), where n is the table length.
|
||||
/// `table[idx]`.
|
||||
///
|
||||
/// The worst case complexity is O(n), where n is the table length.
|
||||
pub fn raw_insert(&self, idx: Integer, value: impl IntoLua) -> Result<()> {
|
||||
let size = self.raw_len() as Integer;
|
||||
if idx < 1 || idx > size + 1 {
|
||||
@@ -311,12 +312,12 @@ impl Table {
|
||||
|
||||
/// Appends a value to the back of the table without invoking metamethods.
|
||||
pub fn raw_push(&self, value: impl IntoLua) -> Result<()> {
|
||||
#[cfg(feature = "luau")]
|
||||
self.check_readonly_write()?;
|
||||
|
||||
let lua = self.0.lua.lock();
|
||||
let state = lua.state();
|
||||
unsafe {
|
||||
#[cfg(feature = "luau")]
|
||||
self.check_readonly_write(&lua)?;
|
||||
|
||||
let _sg = StackGuard::new(state);
|
||||
check_stack(state, 4)?;
|
||||
|
||||
@@ -339,12 +340,12 @@ impl Table {
|
||||
|
||||
/// Removes the last element from the table and returns it, without invoking metamethods.
|
||||
pub fn raw_pop<V: FromLua>(&self) -> Result<V> {
|
||||
#[cfg(feature = "luau")]
|
||||
self.check_readonly_write()?;
|
||||
|
||||
let lua = self.0.lua.lock();
|
||||
let state = lua.state();
|
||||
unsafe {
|
||||
#[cfg(feature = "luau")]
|
||||
self.check_readonly_write(&lua)?;
|
||||
|
||||
let _sg = StackGuard::new(state);
|
||||
check_stack(state, 3)?;
|
||||
|
||||
@@ -362,8 +363,8 @@ impl Table {
|
||||
/// Removes a key from the table.
|
||||
///
|
||||
/// If `key` is an integer, mlua shifts down the elements from `table[key+1]`,
|
||||
/// and erases element `table[key]`. The complexity is O(n) in the worst case,
|
||||
/// where n is the table length.
|
||||
/// and erases element `table[key]`. The complexity is `O(n)` in the worst case,
|
||||
/// where `n` is the table length.
|
||||
///
|
||||
/// For other key types this is equivalent to setting `table[key] = nil`.
|
||||
pub fn raw_remove(&self, key: impl IntoLua) -> Result<()> {
|
||||
@@ -400,13 +401,13 @@ impl Table {
|
||||
///
|
||||
/// This method is useful to clear the table while keeping its capacity.
|
||||
pub fn clear(&self) -> Result<()> {
|
||||
#[cfg(feature = "luau")]
|
||||
self.check_readonly_write()?;
|
||||
|
||||
let lua = self.0.lua.lock();
|
||||
unsafe {
|
||||
#[cfg(feature = "luau")]
|
||||
ffi::lua_cleartable(lua.ref_thread(), self.0.index);
|
||||
{
|
||||
self.check_readonly_write(&lua)?;
|
||||
ffi::lua_cleartable(lua.ref_thread(), self.0.index);
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "luau"))]
|
||||
{
|
||||
@@ -438,9 +439,8 @@ impl Table {
|
||||
|
||||
/// Returns the result of the Lua `#` operator.
|
||||
///
|
||||
/// This might invoke the `__len` metamethod. Use the [`raw_len`] method if that is not desired.
|
||||
///
|
||||
/// [`raw_len`]: #method.raw_len
|
||||
/// This might invoke the `__len` metamethod. Use the [`Table::raw_len`] method if that is not
|
||||
/// desired.
|
||||
pub fn len(&self) -> Result<Integer> {
|
||||
// Fast track (skip protected call)
|
||||
if !self.has_metatable() {
|
||||
@@ -468,43 +468,31 @@ impl Table {
|
||||
///
|
||||
/// It checks both the array part and the hash part.
|
||||
pub fn is_empty(&self) -> bool {
|
||||
// Check array part
|
||||
if self.raw_len() != 0 {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check hash part
|
||||
let lua = self.0.lua.lock();
|
||||
let state = lua.state();
|
||||
let ref_thread = lua.ref_thread();
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(state);
|
||||
assert_stack(state, 4);
|
||||
|
||||
lua.push_ref(&self.0);
|
||||
ffi::lua_pushnil(state);
|
||||
if ffi::lua_next(state, -2) != 0 {
|
||||
return false;
|
||||
ffi::lua_pushnil(ref_thread);
|
||||
if ffi::lua_next(ref_thread, self.0.index) == 0 {
|
||||
return true;
|
||||
}
|
||||
ffi::lua_pop(ref_thread, 2);
|
||||
}
|
||||
|
||||
true
|
||||
false
|
||||
}
|
||||
|
||||
/// Returns a reference to the metatable of this table, or `None` if no metatable is set.
|
||||
///
|
||||
/// Unlike the `getmetatable` Lua function, this method ignores the `__metatable` field.
|
||||
pub fn get_metatable(&self) -> Option<Table> {
|
||||
/// Unlike the [`getmetatable`] Lua function, this method ignores the `__metatable` field.
|
||||
///
|
||||
/// [`getmetatable`]: https://www.lua.org/manual/5.4/manual.html#pdf-getmetatable
|
||||
pub fn metatable(&self) -> Option<Table> {
|
||||
let lua = self.0.lua.lock();
|
||||
let state = lua.state();
|
||||
let ref_thread = lua.ref_thread();
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(state);
|
||||
assert_stack(state, 2);
|
||||
|
||||
lua.push_ref(&self.0);
|
||||
if ffi::lua_getmetatable(state, -1) == 0 {
|
||||
if ffi::lua_getmetatable(ref_thread, self.0.index) == 0 {
|
||||
None
|
||||
} else {
|
||||
Some(Table(lua.pop_ref()))
|
||||
Some(Table(lua.pop_ref_thread()))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -513,27 +501,23 @@ impl Table {
|
||||
///
|
||||
/// If `metatable` is `None`, the metatable is removed (if no metatable is set, this does
|
||||
/// nothing).
|
||||
pub fn set_metatable(&self, metatable: Option<Table>) {
|
||||
// Workaround to throw readonly error without returning Result
|
||||
pub fn set_metatable(&self, metatable: Option<Table>) -> Result<()> {
|
||||
#[cfg(feature = "luau")]
|
||||
if self.is_readonly() {
|
||||
panic!("attempt to modify a readonly table");
|
||||
return Err(Error::runtime("attempt to modify a readonly table"));
|
||||
}
|
||||
|
||||
let lua = self.0.lua.lock();
|
||||
let state = lua.state();
|
||||
let ref_thread = lua.ref_thread();
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(state);
|
||||
assert_stack(state, 2);
|
||||
|
||||
lua.push_ref(&self.0);
|
||||
if let Some(metatable) = metatable {
|
||||
lua.push_ref(&metatable.0);
|
||||
if let Some(metatable) = &metatable {
|
||||
ffi::lua_pushvalue(ref_thread, metatable.0.index);
|
||||
} else {
|
||||
ffi::lua_pushnil(state);
|
||||
ffi::lua_pushnil(ref_thread);
|
||||
}
|
||||
ffi::lua_setmetatable(state, -2);
|
||||
ffi::lua_setmetatable(ref_thread, self.0.index);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Returns true if the table has metatable attached.
|
||||
@@ -541,19 +525,10 @@ impl Table {
|
||||
#[inline]
|
||||
pub fn has_metatable(&self) -> bool {
|
||||
let lua = self.0.lua.lock();
|
||||
let ref_thread = lua.ref_thread();
|
||||
unsafe {
|
||||
if ffi::lua_getmetatable(ref_thread, self.0.index) != 0 {
|
||||
ffi::lua_pop(ref_thread, 1);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
false
|
||||
unsafe { !get_metatable_ptr(lua.ref_thread(), self.0.index).is_null() }
|
||||
}
|
||||
|
||||
/// 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) {
|
||||
@@ -569,8 +544,6 @@ impl Table {
|
||||
}
|
||||
|
||||
/// Returns `readonly` attribute of the table.
|
||||
///
|
||||
/// Requires `feature = "luau"`
|
||||
#[cfg(any(feature = "luau", doc))]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
|
||||
pub fn is_readonly(&self) -> bool {
|
||||
@@ -579,6 +552,22 @@ impl Table {
|
||||
unsafe { ffi::lua_getreadonly(ref_thread, self.0.index) != 0 }
|
||||
}
|
||||
|
||||
/// Controls `safeenv` attribute on the table.
|
||||
///
|
||||
/// This a special flag that activates some performance optimizations for environment tables.
|
||||
/// In particular, it controls:
|
||||
/// - Optimization of import resolution (cache values of constant keys).
|
||||
/// - Fast-path for built-in iteration with pairs/ipairs.
|
||||
/// - Fast-path for some built-in functions (fastcall).
|
||||
///
|
||||
/// For `safeenv` environments, monkey patching or modifying values may not work as expected.
|
||||
#[cfg(any(feature = "luau", doc))]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
|
||||
pub fn set_safeenv(&self, enabled: bool) {
|
||||
let lua = self.0.lua.lock();
|
||||
unsafe { ffi::lua_setsafeenv(lua.ref_thread(), self.0.index, enabled as _) };
|
||||
}
|
||||
|
||||
/// Converts this table to a generic C pointer.
|
||||
///
|
||||
/// Different tables will give different pointers.
|
||||
@@ -615,9 +604,8 @@ impl Table {
|
||||
/// # }
|
||||
/// ```
|
||||
///
|
||||
/// [`Result`]: crate::Result
|
||||
/// [Lua manual]: http://www.lua.org/manual/5.4/manual.html#pdf-next
|
||||
pub fn pairs<K: FromLua, V: FromLua>(&self) -> TablePairs<K, V> {
|
||||
pub fn pairs<K: FromLua, V: FromLua>(&self) -> TablePairs<'_, K, V> {
|
||||
TablePairs {
|
||||
guard: self.0.lua.lock(),
|
||||
table: self,
|
||||
@@ -682,11 +670,7 @@ impl Table {
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
///
|
||||
/// [`pairs`]: #method.pairs
|
||||
/// [`Result`]: crate::Result
|
||||
/// [Lua manual]: http://www.lua.org/manual/5.4/manual.html#pdf-next
|
||||
pub fn sequence_values<V: FromLua>(&self) -> TableSequence<V> {
|
||||
pub fn sequence_values<V: FromLua>(&self) -> TableSequence<'_, V> {
|
||||
TableSequence {
|
||||
guard: self.0.lua.lock(),
|
||||
table: self,
|
||||
@@ -695,8 +679,9 @@ impl Table {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
pub(crate) fn for_each_value<V>(&self, mut f: impl FnMut(V) -> Result<()>) -> Result<()>
|
||||
/// Iterates over the sequence part of the table, invoking the given closure on each value.
|
||||
#[doc(hidden)]
|
||||
pub fn for_each_value<V>(&self, mut f: impl FnMut(V) -> Result<()>) -> Result<()>
|
||||
where
|
||||
V: FromLua,
|
||||
{
|
||||
@@ -720,12 +705,12 @@ impl Table {
|
||||
/// Sets element value at position `idx` without invoking metamethods.
|
||||
#[doc(hidden)]
|
||||
pub fn raw_seti(&self, idx: usize, value: impl IntoLua) -> Result<()> {
|
||||
#[cfg(feature = "luau")]
|
||||
self.check_readonly_write()?;
|
||||
|
||||
let lua = self.0.lua.lock();
|
||||
let state = lua.state();
|
||||
unsafe {
|
||||
#[cfg(feature = "luau")]
|
||||
self.check_readonly_write(&lua)?;
|
||||
|
||||
let _sg = StackGuard::new(state);
|
||||
check_stack(state, 5)?;
|
||||
|
||||
@@ -742,7 +727,7 @@ impl Table {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
#[cfg(feature = "serde")]
|
||||
pub(crate) fn is_array(&self) -> bool {
|
||||
let lua = self.0.lua.lock();
|
||||
let state = lua.state();
|
||||
@@ -761,8 +746,8 @@ impl Table {
|
||||
|
||||
#[cfg(feature = "luau")]
|
||||
#[inline(always)]
|
||||
pub(crate) fn check_readonly_write(&self) -> Result<()> {
|
||||
if self.is_readonly() {
|
||||
fn check_readonly_write(&self, lua: &RawLua) -> Result<()> {
|
||||
if unsafe { ffi::lua_getreadonly(lua.ref_thread(), self.0.index) != 0 } {
|
||||
return Err(Error::runtime("attempt to modify a readonly table"));
|
||||
}
|
||||
Ok(())
|
||||
@@ -779,17 +764,41 @@ impl Table {
|
||||
// Collect key/value pairs into a vector so we can sort them
|
||||
let mut pairs = self.pairs::<Value, Value>().flatten().collect::<Vec<_>>();
|
||||
// Sort keys
|
||||
pairs.sort_by(|(a, _), (b, _)| a.cmp(b));
|
||||
pairs.sort_by(|(a, _), (b, _)| a.sort_cmp(b));
|
||||
let is_sequence = (pairs.iter().enumerate())
|
||||
.all(|(i, (k, _))| matches!(k, Value::Integer(n) if *n == (i + 1) as Integer));
|
||||
if pairs.is_empty() {
|
||||
return write!(fmt, "{{}}");
|
||||
}
|
||||
writeln!(fmt, "{{")?;
|
||||
for (key, value) in pairs {
|
||||
write!(fmt, "{}[", " ".repeat(ident + 2))?;
|
||||
key.fmt_pretty(fmt, false, ident + 2, visited)?;
|
||||
write!(fmt, "] = ")?;
|
||||
value.fmt_pretty(fmt, true, ident + 2, visited)?;
|
||||
writeln!(fmt, ",")?;
|
||||
if is_sequence {
|
||||
// Format as list
|
||||
for (_, value) in pairs {
|
||||
write!(fmt, "{}", " ".repeat(ident + 2))?;
|
||||
value.fmt_pretty(fmt, true, ident + 2, visited)?;
|
||||
writeln!(fmt, ",")?;
|
||||
}
|
||||
} else {
|
||||
fn is_simple_key(key: &[u8]) -> bool {
|
||||
key.iter().take(1).all(|c| c.is_ascii_alphabetic() || *c == b'_')
|
||||
&& key.iter().all(|c| c.is_ascii_alphanumeric() || *c == b'_')
|
||||
}
|
||||
|
||||
for (key, value) in pairs {
|
||||
match key {
|
||||
Value::String(key) if is_simple_key(&key.as_bytes()) => {
|
||||
write!(fmt, "{}{}", " ".repeat(ident + 2), key.display())?;
|
||||
write!(fmt, " = ")?;
|
||||
}
|
||||
_ => {
|
||||
write!(fmt, "{}[", " ".repeat(ident + 2))?;
|
||||
key.fmt_pretty(fmt, false, ident + 2, visited)?;
|
||||
write!(fmt, "] = ")?;
|
||||
}
|
||||
}
|
||||
value.fmt_pretty(fmt, true, ident + 2, visited)?;
|
||||
writeln!(fmt, ",")?;
|
||||
}
|
||||
}
|
||||
write!(fmt, "{}}}", " ".repeat(ident))
|
||||
}
|
||||
@@ -800,20 +809,7 @@ impl fmt::Debug for Table {
|
||||
if fmt.alternate() {
|
||||
return self.fmt_pretty(fmt, 0, &mut HashSet::new());
|
||||
}
|
||||
fmt.write_fmt(format_args!("Table({:?})", self.0))
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for Table {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.0 == other.0
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<Table> for Table {
|
||||
#[inline]
|
||||
fn as_ref(&self) -> &Self {
|
||||
self
|
||||
fmt.debug_tuple("Table").field(&self.0).finish()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -867,6 +863,10 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl LuaType for Table {
|
||||
const TYPE_ID: c_int = ffi::LUA_TTABLE;
|
||||
}
|
||||
|
||||
impl ObjectLike for Table {
|
||||
#[inline]
|
||||
fn get<V: FromLua>(&self, key: impl IntoLua) -> Result<V> {
|
||||
@@ -889,7 +889,7 @@ impl ObjectLike for Table {
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
#[inline]
|
||||
fn call_async<R>(&self, args: impl IntoLuaMulti) -> impl Future<Output = Result<R>>
|
||||
fn call_async<R>(&self, args: impl IntoLuaMulti) -> AsyncCallFuture<R>
|
||||
where
|
||||
R: FromLuaMulti,
|
||||
{
|
||||
@@ -905,7 +905,7 @@ impl ObjectLike for Table {
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
fn call_async_method<R>(&self, name: &str, args: impl IntoLuaMulti) -> impl Future<Output = Result<R>>
|
||||
fn call_async_method<R>(&self, name: &str, args: impl IntoLuaMulti) -> AsyncCallFuture<R>
|
||||
where
|
||||
R: FromLuaMulti,
|
||||
{
|
||||
@@ -925,17 +925,17 @@ impl ObjectLike for Table {
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
#[inline]
|
||||
fn call_async_function<R>(&self, name: &str, args: impl IntoLuaMulti) -> impl Future<Output = Result<R>>
|
||||
fn call_async_function<R>(&self, name: &str, args: impl IntoLuaMulti) -> AsyncCallFuture<R>
|
||||
where
|
||||
R: FromLuaMulti,
|
||||
{
|
||||
match self.get(name) {
|
||||
Ok(Value::Function(func)) => Either::Left(func.call_async(args)),
|
||||
Ok(Value::Function(func)) => func.call_async(args),
|
||||
Ok(val) => {
|
||||
let msg = format!("attempt to call a {} value (function '{name}')", val.type_name());
|
||||
Either::Right(future::ready(Err(Error::RuntimeError(msg))))
|
||||
AsyncCallFuture::error(Error::RuntimeError(msg))
|
||||
}
|
||||
Err(err) => Either::Right(future::ready(Err(err))),
|
||||
Err(err) => AsyncCallFuture::error(err),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -946,14 +946,14 @@ impl ObjectLike for Table {
|
||||
}
|
||||
|
||||
/// A wrapped [`Table`] with customized serialization behavior.
|
||||
#[cfg(feature = "serialize")]
|
||||
#[cfg(feature = "serde")]
|
||||
pub(crate) struct SerializableTable<'a> {
|
||||
table: &'a Table,
|
||||
options: crate::serde::de::Options,
|
||||
visited: Rc<RefCell<FxHashSet<*const c_void>>>,
|
||||
}
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
#[cfg(feature = "serde")]
|
||||
impl Serialize for Table {
|
||||
#[inline]
|
||||
fn serialize<S: Serializer>(&self, serializer: S) -> StdResult<S::Ok, S::Error> {
|
||||
@@ -961,7 +961,7 @@ impl Serialize for Table {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
#[cfg(feature = "serde")]
|
||||
impl<'a> SerializableTable<'a> {
|
||||
#[inline]
|
||||
pub(crate) fn new(
|
||||
@@ -977,8 +977,8 @@ impl<'a> SerializableTable<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
impl<'a> Serialize for SerializableTable<'a> {
|
||||
#[cfg(feature = "serde")]
|
||||
impl Serialize for SerializableTable<'_> {
|
||||
fn serialize<S>(&self, serializer: S) -> StdResult<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
@@ -999,7 +999,10 @@ impl<'a> Serialize for SerializableTable<'a> {
|
||||
|
||||
// Array
|
||||
let len = self.table.raw_len();
|
||||
if len > 0 || self.table.is_array() {
|
||||
if len > 0
|
||||
|| self.table.is_array()
|
||||
|| (self.options.encode_empty_tables_as_array && self.table.is_empty())
|
||||
{
|
||||
let mut seq = serializer.serialize_seq(Some(len))?;
|
||||
let mut serialize_err = None;
|
||||
let res = self.table.for_each_value::<Value>(|value| {
|
||||
@@ -1069,7 +1072,7 @@ pub struct TablePairs<'a, K, V> {
|
||||
_phantom: PhantomData<(K, V)>,
|
||||
}
|
||||
|
||||
impl<'a, K, V> Iterator for TablePairs<'a, K, V>
|
||||
impl<K, V> Iterator for TablePairs<'_, K, V>
|
||||
where
|
||||
K: FromLua,
|
||||
V: FromLua,
|
||||
@@ -1129,7 +1132,7 @@ pub struct TableSequence<'a, V> {
|
||||
_phantom: PhantomData<V>,
|
||||
}
|
||||
|
||||
impl<'a, V> Iterator for TableSequence<'a, V>
|
||||
impl<V> Iterator for TableSequence<'_, V>
|
||||
where
|
||||
V: FromLua,
|
||||
{
|
||||
|
||||
+278
-144
@@ -1,17 +1,17 @@
|
||||
use std::fmt;
|
||||
use std::os::raw::{c_int, c_void};
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
#[allow(unused)]
|
||||
use crate::state::Lua;
|
||||
use crate::function::Function;
|
||||
use crate::state::RawLua;
|
||||
use crate::types::ValueRef;
|
||||
use crate::traits::{FromLuaMulti, IntoLuaMulti};
|
||||
use crate::types::{LuaType, ValueRef};
|
||||
use crate::util::{check_stack, error_traceback_thread, pop_error, StackGuard};
|
||||
use crate::value::{FromLuaMulti, IntoLuaMulti};
|
||||
|
||||
#[cfg(not(feature = "luau"))]
|
||||
use crate::{
|
||||
hook::{Debug, HookTriggers},
|
||||
types::MaybeSend,
|
||||
debug::{Debug, HookTriggers},
|
||||
types::HookKind,
|
||||
};
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
@@ -41,8 +41,35 @@ pub enum ThreadStatus {
|
||||
Error,
|
||||
}
|
||||
|
||||
/// Internal representation of a Lua thread status.
|
||||
///
|
||||
/// The number in `New` and `Yielded` variants is the number of arguments pushed
|
||||
/// to the thread stack.
|
||||
#[derive(Clone, Copy)]
|
||||
enum ThreadStatusInner {
|
||||
New(c_int),
|
||||
Running,
|
||||
Yielded(c_int),
|
||||
Finished,
|
||||
Error,
|
||||
}
|
||||
|
||||
impl ThreadStatusInner {
|
||||
#[cfg(feature = "async")]
|
||||
#[inline(always)]
|
||||
fn is_resumable(self) -> bool {
|
||||
matches!(self, ThreadStatusInner::New(_) | ThreadStatusInner::Yielded(_))
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
#[inline(always)]
|
||||
fn is_yielded(self) -> bool {
|
||||
matches!(self, ThreadStatusInner::Yielded(_))
|
||||
}
|
||||
}
|
||||
|
||||
/// Handle to an internal Lua thread (coroutine).
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(Clone)]
|
||||
pub struct Thread(pub(crate) ValueRef, pub(crate) *mut ffi::lua_State);
|
||||
|
||||
#[cfg(feature = "send")]
|
||||
@@ -52,39 +79,38 @@ unsafe impl Sync for Thread {}
|
||||
|
||||
/// Thread (coroutine) representation as an async [`Future`] or [`Stream`].
|
||||
///
|
||||
/// Requires `feature = "async"`
|
||||
///
|
||||
/// [`Future`]: std::future::Future
|
||||
/// [`Stream`]: futures_util::stream::Stream
|
||||
#[cfg(feature = "async")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
|
||||
#[must_use = "futures do nothing unless you `.await` or poll them"]
|
||||
pub struct AsyncThread<A, R> {
|
||||
pub struct AsyncThread<R> {
|
||||
thread: Thread,
|
||||
init_args: Option<A>,
|
||||
ret: PhantomData<R>,
|
||||
ret: PhantomData<fn() -> R>,
|
||||
recycle: bool,
|
||||
}
|
||||
|
||||
impl Thread {
|
||||
/// Returns reference to the Lua state that this thread is associated with.
|
||||
#[doc(hidden)]
|
||||
#[inline(always)]
|
||||
const fn state(&self) -> *mut ffi::lua_State {
|
||||
pub fn state(&self) -> *mut ffi::lua_State {
|
||||
self.1
|
||||
}
|
||||
|
||||
/// Resumes execution of this thread.
|
||||
///
|
||||
/// Equivalent to `coroutine.resume`.
|
||||
/// Equivalent to [`coroutine.resume`].
|
||||
///
|
||||
/// Passes `args` as arguments to the thread. If the coroutine has called `coroutine.yield`, it
|
||||
/// will return these arguments. Otherwise, the coroutine wasn't yet started, so the arguments
|
||||
/// are passed to its main function.
|
||||
/// Passes `args` as arguments to the thread. If the coroutine has called [`coroutine.yield`],
|
||||
/// it will return these arguments. Otherwise, the coroutine wasn't yet started, so the
|
||||
/// arguments are passed to its main function.
|
||||
///
|
||||
/// If the thread is no longer in `Active` state (meaning it has finished execution or
|
||||
/// encountered an error), this will return `Err(CoroutineInactive)`, otherwise will return `Ok`
|
||||
/// as follows:
|
||||
/// If the thread is no longer resumable (meaning it has finished execution or encountered an
|
||||
/// error), this will return [`Error::CoroutineUnresumable`], otherwise will return `Ok` as
|
||||
/// follows:
|
||||
///
|
||||
/// If the thread calls `coroutine.yield`, returns the values passed to `yield`. If the thread
|
||||
/// If the thread calls [`coroutine.yield`], returns the values passed to `yield`. If the thread
|
||||
/// `return`s values from its main function, returns those.
|
||||
///
|
||||
/// # Examples
|
||||
@@ -113,14 +139,18 @@ impl Thread {
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
///
|
||||
/// [`coroutine.resume`]: https://www.lua.org/manual/5.4/manual.html#pdf-coroutine.resume
|
||||
/// [`coroutine.yield`]: https://www.lua.org/manual/5.4/manual.html#pdf-coroutine.yield
|
||||
pub fn resume<R>(&self, args: impl IntoLuaMulti) -> Result<R>
|
||||
where
|
||||
R: FromLuaMulti,
|
||||
{
|
||||
let lua = self.0.lua.lock();
|
||||
if self.status_inner(&lua) != ThreadStatus::Resumable {
|
||||
return Err(Error::CoroutineUnresumable);
|
||||
}
|
||||
let mut pushed_nargs = match self.status_inner(&lua) {
|
||||
ThreadStatusInner::New(nargs) | ThreadStatusInner::Yielded(nargs) => nargs,
|
||||
_ => return Err(Error::CoroutineUnresumable),
|
||||
};
|
||||
|
||||
let state = lua.state();
|
||||
let thread_state = self.state();
|
||||
@@ -128,7 +158,47 @@ impl Thread {
|
||||
let _sg = StackGuard::new(state);
|
||||
let _thread_sg = StackGuard::with_top(thread_state, 0);
|
||||
|
||||
let nresults = self.resume_inner(&lua, args)?;
|
||||
let nargs = args.push_into_stack_multi(&lua)?;
|
||||
if nargs > 0 {
|
||||
check_stack(thread_state, nargs)?;
|
||||
ffi::lua_xmove(state, thread_state, nargs);
|
||||
pushed_nargs += nargs;
|
||||
}
|
||||
|
||||
let (_, nresults) = self.resume_inner(&lua, pushed_nargs)?;
|
||||
check_stack(state, nresults + 1)?;
|
||||
ffi::lua_xmove(thread_state, state, nresults);
|
||||
|
||||
R::from_stack_multi(nresults, &lua)
|
||||
}
|
||||
}
|
||||
|
||||
/// Resumes execution of this thread, immediately raising an error.
|
||||
///
|
||||
/// This is a Luau specific extension.
|
||||
#[cfg(feature = "luau")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
|
||||
pub fn resume_error<R>(&self, error: impl crate::IntoLua) -> Result<R>
|
||||
where
|
||||
R: FromLuaMulti,
|
||||
{
|
||||
let lua = self.0.lua.lock();
|
||||
match self.status_inner(&lua) {
|
||||
ThreadStatusInner::New(_) | ThreadStatusInner::Yielded(_) => {}
|
||||
_ => return Err(Error::CoroutineUnresumable),
|
||||
};
|
||||
|
||||
let state = lua.state();
|
||||
let thread_state = self.state();
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(state);
|
||||
let _thread_sg = StackGuard::with_top(thread_state, 0);
|
||||
|
||||
check_stack(state, 1)?;
|
||||
error.push_into_stack(&lua)?;
|
||||
ffi::lua_xmove(state, thread_state, 1);
|
||||
|
||||
let (_, nresults) = self.resume_inner(&lua, ffi::LUA_RESUMEERROR)?;
|
||||
check_stack(state, nresults + 1)?;
|
||||
ffi::lua_xmove(thread_state, state, nresults);
|
||||
|
||||
@@ -139,66 +209,86 @@ impl Thread {
|
||||
/// Resumes execution of this thread.
|
||||
///
|
||||
/// It's similar to `resume()` but leaves `nresults` values on the thread stack.
|
||||
unsafe fn resume_inner(&self, lua: &RawLua, args: impl IntoLuaMulti) -> Result<c_int> {
|
||||
unsafe fn resume_inner(&self, lua: &RawLua, nargs: c_int) -> Result<(ThreadStatusInner, c_int)> {
|
||||
let state = lua.state();
|
||||
let thread_state = self.state();
|
||||
|
||||
let nargs = args.push_into_stack_multi(&lua)?;
|
||||
if nargs > 0 {
|
||||
check_stack(thread_state, nargs)?;
|
||||
ffi::lua_xmove(state, thread_state, nargs);
|
||||
}
|
||||
|
||||
let mut nresults = 0;
|
||||
#[cfg(not(feature = "luau"))]
|
||||
let ret = ffi::lua_resume(thread_state, state, nargs, &mut nresults as *mut c_int);
|
||||
if ret != ffi::LUA_OK && ret != ffi::LUA_YIELD {
|
||||
if ret == ffi::LUA_ERRMEM {
|
||||
#[cfg(feature = "luau")]
|
||||
let ret = ffi::lua_resumex(thread_state, state, nargs, &mut nresults as *mut c_int);
|
||||
match ret {
|
||||
ffi::LUA_OK => Ok((ThreadStatusInner::Finished, nresults)),
|
||||
ffi::LUA_YIELD => Ok((ThreadStatusInner::Yielded(0), nresults)),
|
||||
ffi::LUA_ERRMEM => {
|
||||
// Don't call error handler for memory errors
|
||||
return Err(pop_error(thread_state, ret));
|
||||
Err(pop_error(thread_state, ret))
|
||||
}
|
||||
_ => {
|
||||
check_stack(state, 3)?;
|
||||
protect_lua!(state, 0, 1, |state| error_traceback_thread(state, thread_state))?;
|
||||
Err(pop_error(state, ret))
|
||||
}
|
||||
check_stack(state, 3)?;
|
||||
protect_lua!(state, 0, 1, |state| error_traceback_thread(state, thread_state))?;
|
||||
return Err(pop_error(state, ret));
|
||||
}
|
||||
|
||||
Ok(nresults)
|
||||
}
|
||||
|
||||
/// Gets the status of the thread.
|
||||
pub fn status(&self) -> ThreadStatus {
|
||||
self.status_inner(&self.0.lua.lock())
|
||||
match self.status_inner(&self.0.lua.lock()) {
|
||||
ThreadStatusInner::New(_) | ThreadStatusInner::Yielded(_) => ThreadStatus::Resumable,
|
||||
ThreadStatusInner::Running => ThreadStatus::Running,
|
||||
ThreadStatusInner::Finished => ThreadStatus::Finished,
|
||||
ThreadStatusInner::Error => ThreadStatus::Error,
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets the status of the thread (internal implementation).
|
||||
pub(crate) fn status_inner(&self, lua: &RawLua) -> ThreadStatus {
|
||||
fn status_inner(&self, lua: &RawLua) -> ThreadStatusInner {
|
||||
let thread_state = self.state();
|
||||
if thread_state == lua.state() {
|
||||
// The thread is currently running
|
||||
return ThreadStatus::Running;
|
||||
return ThreadStatusInner::Running;
|
||||
}
|
||||
let status = unsafe { ffi::lua_status(thread_state) };
|
||||
if status != ffi::LUA_OK && status != ffi::LUA_YIELD {
|
||||
ThreadStatus::Error
|
||||
} else if status == ffi::LUA_YIELD || unsafe { ffi::lua_gettop(thread_state) > 0 } {
|
||||
ThreadStatus::Resumable
|
||||
} else {
|
||||
ThreadStatus::Finished
|
||||
let top = unsafe { ffi::lua_gettop(thread_state) };
|
||||
match status {
|
||||
ffi::LUA_YIELD => ThreadStatusInner::Yielded(top),
|
||||
ffi::LUA_OK if top > 0 => ThreadStatusInner::New(top - 1),
|
||||
ffi::LUA_OK => ThreadStatusInner::Finished,
|
||||
_ => ThreadStatusInner::Error,
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets a 'hook' function that will periodically be called as Lua code executes.
|
||||
/// Sets a hook function that will periodically be called as Lua code executes.
|
||||
///
|
||||
/// This function is similar or [`Lua::set_hook()`] except that it sets for the thread.
|
||||
/// To remove a hook call [`Lua::remove_hook()`].
|
||||
/// This function is similar or [`Lua::set_hook`] except that it sets for the thread.
|
||||
/// You can have multiple hooks for different threads.
|
||||
///
|
||||
/// To remove a hook call [`Thread::remove_hook`].
|
||||
///
|
||||
/// [`Lua::set_hook`]: crate::Lua::set_hook
|
||||
#[cfg(not(feature = "luau"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(not(feature = "luau"))))]
|
||||
pub fn set_hook<F>(&self, triggers: HookTriggers, callback: F)
|
||||
pub fn set_hook<F>(&self, triggers: HookTriggers, callback: F) -> Result<()>
|
||||
where
|
||||
F: Fn(&Lua, Debug) -> Result<()> + MaybeSend + 'static,
|
||||
F: Fn(&crate::Lua, &Debug) -> Result<crate::VmState> + crate::MaybeSend + 'static,
|
||||
{
|
||||
let lua = self.0.lua.lock();
|
||||
unsafe {
|
||||
lua.set_thread_hook(self.state(), triggers, callback);
|
||||
lua.set_thread_hook(
|
||||
self.state(),
|
||||
HookKind::Thread(triggers, crate::types::XRc::new(callback)),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// Removes any hook function from this thread.
|
||||
#[cfg(not(feature = "luau"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(not(feature = "luau"))))]
|
||||
pub fn remove_hook(&self) {
|
||||
let _lua = self.0.lua.lock();
|
||||
unsafe {
|
||||
ffi::lua_sethook(self.state(), None, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -211,31 +301,17 @@ impl Thread {
|
||||
/// In Luau: resets to the initial state of a newly created Lua thread.
|
||||
/// Lua threads in arbitrary states (like yielded or errored) can be reset properly.
|
||||
///
|
||||
/// Other Lua versions can reset only new or finished threads.
|
||||
///
|
||||
/// Sets a Lua function for the thread afterwards.
|
||||
///
|
||||
/// Requires `feature = "lua54"` OR `feature = "luau"`.
|
||||
///
|
||||
/// [Lua 5.4]: https://www.lua.org/manual/5.4/manual.html#lua_closethread
|
||||
#[cfg(any(feature = "lua54", feature = "luau"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(any(feature = "lua54", feature = "luau"))))]
|
||||
pub fn reset(&self, func: crate::function::Function) -> Result<()> {
|
||||
pub fn reset(&self, func: Function) -> Result<()> {
|
||||
let lua = self.0.lua.lock();
|
||||
if self.status_inner(&lua) == ThreadStatus::Running {
|
||||
return Err(Error::runtime("cannot reset a running thread"));
|
||||
}
|
||||
|
||||
let thread_state = self.state();
|
||||
unsafe {
|
||||
#[cfg(all(feature = "lua54", not(feature = "vendored")))]
|
||||
let status = ffi::lua_resetthread(thread_state);
|
||||
#[cfg(all(feature = "lua54", feature = "vendored"))]
|
||||
let status = ffi::lua_closethread(thread_state, lua.state());
|
||||
#[cfg(feature = "lua54")]
|
||||
if status != ffi::LUA_OK {
|
||||
return Err(pop_error(thread_state, status));
|
||||
}
|
||||
#[cfg(feature = "luau")]
|
||||
ffi::lua_resetthread(thread_state);
|
||||
let status = self.status_inner(&lua);
|
||||
self.reset_inner(status)?;
|
||||
|
||||
// Push function to the top of the thread stack
|
||||
ffi::lua_xpush(lua.ref_thread(), thread_state, func.0.index);
|
||||
@@ -251,21 +327,58 @@ impl Thread {
|
||||
}
|
||||
}
|
||||
|
||||
/// Converts Thread to an AsyncThread which implements [`Future`] and [`Stream`] traits.
|
||||
unsafe fn reset_inner(&self, status: ThreadStatusInner) -> Result<()> {
|
||||
match status {
|
||||
ThreadStatusInner::New(_) => {
|
||||
// The thread is new, so we can just set the top to 0
|
||||
ffi::lua_settop(self.state(), 0);
|
||||
Ok(())
|
||||
}
|
||||
ThreadStatusInner::Running => Err(Error::runtime("cannot reset a running thread")),
|
||||
ThreadStatusInner::Finished => Ok(()),
|
||||
#[cfg(not(any(feature = "lua54", feature = "luau")))]
|
||||
ThreadStatusInner::Yielded(_) | ThreadStatusInner::Error => {
|
||||
Err(Error::runtime("cannot reset non-finished thread"))
|
||||
}
|
||||
#[cfg(any(feature = "lua54", feature = "luau"))]
|
||||
ThreadStatusInner::Yielded(_) | ThreadStatusInner::Error => {
|
||||
let thread_state = self.state();
|
||||
|
||||
#[cfg(all(feature = "lua54", not(feature = "vendored")))]
|
||||
let status = ffi::lua_resetthread(thread_state);
|
||||
#[cfg(all(feature = "lua54", feature = "vendored"))]
|
||||
let status = {
|
||||
let lua = self.0.lua.lock();
|
||||
ffi::lua_closethread(thread_state, lua.state())
|
||||
};
|
||||
#[cfg(feature = "lua54")]
|
||||
if status != ffi::LUA_OK {
|
||||
return Err(pop_error(thread_state, status));
|
||||
}
|
||||
#[cfg(feature = "luau")]
|
||||
ffi::lua_resetthread(thread_state);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Converts [`Thread`] to an [`AsyncThread`] which implements [`Future`] and [`Stream`] traits.
|
||||
///
|
||||
/// `args` are passed as arguments to the thread function for first call.
|
||||
/// The object calls [`resume()`] while polling and also allows to run rust futures
|
||||
/// Only resumable threads can be converted to [`AsyncThread`].
|
||||
///
|
||||
/// `args` are pushed to the thread stack and will be used when the thread is resumed.
|
||||
/// The object calls [`resume`] while polling and also allow to run Rust futures
|
||||
/// to completion using an executor.
|
||||
///
|
||||
/// Using AsyncThread as a Stream allows to iterate through `coroutine.yield()`
|
||||
/// values whereas Future version discards that values and poll until the final
|
||||
/// Using [`AsyncThread`] as a [`Stream`] allow to iterate through [`coroutine.yield`]
|
||||
/// values whereas [`Future`] version discards that values and poll until the final
|
||||
/// one (returned from the thread function).
|
||||
///
|
||||
/// Requires `feature = "async"`
|
||||
///
|
||||
/// [`Future`]: std::future::Future
|
||||
/// [`Stream`]: futures_util::stream::Stream
|
||||
/// [`resume()`]: https://www.lua.org/manual/5.4/manual.html#lua_resume
|
||||
/// [`resume`]: https://www.lua.org/manual/5.4/manual.html#lua_resume
|
||||
/// [`coroutine.yield`]: https://www.lua.org/manual/5.4/manual.html#pdf-coroutine.yield
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
@@ -285,7 +398,7 @@ impl Thread {
|
||||
/// end)
|
||||
/// "#).eval()?;
|
||||
///
|
||||
/// let mut stream = thread.into_async::<i64>(1);
|
||||
/// let mut stream = thread.into_async::<i64>(1)?;
|
||||
/// let mut sum = 0;
|
||||
/// while let Some(n) = stream.try_next().await? {
|
||||
/// sum += n;
|
||||
@@ -298,15 +411,31 @@ impl Thread {
|
||||
/// ```
|
||||
#[cfg(feature = "async")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
|
||||
pub fn into_async<R>(self, args: impl IntoLuaMulti) -> AsyncThread<impl IntoLuaMulti, R>
|
||||
pub fn into_async<R>(self, args: impl IntoLuaMulti) -> Result<AsyncThread<R>>
|
||||
where
|
||||
R: FromLuaMulti,
|
||||
{
|
||||
AsyncThread {
|
||||
thread: self,
|
||||
init_args: Some(args),
|
||||
ret: PhantomData,
|
||||
recycle: false,
|
||||
let lua = self.0.lua.lock();
|
||||
if !self.status_inner(&lua).is_resumable() {
|
||||
return Err(Error::CoroutineUnresumable);
|
||||
}
|
||||
|
||||
let state = lua.state();
|
||||
let thread_state = self.state();
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(state);
|
||||
|
||||
let nargs = args.push_into_stack_multi(&lua)?;
|
||||
if nargs > 0 {
|
||||
check_stack(thread_state, nargs)?;
|
||||
ffi::lua_xmove(state, thread_state, nargs);
|
||||
}
|
||||
|
||||
Ok(AsyncThread {
|
||||
thread: self,
|
||||
ret: PhantomData,
|
||||
recycle: false,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -315,7 +444,7 @@ impl Thread {
|
||||
/// Under the hood replaces the global environment table with a new table,
|
||||
/// that performs writes locally and proxies reads to caller's global environment.
|
||||
///
|
||||
/// This mode ideally should be used together with the global sandbox mode [`Lua::sandbox()`].
|
||||
/// This mode ideally should be used together with the global sandbox mode [`Lua::sandbox`].
|
||||
///
|
||||
/// Please note that Luau links environment table with chunk when loading it into Lua state.
|
||||
/// Therefore you need to load chunks into a thread to link with the thread environment.
|
||||
@@ -324,6 +453,7 @@ impl Thread {
|
||||
///
|
||||
/// ```
|
||||
/// # use mlua::{Lua, Result};
|
||||
/// # #[cfg(feature = "luau")]
|
||||
/// # fn main() -> Result<()> {
|
||||
/// let lua = Lua::new();
|
||||
/// let thread = lua.create_thread(lua.create_function(|lua2, ()| {
|
||||
@@ -332,18 +462,18 @@ impl Thread {
|
||||
/// Ok(())
|
||||
/// })?)?;
|
||||
/// thread.sandbox()?;
|
||||
/// thread.resume(())?;
|
||||
/// thread.resume::<()>(())?;
|
||||
///
|
||||
/// // The global environment should be unchanged
|
||||
/// assert_eq!(lua.globals().get::<Option<u32>>("var")?, None);
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
///
|
||||
/// Requires `feature = "luau"`
|
||||
#[cfg(any(feature = "luau", docsrs))]
|
||||
/// # #[cfg(not(feature = "luau"))]
|
||||
/// # fn main() { }
|
||||
/// ```
|
||||
#[cfg(any(feature = "luau", doc))]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
|
||||
#[doc(hidden)]
|
||||
pub fn sandbox(&self) -> Result<()> {
|
||||
let lua = self.0.lua.lock();
|
||||
let state = lua.state();
|
||||
@@ -366,37 +496,50 @@ impl Thread {
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Thread {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
fmt.debug_tuple("Thread").field(&self.0).finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for Thread {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.0 == other.0
|
||||
}
|
||||
}
|
||||
|
||||
impl LuaType for Thread {
|
||||
const TYPE_ID: c_int = ffi::LUA_TTHREAD;
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
impl<A, R> AsyncThread<A, R> {
|
||||
#[inline]
|
||||
impl<R> AsyncThread<R> {
|
||||
#[inline(always)]
|
||||
pub(crate) fn set_recyclable(&mut self, recyclable: bool) {
|
||||
self.recycle = recyclable;
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
#[cfg(any(feature = "lua54", feature = "luau"))]
|
||||
impl<A, R> Drop for AsyncThread<A, R> {
|
||||
impl<R> Drop for AsyncThread<R> {
|
||||
fn drop(&mut self) {
|
||||
if self.recycle {
|
||||
if let Some(lua) = self.thread.0.lua.try_lock() {
|
||||
unsafe {
|
||||
// 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_inner(&lua) == ThreadStatus::Error {
|
||||
#[cfg(not(feature = "vendored"))]
|
||||
ffi::lua_resetthread(self.thread.state());
|
||||
#[cfg(feature = "vendored")]
|
||||
ffi::lua_closethread(self.thread.state(), lua.state());
|
||||
let mut status = self.thread.status_inner(&lua);
|
||||
if matches!(status, ThreadStatusInner::Yielded(0)) {
|
||||
// The thread is dropped while yielded, resume it with the "terminate" signal
|
||||
ffi::lua_pushlightuserdata(self.thread.1, crate::Lua::poll_terminate().0);
|
||||
if let Ok((new_status, _)) = self.thread.resume_inner(&lua, 1) {
|
||||
// `new_status` should always be `ThreadStatusInner::Yielded(0)`
|
||||
status = new_status;
|
||||
}
|
||||
}
|
||||
|
||||
// For Lua 5.4 this also closes all pending to-be-closed variables
|
||||
if self.thread.reset_inner(status).is_ok() {
|
||||
lua.recycle_thread(&mut self.thread);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -404,14 +547,15 @@ impl<A, R> Drop for AsyncThread<A, R> {
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
impl<A: IntoLuaMulti, R: FromLuaMulti> Stream for AsyncThread<A, R> {
|
||||
impl<R: FromLuaMulti> Stream for AsyncThread<R> {
|
||||
type Item = Result<R>;
|
||||
|
||||
fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
|
||||
let lua = self.thread.0.lua.lock();
|
||||
if self.thread.status_inner(&lua) != ThreadStatus::Resumable {
|
||||
return Poll::Ready(None);
|
||||
}
|
||||
let nargs = match self.thread.status_inner(&lua) {
|
||||
ThreadStatusInner::New(nargs) | ThreadStatusInner::Yielded(nargs) => nargs,
|
||||
_ => return Poll::Ready(None),
|
||||
};
|
||||
|
||||
let state = lua.state();
|
||||
let thread_state = self.thread.state();
|
||||
@@ -420,36 +564,34 @@ impl<A: IntoLuaMulti, R: FromLuaMulti> Stream for AsyncThread<A, R> {
|
||||
let _thread_sg = StackGuard::with_top(thread_state, 0);
|
||||
let _wg = WakerGuard::new(&lua, cx.waker());
|
||||
|
||||
// This is safe as we are not moving the whole struct
|
||||
let this = self.get_unchecked_mut();
|
||||
let nresults = if let Some(args) = this.init_args.take() {
|
||||
this.thread.resume_inner(&lua, args)?
|
||||
} else {
|
||||
this.thread.resume_inner(&lua, ())?
|
||||
};
|
||||
let (status, nresults) = (self.thread).resume_inner(&lua, nargs)?;
|
||||
|
||||
if nresults == 1 && is_poll_pending(thread_state) {
|
||||
return Poll::Pending;
|
||||
if status.is_yielded() {
|
||||
if nresults == 1 && is_poll_pending(thread_state) {
|
||||
return Poll::Pending;
|
||||
}
|
||||
// Continue polling
|
||||
cx.waker().wake_by_ref();
|
||||
}
|
||||
|
||||
check_stack(state, nresults + 1)?;
|
||||
ffi::lua_xmove(thread_state, state, nresults);
|
||||
|
||||
cx.waker().wake_by_ref();
|
||||
Poll::Ready(Some(R::from_stack_multi(nresults, &lua)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
impl<A: IntoLuaMulti, R: FromLuaMulti> Future for AsyncThread<A, R> {
|
||||
impl<R: FromLuaMulti> Future for AsyncThread<R> {
|
||||
type Output = Result<R>;
|
||||
|
||||
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
let lua = self.thread.0.lua.lock();
|
||||
if self.thread.status_inner(&lua) != ThreadStatus::Resumable {
|
||||
return Poll::Ready(Err(Error::CoroutineUnresumable));
|
||||
}
|
||||
let nargs = match self.thread.status_inner(&lua) {
|
||||
ThreadStatusInner::New(nargs) | ThreadStatusInner::Yielded(nargs) => nargs,
|
||||
_ => return Poll::Ready(Err(Error::CoroutineUnresumable)),
|
||||
};
|
||||
|
||||
let state = lua.state();
|
||||
let thread_state = self.thread.state();
|
||||
@@ -458,21 +600,13 @@ impl<A: IntoLuaMulti, R: FromLuaMulti> Future for AsyncThread<A, R> {
|
||||
let _thread_sg = StackGuard::with_top(thread_state, 0);
|
||||
let _wg = WakerGuard::new(&lua, cx.waker());
|
||||
|
||||
// This is safe as we are not moving the whole struct
|
||||
let this = self.get_unchecked_mut();
|
||||
let nresults = if let Some(args) = this.init_args.take() {
|
||||
this.thread.resume_inner(&lua, args)?
|
||||
} else {
|
||||
this.thread.resume_inner(&lua, ())?
|
||||
};
|
||||
let (status, nresults) = self.thread.resume_inner(&lua, nargs)?;
|
||||
|
||||
if nresults == 1 && is_poll_pending(thread_state) {
|
||||
return Poll::Pending;
|
||||
}
|
||||
|
||||
if ffi::lua_status(thread_state) == ffi::LUA_YIELD {
|
||||
// Ignore value returned via yield()
|
||||
cx.waker().wake_by_ref();
|
||||
if status.is_yielded() {
|
||||
if !(nresults == 1 && is_poll_pending(thread_state)) {
|
||||
// Ignore value returned via yield()
|
||||
cx.waker().wake_by_ref();
|
||||
}
|
||||
return Poll::Pending;
|
||||
}
|
||||
|
||||
@@ -487,7 +621,7 @@ impl<A: IntoLuaMulti, R: FromLuaMulti> Future for AsyncThread<A, R> {
|
||||
#[cfg(feature = "async")]
|
||||
#[inline(always)]
|
||||
unsafe fn is_poll_pending(state: *mut ffi::lua_State) -> bool {
|
||||
ffi::lua_tolightuserdata(state, -1) == Lua::poll_pending().0
|
||||
ffi::lua_tolightuserdata(state, -1) == crate::Lua::poll_pending().0
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
@@ -511,7 +645,7 @@ impl<'lua, 'a> WakerGuard<'lua, 'a> {
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
impl<'lua, 'a> Drop for WakerGuard<'lua, 'a> {
|
||||
impl Drop for WakerGuard<'_, '_> {
|
||||
fn drop(&mut self) {
|
||||
unsafe { self.lua.set_waker(self.prev) };
|
||||
}
|
||||
@@ -526,7 +660,7 @@ mod assertions {
|
||||
#[cfg(feature = "send")]
|
||||
static_assertions::assert_impl_all!(Thread: Send, Sync);
|
||||
#[cfg(all(feature = "async", not(feature = "send")))]
|
||||
static_assertions::assert_not_impl_any!(AsyncThread<(), ()>: Send);
|
||||
static_assertions::assert_not_impl_any!(AsyncThread<()>: Send);
|
||||
#[cfg(all(feature = "async", feature = "send"))]
|
||||
static_assertions::assert_impl_all!(AsyncThread<(), ()>: Send, Sync);
|
||||
static_assertions::assert_impl_all!(AsyncThread<()>: Send, Sync);
|
||||
}
|
||||
|
||||
+239
-10
@@ -1,11 +1,144 @@
|
||||
use std::os::raw::c_int;
|
||||
use std::string::String as StdString;
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::error::Result;
|
||||
use crate::error::{Error, Result};
|
||||
use crate::multi::MultiValue;
|
||||
use crate::private::Sealed;
|
||||
use crate::value::{FromLua, FromLuaMulti, IntoLua, IntoLuaMulti};
|
||||
use crate::state::{Lua, RawLua};
|
||||
use crate::types::MaybeSend;
|
||||
use crate::util::{check_stack, short_type_name};
|
||||
use crate::value::Value;
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
use std::future::Future;
|
||||
use {crate::function::AsyncCallFuture, std::future::Future};
|
||||
|
||||
/// Trait for types convertible to [`Value`].
|
||||
pub trait IntoLua: Sized {
|
||||
/// Performs the conversion.
|
||||
fn into_lua(self, lua: &Lua) -> Result<Value>;
|
||||
|
||||
/// Pushes the value into the Lua stack.
|
||||
///
|
||||
/// # Safety
|
||||
/// This method does not check Lua stack space.
|
||||
#[doc(hidden)]
|
||||
#[inline]
|
||||
unsafe fn push_into_stack(self, lua: &RawLua) -> Result<()> {
|
||||
lua.push_value(&self.into_lua(lua.lua())?)
|
||||
}
|
||||
}
|
||||
|
||||
/// Trait for types convertible from [`Value`].
|
||||
pub trait FromLua: Sized {
|
||||
/// Performs the conversion.
|
||||
fn from_lua(value: Value, lua: &Lua) -> Result<Self>;
|
||||
|
||||
/// Performs the conversion for an argument (eg. function argument).
|
||||
///
|
||||
/// `i` is the argument index (position),
|
||||
/// `to` is a function name that received the argument.
|
||||
#[doc(hidden)]
|
||||
#[inline]
|
||||
fn from_lua_arg(arg: Value, i: usize, to: Option<&str>, lua: &Lua) -> Result<Self> {
|
||||
Self::from_lua(arg, lua).map_err(|err| Error::BadArgument {
|
||||
to: to.map(|s| s.to_string()),
|
||||
pos: i,
|
||||
name: None,
|
||||
cause: Arc::new(err),
|
||||
})
|
||||
}
|
||||
|
||||
/// Performs the conversion for a value in the Lua stack at index `idx`.
|
||||
#[doc(hidden)]
|
||||
#[inline]
|
||||
unsafe fn from_stack(idx: c_int, lua: &RawLua) -> Result<Self> {
|
||||
Self::from_lua(lua.stack_value(idx, None), lua.lua())
|
||||
}
|
||||
|
||||
/// Same as `from_lua_arg` but for a value in the Lua stack at index `idx`.
|
||||
#[doc(hidden)]
|
||||
#[inline]
|
||||
unsafe fn from_stack_arg(idx: c_int, i: usize, to: Option<&str>, lua: &RawLua) -> Result<Self> {
|
||||
Self::from_stack(idx, lua).map_err(|err| Error::BadArgument {
|
||||
to: to.map(|s| s.to_string()),
|
||||
pos: i,
|
||||
name: None,
|
||||
cause: Arc::new(err),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Trait for types convertible to any number of Lua values.
|
||||
///
|
||||
/// This is a generalization of [`IntoLua`], allowing any number of resulting Lua values instead of
|
||||
/// just one. Any type that implements [`IntoLua`] will automatically implement this trait.
|
||||
pub trait IntoLuaMulti: Sized {
|
||||
/// Performs the conversion.
|
||||
fn into_lua_multi(self, lua: &Lua) -> Result<MultiValue>;
|
||||
|
||||
/// Pushes the values into the Lua stack.
|
||||
///
|
||||
/// Returns number of pushed values.
|
||||
#[doc(hidden)]
|
||||
#[inline]
|
||||
unsafe fn push_into_stack_multi(self, lua: &RawLua) -> Result<c_int> {
|
||||
let values = self.into_lua_multi(lua.lua())?;
|
||||
let len: c_int = values.len().try_into().unwrap();
|
||||
unsafe {
|
||||
check_stack(lua.state(), len + 1)?;
|
||||
for val in &values {
|
||||
lua.push_value(val)?;
|
||||
}
|
||||
}
|
||||
Ok(len)
|
||||
}
|
||||
}
|
||||
|
||||
/// Trait for types that can be created from an arbitrary number of Lua values.
|
||||
///
|
||||
/// This is a generalization of [`FromLua`], allowing an arbitrary number of Lua values to
|
||||
/// participate in the conversion. Any type that implements [`FromLua`] will automatically
|
||||
/// implement this trait.
|
||||
pub trait FromLuaMulti: Sized {
|
||||
/// Performs the conversion.
|
||||
///
|
||||
/// In case `values` contains more values than needed to perform the conversion, the excess
|
||||
/// values should be ignored. This reflects the semantics of Lua when calling a function or
|
||||
/// assigning values. Similarly, if not enough values are given, conversions should assume that
|
||||
/// any missing values are nil.
|
||||
fn from_lua_multi(values: MultiValue, lua: &Lua) -> Result<Self>;
|
||||
|
||||
/// Performs the conversion for a list of arguments.
|
||||
///
|
||||
/// `i` is an index (position) of the first argument,
|
||||
/// `to` is a function name that received the arguments.
|
||||
#[doc(hidden)]
|
||||
#[inline]
|
||||
fn from_lua_args(args: MultiValue, i: usize, to: Option<&str>, lua: &Lua) -> Result<Self> {
|
||||
let _ = (i, to);
|
||||
Self::from_lua_multi(args, lua)
|
||||
}
|
||||
|
||||
/// Performs the conversion for a number of values in the Lua stack.
|
||||
#[doc(hidden)]
|
||||
#[inline]
|
||||
unsafe fn from_stack_multi(nvals: c_int, lua: &RawLua) -> Result<Self> {
|
||||
let mut values = MultiValue::with_capacity(nvals as usize);
|
||||
for idx in 0..nvals {
|
||||
values.push_back(lua.stack_value(-nvals + idx, None));
|
||||
}
|
||||
Self::from_lua_multi(values, lua.lua())
|
||||
}
|
||||
|
||||
/// Same as `from_lua_args` but for a number of values in the Lua stack.
|
||||
#[doc(hidden)]
|
||||
#[inline]
|
||||
unsafe fn from_stack_args(nargs: c_int, i: usize, to: Option<&str>, lua: &RawLua) -> Result<Self> {
|
||||
let _ = (i, to);
|
||||
Self::from_stack_multi(nargs, lua)
|
||||
}
|
||||
}
|
||||
|
||||
/// A trait for types that can be used as Lua objects (usually table and userdata).
|
||||
pub trait ObjectLike: Sealed {
|
||||
@@ -29,7 +162,7 @@ pub trait ObjectLike: Sealed {
|
||||
/// arguments.
|
||||
#[cfg(feature = "async")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
|
||||
fn call_async<R>(&self, args: impl IntoLuaMulti) -> impl Future<Output = Result<R>>
|
||||
fn call_async<R>(&self, args: impl IntoLuaMulti) -> AsyncCallFuture<R>
|
||||
where
|
||||
R: FromLuaMulti;
|
||||
|
||||
@@ -42,12 +175,10 @@ pub trait ObjectLike: Sealed {
|
||||
/// Gets the function associated to key `name` from the object and asynchronously calls it,
|
||||
/// passing the object itself along with `args` as function arguments.
|
||||
///
|
||||
/// Requires `feature = "async"`
|
||||
///
|
||||
/// This might invoke the `__index` metamethod.
|
||||
#[cfg(feature = "async")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
|
||||
fn call_async_method<R>(&self, name: &str, args: impl IntoLuaMulti) -> impl Future<Output = Result<R>>
|
||||
fn call_async_method<R>(&self, name: &str, args: impl IntoLuaMulti) -> AsyncCallFuture<R>
|
||||
where
|
||||
R: FromLuaMulti;
|
||||
|
||||
@@ -62,12 +193,10 @@ pub trait ObjectLike: Sealed {
|
||||
/// Gets the function associated to key `name` from the object and asynchronously calls it,
|
||||
/// passing `args` as function arguments.
|
||||
///
|
||||
/// Requires `feature = "async"`
|
||||
///
|
||||
/// This might invoke the `__index` metamethod.
|
||||
#[cfg(feature = "async")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
|
||||
fn call_async_function<R>(&self, name: &str, args: impl IntoLuaMulti) -> impl Future<Output = Result<R>>
|
||||
fn call_async_function<R>(&self, name: &str, args: impl IntoLuaMulti) -> AsyncCallFuture<R>
|
||||
where
|
||||
R: FromLuaMulti;
|
||||
|
||||
@@ -76,3 +205,103 @@ pub trait ObjectLike: Sealed {
|
||||
/// This might invoke the `__tostring` metamethod.
|
||||
fn to_string(&self) -> Result<StdString>;
|
||||
}
|
||||
|
||||
/// A trait for types that can be used as Lua functions.
|
||||
pub trait LuaNativeFn<A: FromLuaMulti> {
|
||||
type Output: IntoLuaMulti;
|
||||
|
||||
fn call(&self, args: A) -> Self::Output;
|
||||
}
|
||||
|
||||
/// A trait for types with mutable state that can be used as Lua functions.
|
||||
pub trait LuaNativeFnMut<A: FromLuaMulti> {
|
||||
type Output: IntoLuaMulti;
|
||||
|
||||
fn call(&mut self, args: A) -> Self::Output;
|
||||
}
|
||||
|
||||
/// A trait for types that returns a future and can be used as Lua functions.
|
||||
#[cfg(feature = "async")]
|
||||
pub trait LuaNativeAsyncFn<A: FromLuaMulti> {
|
||||
type Output: IntoLuaMulti;
|
||||
|
||||
fn call(&self, args: A) -> impl Future<Output = Self::Output> + MaybeSend + 'static;
|
||||
}
|
||||
|
||||
macro_rules! impl_lua_native_fn {
|
||||
($($A:ident),*) => {
|
||||
impl<FN, $($A,)* R> LuaNativeFn<($($A,)*)> for FN
|
||||
where
|
||||
FN: Fn($($A,)*) -> R + MaybeSend + 'static,
|
||||
($($A,)*): FromLuaMulti,
|
||||
R: IntoLuaMulti,
|
||||
{
|
||||
type Output = R;
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
fn call(&self, args: ($($A,)*)) -> Self::Output {
|
||||
let ($($A,)*) = args;
|
||||
self($($A,)*)
|
||||
}
|
||||
}
|
||||
|
||||
impl<FN, $($A,)* R> LuaNativeFnMut<($($A,)*)> for FN
|
||||
where
|
||||
FN: FnMut($($A,)*) -> R + MaybeSend + 'static,
|
||||
($($A,)*): FromLuaMulti,
|
||||
R: IntoLuaMulti,
|
||||
{
|
||||
type Output = R;
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
fn call(&mut self, args: ($($A,)*)) -> Self::Output {
|
||||
let ($($A,)*) = args;
|
||||
self($($A,)*)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
impl<FN, $($A,)* Fut, R> LuaNativeAsyncFn<($($A,)*)> for FN
|
||||
where
|
||||
FN: Fn($($A,)*) -> Fut + MaybeSend + 'static,
|
||||
($($A,)*): FromLuaMulti,
|
||||
Fut: Future<Output = R> + MaybeSend + 'static,
|
||||
R: IntoLuaMulti,
|
||||
{
|
||||
type Output = R;
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
fn call(&self, args: ($($A,)*)) -> impl Future<Output = Self::Output> + MaybeSend + 'static {
|
||||
let ($($A,)*) = args;
|
||||
self($($A,)*)
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl_lua_native_fn!();
|
||||
impl_lua_native_fn!(A);
|
||||
impl_lua_native_fn!(A, B);
|
||||
impl_lua_native_fn!(A, B, C);
|
||||
impl_lua_native_fn!(A, B, C, D);
|
||||
impl_lua_native_fn!(A, B, C, D, E);
|
||||
impl_lua_native_fn!(A, B, C, D, E, F);
|
||||
impl_lua_native_fn!(A, B, C, D, E, F, G);
|
||||
impl_lua_native_fn!(A, B, C, D, E, F, G, H);
|
||||
impl_lua_native_fn!(A, B, C, D, E, F, G, H, I);
|
||||
impl_lua_native_fn!(A, B, C, D, E, F, G, H, I, J);
|
||||
impl_lua_native_fn!(A, B, C, D, E, F, G, H, I, J, K);
|
||||
impl_lua_native_fn!(A, B, C, D, E, F, G, H, I, J, K, L);
|
||||
impl_lua_native_fn!(A, B, C, D, E, F, G, H, I, J, K, L, M);
|
||||
impl_lua_native_fn!(A, B, C, D, E, F, G, H, I, J, K, L, M, N);
|
||||
impl_lua_native_fn!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O);
|
||||
impl_lua_native_fn!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P);
|
||||
|
||||
pub(crate) trait ShortTypeName {
|
||||
#[inline(always)]
|
||||
fn type_name() -> StdString {
|
||||
short_type_name::<Self>()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> ShortTypeName for T {}
|
||||
|
||||
+46
-89
@@ -1,12 +1,10 @@
|
||||
use std::cell::UnsafeCell;
|
||||
use std::fmt;
|
||||
use std::os::raw::{c_int, c_void};
|
||||
use std::rc::Rc;
|
||||
|
||||
use crate::error::Result;
|
||||
#[cfg(not(feature = "luau"))]
|
||||
use crate::hook::Debug;
|
||||
use crate::state::{ExtraData, Lua, RawLua, WeakLua};
|
||||
use crate::debug::{Debug, HookTriggers};
|
||||
use crate::error::Result;
|
||||
use crate::state::{ExtraData, Lua, RawLua};
|
||||
|
||||
// Re-export mutex wrappers
|
||||
pub(crate) use sync::{ArcReentrantMutexGuard, ReentrantMutex, ReentrantMutexGuard, XRc, XWeak};
|
||||
@@ -18,25 +16,15 @@ pub(crate) type BoxFuture<'a, T> = futures_util::future::BoxFuture<'a, T>;
|
||||
pub(crate) type BoxFuture<'a, T> = futures_util::future::LocalBoxFuture<'a, T>;
|
||||
|
||||
pub use app_data::{AppData, AppDataRef, AppDataRefMut};
|
||||
pub use either::Either;
|
||||
pub use registry_key::RegistryKey;
|
||||
#[cfg(any(feature = "luau", doc))]
|
||||
pub use vector::Vector;
|
||||
pub(crate) use value_ref::ValueRef;
|
||||
|
||||
/// Type of Lua integer numbers.
|
||||
pub type Integer = ffi::lua_Integer;
|
||||
/// Type of Lua floating point numbers.
|
||||
pub type Number = ffi::lua_Number;
|
||||
|
||||
// Represents different subtypes wrapped to AnyUserData
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
pub(crate) enum SubtypeId {
|
||||
None,
|
||||
#[cfg(feature = "luau")]
|
||||
Buffer,
|
||||
#[cfg(feature = "luajit")]
|
||||
CData,
|
||||
}
|
||||
|
||||
/// A "light" userdata value. Equivalent to an unmanaged raw pointer.
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
pub struct LightUserData(pub *mut c_void);
|
||||
@@ -52,12 +40,14 @@ pub(crate) type Callback = Box<dyn Fn(&RawLua, c_int) -> Result<c_int> + Send +
|
||||
#[cfg(not(feature = "send"))]
|
||||
pub(crate) type Callback = Box<dyn Fn(&RawLua, c_int) -> Result<c_int> + 'static>;
|
||||
|
||||
pub(crate) type ScopedCallback<'s> = Box<dyn Fn(&RawLua, c_int) -> Result<c_int> + 's>;
|
||||
|
||||
pub(crate) struct Upvalue<T> {
|
||||
pub(crate) data: T,
|
||||
pub(crate) extra: XRc<UnsafeCell<ExtraData>>,
|
||||
}
|
||||
|
||||
pub(crate) type CallbackUpvalue = Upvalue<Callback>;
|
||||
pub(crate) type CallbackUpvalue = Upvalue<Option<Callback>>;
|
||||
|
||||
#[cfg(all(feature = "async", feature = "send"))]
|
||||
pub(crate) type AsyncCallback =
|
||||
@@ -71,33 +61,52 @@ pub(crate) type AsyncCallback =
|
||||
pub(crate) type AsyncCallbackUpvalue = Upvalue<AsyncCallback>;
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
pub(crate) type AsyncPollUpvalue = Upvalue<BoxFuture<'static, Result<c_int>>>;
|
||||
pub(crate) type AsyncPollUpvalue = Upvalue<Option<BoxFuture<'static, Result<c_int>>>>;
|
||||
|
||||
/// Type to set next Luau VM action after executing interrupt function.
|
||||
#[cfg(any(feature = "luau", doc))]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
|
||||
/// Type to set next Lua VM action after executing interrupt or hook function.
|
||||
pub enum VmState {
|
||||
Continue,
|
||||
/// Yield the current thread.
|
||||
///
|
||||
/// Supported by Lua 5.3+ and Luau.
|
||||
Yield,
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "luau"))]
|
||||
pub(crate) enum HookKind {
|
||||
Global,
|
||||
Thread(HookTriggers, HookCallback),
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "send", not(feature = "luau")))]
|
||||
pub(crate) type HookCallback = Rc<dyn Fn(&Lua, Debug) -> Result<()> + Send>;
|
||||
pub(crate) type HookCallback = XRc<dyn Fn(&Lua, &Debug) -> Result<VmState> + Send>;
|
||||
|
||||
#[cfg(all(not(feature = "send"), not(feature = "luau")))]
|
||||
pub(crate) type HookCallback = Rc<dyn Fn(&Lua, Debug) -> Result<()>>;
|
||||
pub(crate) type HookCallback = XRc<dyn Fn(&Lua, &Debug) -> Result<VmState>>;
|
||||
|
||||
#[cfg(all(feature = "send", feature = "luau"))]
|
||||
pub(crate) type InterruptCallback = Rc<dyn Fn(&Lua) -> Result<VmState> + Send>;
|
||||
pub(crate) type InterruptCallback = XRc<dyn Fn(&Lua) -> Result<VmState> + Send>;
|
||||
|
||||
#[cfg(all(not(feature = "send"), feature = "luau"))]
|
||||
pub(crate) type InterruptCallback = Rc<dyn Fn(&Lua) -> Result<VmState>>;
|
||||
pub(crate) type InterruptCallback = XRc<dyn Fn(&Lua) -> Result<VmState>>;
|
||||
|
||||
#[cfg(all(feature = "send", feature = "luau"))]
|
||||
pub(crate) type ThreadCreationCallback = XRc<dyn Fn(&Lua, crate::Thread) -> Result<()> + Send>;
|
||||
|
||||
#[cfg(all(not(feature = "send"), feature = "luau"))]
|
||||
pub(crate) type ThreadCreationCallback = XRc<dyn Fn(&Lua, crate::Thread) -> Result<()>>;
|
||||
|
||||
#[cfg(all(feature = "send", feature = "luau"))]
|
||||
pub(crate) type ThreadCollectionCallback = XRc<dyn Fn(crate::LightUserData) + Send>;
|
||||
|
||||
#[cfg(all(not(feature = "send"), feature = "luau"))]
|
||||
pub(crate) type ThreadCollectionCallback = XRc<dyn Fn(crate::LightUserData)>;
|
||||
|
||||
#[cfg(all(feature = "send", feature = "lua54"))]
|
||||
pub(crate) type WarnCallback = Box<dyn Fn(&Lua, &str, bool) -> Result<()> + Send>;
|
||||
pub(crate) type WarnCallback = XRc<dyn Fn(&Lua, &str, bool) -> Result<()> + Send>;
|
||||
|
||||
#[cfg(all(not(feature = "send"), feature = "lua54"))]
|
||||
pub(crate) type WarnCallback = Box<dyn Fn(&Lua, &str, bool) -> Result<()>>;
|
||||
pub(crate) type WarnCallback = XRc<dyn Fn(&Lua, &str, bool) -> Result<()>>;
|
||||
|
||||
/// A trait that adds `Send` requirement if `send` feature is enabled.
|
||||
#[cfg(feature = "send")]
|
||||
@@ -112,78 +121,26 @@ impl<T> MaybeSend for T {}
|
||||
|
||||
pub(crate) struct DestructedUserdata;
|
||||
|
||||
pub(crate) struct ValueRef {
|
||||
pub(crate) lua: WeakLua,
|
||||
pub(crate) index: c_int,
|
||||
pub(crate) drop: bool,
|
||||
pub(crate) trait LuaType {
|
||||
const TYPE_ID: c_int;
|
||||
}
|
||||
|
||||
impl ValueRef {
|
||||
#[inline]
|
||||
pub(crate) fn new(lua: &RawLua, index: c_int) -> Self {
|
||||
ValueRef {
|
||||
lua: lua.weak().clone(),
|
||||
index,
|
||||
drop: true,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn to_pointer(&self) -> *const c_void {
|
||||
let lua = self.lua.lock();
|
||||
unsafe { ffi::lua_topointer(lua.ref_thread(), self.index) }
|
||||
}
|
||||
|
||||
/// Returns a copy of the value, which is valid as long as the original value is held.
|
||||
#[inline]
|
||||
pub(crate) fn copy(&self) -> Self {
|
||||
ValueRef {
|
||||
lua: self.lua.clone(),
|
||||
index: self.index,
|
||||
drop: false,
|
||||
}
|
||||
}
|
||||
impl LuaType for bool {
|
||||
const TYPE_ID: c_int = ffi::LUA_TBOOLEAN;
|
||||
}
|
||||
|
||||
impl fmt::Debug for ValueRef {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "Ref({:p})", self.to_pointer())
|
||||
}
|
||||
impl LuaType for Number {
|
||||
const TYPE_ID: c_int = ffi::LUA_TNUMBER;
|
||||
}
|
||||
|
||||
impl Clone for ValueRef {
|
||||
fn clone(&self) -> Self {
|
||||
unsafe { self.lua.lock().clone_ref(self) }
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for ValueRef {
|
||||
fn drop(&mut self) {
|
||||
if self.drop {
|
||||
if let Some(lua) = self.lua.try_lock() {
|
||||
unsafe { lua.drop_ref(self) };
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for ValueRef {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
assert!(
|
||||
self.lua == other.lua,
|
||||
"Lua instance passed Value created from a different main Lua state"
|
||||
);
|
||||
let lua = self.lua.lock();
|
||||
unsafe { ffi::lua_rawequal(lua.ref_thread(), self.index, other.index) == 1 }
|
||||
}
|
||||
impl LuaType for LightUserData {
|
||||
const TYPE_ID: c_int = ffi::LUA_TLIGHTUSERDATA;
|
||||
}
|
||||
|
||||
mod app_data;
|
||||
mod registry_key;
|
||||
mod sync;
|
||||
|
||||
#[cfg(any(feature = "luau", doc))]
|
||||
mod vector;
|
||||
mod value_ref;
|
||||
|
||||
#[cfg(test)]
|
||||
mod assertions {
|
||||
|
||||
+57
-21
@@ -1,5 +1,5 @@
|
||||
use std::any::{Any, TypeId};
|
||||
use std::cell::{Cell, Ref, RefCell, RefMut, UnsafeCell};
|
||||
use std::cell::{BorrowError, BorrowMutError, Cell, Ref, RefCell, RefMut, UnsafeCell};
|
||||
use std::fmt;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use std::result::Result as StdResult;
|
||||
@@ -41,30 +41,66 @@ impl AppData {
|
||||
.and_then(|data| data.into_inner().downcast::<T>().ok().map(|data| *data)))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[track_caller]
|
||||
pub(crate) fn borrow<T: 'static>(&self, guard: Option<LuaGuard>) -> Option<AppDataRef<T>> {
|
||||
let data = unsafe { &*self.container.get() }
|
||||
.get(&TypeId::of::<T>())?
|
||||
.borrow();
|
||||
self.borrow.set(self.borrow.get() + 1);
|
||||
Some(AppDataRef {
|
||||
data: Ref::filter_map(data, |data| data.downcast_ref()).ok()?,
|
||||
borrow: &self.borrow,
|
||||
_guard: guard,
|
||||
})
|
||||
pub(crate) fn borrow<T: 'static>(&self, guard: Option<LuaGuard>) -> Option<AppDataRef<'_, T>> {
|
||||
match self.try_borrow(guard) {
|
||||
Ok(data) => data,
|
||||
Err(err) => panic!("already mutably borrowed: {err:?}"),
|
||||
}
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
pub(crate) fn borrow_mut<T: 'static>(&self, guard: Option<LuaGuard>) -> Option<AppDataRefMut<T>> {
|
||||
pub(crate) fn try_borrow<T: 'static>(
|
||||
&self,
|
||||
guard: Option<LuaGuard>,
|
||||
) -> Result<Option<AppDataRef<'_, T>>, BorrowError> {
|
||||
let data = unsafe { &*self.container.get() }
|
||||
.get(&TypeId::of::<T>())?
|
||||
.borrow_mut();
|
||||
self.borrow.set(self.borrow.get() + 1);
|
||||
Some(AppDataRefMut {
|
||||
data: RefMut::filter_map(data, |data| data.downcast_mut()).ok()?,
|
||||
borrow: &self.borrow,
|
||||
_guard: guard,
|
||||
})
|
||||
.get(&TypeId::of::<T>())
|
||||
.map(|c| c.try_borrow())
|
||||
.transpose()?
|
||||
.and_then(|data| Ref::filter_map(data, |data| data.downcast_ref()).ok());
|
||||
match data {
|
||||
Some(data) => {
|
||||
self.borrow.set(self.borrow.get() + 1);
|
||||
Ok(Some(AppDataRef {
|
||||
data,
|
||||
borrow: &self.borrow,
|
||||
_guard: guard,
|
||||
}))
|
||||
}
|
||||
None => Ok(None),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[track_caller]
|
||||
pub(crate) fn borrow_mut<T: 'static>(&self, guard: Option<LuaGuard>) -> Option<AppDataRefMut<'_, T>> {
|
||||
match self.try_borrow_mut(guard) {
|
||||
Ok(data) => data,
|
||||
Err(err) => panic!("already borrowed: {err:?}"),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn try_borrow_mut<T: 'static>(
|
||||
&self,
|
||||
guard: Option<LuaGuard>,
|
||||
) -> Result<Option<AppDataRefMut<'_, T>>, BorrowMutError> {
|
||||
let data = unsafe { &*self.container.get() }
|
||||
.get(&TypeId::of::<T>())
|
||||
.map(|c| c.try_borrow_mut())
|
||||
.transpose()?
|
||||
.and_then(|data| RefMut::filter_map(data, |data| data.downcast_mut()).ok());
|
||||
match data {
|
||||
Some(data) => {
|
||||
self.borrow.set(self.borrow.get() + 1);
|
||||
Ok(Some(AppDataRefMut {
|
||||
data,
|
||||
borrow: &self.borrow,
|
||||
_guard: guard,
|
||||
}))
|
||||
}
|
||||
None => Ok(None),
|
||||
}
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
|
||||
@@ -12,17 +12,16 @@ use parking_lot::Mutex;
|
||||
/// and instances not manually removed can be garbage collected with
|
||||
/// [`Lua::expire_registry_values`].
|
||||
///
|
||||
/// Be warned, If you place this into Lua via a [`UserData`] type or a rust callback, it is *very
|
||||
/// easy* to accidentally cause reference cycles that the Lua garbage collector cannot resolve.
|
||||
/// Instead of placing a [`RegistryKey`] into a [`UserData`] type, prefer instead to use
|
||||
/// [`AnyUserData::set_user_value`] / [`AnyUserData::user_value`].
|
||||
/// Be warned, If you place this into Lua via a [`UserData`] type or a Rust callback, it is *easy*
|
||||
/// to accidentally cause reference cycles that the Lua garbage collector cannot resolve. Instead of
|
||||
/// placing a [`RegistryKey`] into a [`UserData`] type, consider to use
|
||||
/// [`AnyUserData::set_user_value`].
|
||||
///
|
||||
/// [`UserData`]: crate::UserData
|
||||
/// [`RegistryKey`]: crate::RegistryKey
|
||||
/// [`Lua::remove_registry_value`]: crate::Lua::remove_registry_value
|
||||
/// [`Lua::expire_registry_values`]: crate::Lua::expire_registry_values
|
||||
/// [`AnyUserData::set_user_value`]: crate::AnyUserData::set_user_value
|
||||
/// [`AnyUserData::user_value`]: crate::AnyUserData::user_value
|
||||
pub struct RegistryKey {
|
||||
pub(crate) registry_id: i32,
|
||||
pub(crate) unref_list: Arc<Mutex<Option<Vec<c_int>>>>,
|
||||
|
||||
+2
-2
@@ -31,7 +31,7 @@ mod inner {
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub(crate) fn lock(&self) -> ReentrantMutexGuard<T> {
|
||||
pub(crate) fn lock(&self) -> ReentrantMutexGuard<'_, T> {
|
||||
ReentrantMutexGuard(&self.0)
|
||||
}
|
||||
|
||||
@@ -53,7 +53,7 @@ mod inner {
|
||||
|
||||
pub(crate) struct ReentrantMutexGuard<'a, T>(&'a T);
|
||||
|
||||
impl<'a, T> Deref for ReentrantMutexGuard<'a, T> {
|
||||
impl<T> Deref for ReentrantMutexGuard<'_, T> {
|
||||
type Target = T;
|
||||
|
||||
#[inline(always)]
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
use std::fmt;
|
||||
use std::os::raw::{c_int, c_void};
|
||||
|
||||
use crate::state::{RawLua, WeakLua};
|
||||
|
||||
/// A reference to a Lua (complex) value stored in the Lua auxiliary thread.
|
||||
pub struct ValueRef {
|
||||
pub(crate) lua: WeakLua,
|
||||
pub(crate) index: c_int,
|
||||
pub(crate) drop: bool,
|
||||
}
|
||||
|
||||
impl ValueRef {
|
||||
#[inline]
|
||||
pub(crate) fn new(lua: &RawLua, index: c_int) -> Self {
|
||||
ValueRef {
|
||||
lua: lua.weak().clone(),
|
||||
index,
|
||||
drop: true,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn to_pointer(&self) -> *const c_void {
|
||||
let lua = self.lua.lock();
|
||||
unsafe { ffi::lua_topointer(lua.ref_thread(), self.index) }
|
||||
}
|
||||
|
||||
/// Returns a copy of the value, which is valid as long as the original value is held.
|
||||
#[inline]
|
||||
pub(crate) fn copy(&self) -> Self {
|
||||
ValueRef {
|
||||
lua: self.lua.clone(),
|
||||
index: self.index,
|
||||
drop: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for ValueRef {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "Ref({:p})", self.to_pointer())
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for ValueRef {
|
||||
fn clone(&self) -> Self {
|
||||
unsafe { self.lua.lock().clone_ref(self) }
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for ValueRef {
|
||||
fn drop(&mut self) {
|
||||
if self.drop {
|
||||
if let Some(lua) = self.lua.try_lock() {
|
||||
unsafe { lua.drop_ref(self) };
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for ValueRef {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
assert!(
|
||||
self.lua == other.lua,
|
||||
"Lua instance passed Value created from a different main Lua state"
|
||||
);
|
||||
let lua = self.lua.lock();
|
||||
unsafe { ffi::lua_rawequal(lua.ref_thread(), self.index, other.index) == 1 }
|
||||
}
|
||||
}
|
||||
+253
-327
File diff suppressed because it is too large
Load Diff
+184
-277
@@ -1,33 +1,31 @@
|
||||
use std::any::{type_name, TypeId};
|
||||
use std::cell::UnsafeCell;
|
||||
use std::fmt;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use std::os::raw::c_int;
|
||||
use std::cell::{RefCell, UnsafeCell};
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
#[cfg(feature = "serde")]
|
||||
use serde::ser::{Serialize, Serializer};
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
use crate::state::{Lua, RawLua};
|
||||
use crate::types::{MaybeSend, XRc};
|
||||
use crate::userdata::AnyUserData;
|
||||
use crate::util::get_userdata;
|
||||
use crate::value::{FromLua, Value};
|
||||
use crate::types::XRc;
|
||||
|
||||
use super::lock::{RawLock, UserDataLock};
|
||||
use super::r#ref::{UserDataRef, UserDataRefMut};
|
||||
|
||||
#[cfg(all(feature = "serialize", not(feature = "send")))]
|
||||
#[cfg(all(feature = "serde", not(feature = "send")))]
|
||||
type DynSerialize = dyn erased_serde::Serialize;
|
||||
|
||||
#[cfg(all(feature = "serialize", feature = "send"))]
|
||||
#[cfg(all(feature = "serde", feature = "send"))]
|
||||
type DynSerialize = dyn erased_serde::Serialize + Send;
|
||||
|
||||
pub(crate) enum UserDataStorage<T> {
|
||||
Owned(UserDataVariant<T>),
|
||||
Scoped(ScopedUserDataVariant<T>),
|
||||
}
|
||||
|
||||
// A enum for storing userdata values.
|
||||
// It's stored inside a Lua VM and protected by the outer `ReentrantMutex`.
|
||||
pub(crate) enum UserDataVariant<T> {
|
||||
Default(XRc<UserDataCell<T>>),
|
||||
#[cfg(feature = "serialize")]
|
||||
Serializable(XRc<UserDataCell<Box<DynSerialize>>>),
|
||||
#[cfg(feature = "serde")]
|
||||
Serializable(XRc<UserDataCell<Box<DynSerialize>>>, bool), // bool is `is_sync`
|
||||
}
|
||||
|
||||
impl<T> Clone for UserDataVariant<T> {
|
||||
@@ -35,53 +33,54 @@ impl<T> Clone for UserDataVariant<T> {
|
||||
fn clone(&self) -> Self {
|
||||
match self {
|
||||
Self::Default(inner) => Self::Default(XRc::clone(inner)),
|
||||
#[cfg(feature = "serialize")]
|
||||
Self::Serializable(inner) => Self::Serializable(XRc::clone(inner)),
|
||||
#[cfg(feature = "serde")]
|
||||
Self::Serializable(inner, is_sync) => Self::Serializable(XRc::clone(inner), *is_sync),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> UserDataVariant<T> {
|
||||
#[inline(always)]
|
||||
pub(crate) fn new(data: T) -> Self {
|
||||
Self::Default(XRc::new(UserDataCell::new(data)))
|
||||
}
|
||||
|
||||
// Immutably borrows the wrapped value in-place.
|
||||
#[inline(always)]
|
||||
pub(crate) fn try_borrow(&self) -> Result<UserDataBorrowRef<T>> {
|
||||
UserDataBorrowRef::try_from(self)
|
||||
}
|
||||
|
||||
// Immutably borrows the wrapped value and returns an owned reference.
|
||||
#[inline(always)]
|
||||
pub(crate) fn try_borrow_owned(&self) -> Result<UserDataRef<T>> {
|
||||
UserDataRef::try_from(self.clone())
|
||||
pub(super) fn try_borrow_scoped<R>(&self, f: impl FnOnce(&T) -> R) -> Result<R> {
|
||||
// We don't need to check for `T: Sync` because when this method is used (internally),
|
||||
// Lua mutex is already locked.
|
||||
// If non-`Sync` userdata is already borrowed by another thread (via `UserDataRef`), it will be
|
||||
// exclusively locked.
|
||||
let _guard = (self.raw_lock().try_lock_shared_guarded()).map_err(|_| Error::UserDataBorrowError)?;
|
||||
Ok(f(unsafe { &*self.as_ptr() }))
|
||||
}
|
||||
|
||||
// Mutably borrows the wrapped value in-place.
|
||||
#[inline(always)]
|
||||
pub(crate) fn try_borrow_mut(&self) -> Result<UserDataBorrowMut<T>> {
|
||||
UserDataBorrowMut::try_from(self)
|
||||
fn try_borrow_scoped_mut<R>(&self, f: impl FnOnce(&mut T) -> R) -> Result<R> {
|
||||
let _guard =
|
||||
(self.raw_lock().try_lock_exclusive_guarded()).map_err(|_| Error::UserDataBorrowMutError)?;
|
||||
Ok(f(unsafe { &mut *self.as_ptr() }))
|
||||
}
|
||||
|
||||
// Immutably borrows the wrapped value and returns an owned reference.
|
||||
#[inline(always)]
|
||||
fn try_borrow_owned(&self) -> Result<UserDataRef<T>> {
|
||||
UserDataRef::try_from(self.clone())
|
||||
}
|
||||
|
||||
// Mutably borrows the wrapped value and returns an owned reference.
|
||||
#[inline(always)]
|
||||
pub(crate) fn try_borrow_owned_mut(&self) -> Result<UserDataRefMut<T>> {
|
||||
fn try_borrow_owned_mut(&self) -> Result<UserDataRefMut<T>> {
|
||||
UserDataRefMut::try_from(self.clone())
|
||||
}
|
||||
|
||||
// Returns the wrapped value.
|
||||
//
|
||||
// This method checks that we have exclusive access to the value.
|
||||
pub(crate) fn into_inner(self) -> Result<T> {
|
||||
fn into_inner(self) -> Result<T> {
|
||||
if !self.raw_lock().try_lock_exclusive() {
|
||||
return Err(Error::UserDataBorrowMutError);
|
||||
}
|
||||
Ok(match self {
|
||||
Self::Default(inner) => XRc::into_inner(inner).unwrap().value.into_inner(),
|
||||
#[cfg(feature = "serialize")]
|
||||
Self::Serializable(inner) => unsafe {
|
||||
#[cfg(feature = "serde")]
|
||||
Self::Serializable(inner, _) => unsafe {
|
||||
let raw = Box::into_raw(XRc::into_inner(inner).unwrap().value.into_inner());
|
||||
*Box::from_raw(raw as *mut T)
|
||||
},
|
||||
@@ -89,47 +88,57 @@ impl<T> UserDataVariant<T> {
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn raw_lock(&self) -> &RawLock {
|
||||
fn strong_count(&self) -> usize {
|
||||
match self {
|
||||
Self::Default(inner) => XRc::strong_count(inner),
|
||||
#[cfg(feature = "serde")]
|
||||
Self::Serializable(inner, _) => XRc::strong_count(inner),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub(super) fn raw_lock(&self) -> &RawLock {
|
||||
match self {
|
||||
Self::Default(inner) => &inner.raw_lock,
|
||||
#[cfg(feature = "serialize")]
|
||||
Self::Serializable(inner) => &inner.raw_lock,
|
||||
#[cfg(feature = "serde")]
|
||||
Self::Serializable(inner, _) => &inner.raw_lock,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn as_ptr(&self) -> *mut T {
|
||||
pub(super) fn as_ptr(&self) -> *mut T {
|
||||
match self {
|
||||
Self::Default(inner) => inner.value.get(),
|
||||
#[cfg(feature = "serialize")]
|
||||
Self::Serializable(inner) => unsafe { &mut **(inner.value.get() as *mut Box<T>) },
|
||||
#[cfg(feature = "serde")]
|
||||
Self::Serializable(inner, _) => unsafe { &mut **(inner.value.get() as *mut Box<T>) },
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
impl<T: Serialize + MaybeSend + 'static> UserDataVariant<T> {
|
||||
#[inline(always)]
|
||||
pub(crate) fn new_ser(data: T) -> Self {
|
||||
let data = Box::new(data) as Box<DynSerialize>;
|
||||
Self::Serializable(XRc::new(UserDataCell::new(data)))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
impl Serialize for UserDataVariant<()> {
|
||||
#[cfg(feature = "serde")]
|
||||
impl Serialize for UserDataStorage<()> {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error> {
|
||||
match self {
|
||||
Self::Default(_) => Err(serde::ser::Error::custom("cannot serialize <userdata>")),
|
||||
Self::Serializable(inner) => unsafe {
|
||||
// We need to borrow the inner value exclusively to serialize it.
|
||||
Self::Owned(variant @ UserDataVariant::Serializable(inner, is_sync)) => unsafe {
|
||||
#[cfg(feature = "send")]
|
||||
let _guard = self.try_borrow_mut().map_err(serde::ser::Error::custom)?;
|
||||
// No need to do this if the `send` feature is disabled.
|
||||
if *is_sync {
|
||||
let _guard = (variant.raw_lock().try_lock_shared_guarded())
|
||||
.map_err(|_| serde::ser::Error::custom(Error::UserDataBorrowError))?;
|
||||
(*inner.value.get()).serialize(serializer)
|
||||
} else {
|
||||
let _guard = (variant.raw_lock().try_lock_exclusive_guarded())
|
||||
.map_err(|_| serde::ser::Error::custom(Error::UserDataBorrowError))?;
|
||||
(*inner.value.get()).serialize(serializer)
|
||||
}
|
||||
#[cfg(not(feature = "send"))]
|
||||
let _guard = self.try_borrow().map_err(serde::ser::Error::custom)?;
|
||||
(*inner.value.get()).serialize(serializer)
|
||||
{
|
||||
let _ = is_sync;
|
||||
let _guard = (variant.raw_lock().try_lock_shared_guarded())
|
||||
.map_err(|_| serde::ser::Error::custom(Error::UserDataBorrowError))?;
|
||||
(*inner.value.get()).serialize(serializer)
|
||||
}
|
||||
},
|
||||
_ => Err(serde::ser::Error::custom("cannot serialize <userdata>")),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -140,12 +149,14 @@ pub(crate) struct UserDataCell<T> {
|
||||
value: UnsafeCell<T>,
|
||||
}
|
||||
|
||||
#[cfg(feature = "send")]
|
||||
unsafe impl<T: Send> Send for UserDataCell<T> {}
|
||||
#[cfg(feature = "send")]
|
||||
unsafe impl<T: Send> Sync for UserDataCell<T> {}
|
||||
|
||||
impl<T> UserDataCell<T> {
|
||||
#[inline(always)]
|
||||
pub fn new(value: T) -> Self {
|
||||
fn new(value: T) -> Self {
|
||||
UserDataCell {
|
||||
raw_lock: RawLock::INIT,
|
||||
value: UnsafeCell::new(value),
|
||||
@@ -153,239 +164,135 @@ impl<T> UserDataCell<T> {
|
||||
}
|
||||
}
|
||||
|
||||
/// A wrapper type for a [`UserData`] value that provides read access.
|
||||
///
|
||||
/// It implements [`FromLua`] and can be used to receive a typed userdata from Lua.
|
||||
pub struct UserDataRef<T>(UserDataVariant<T>);
|
||||
|
||||
impl<T> Deref for UserDataRef<T> {
|
||||
type Target = T;
|
||||
|
||||
#[inline]
|
||||
fn deref(&self) -> &T {
|
||||
unsafe { &*self.0.as_ptr() }
|
||||
}
|
||||
pub(crate) enum ScopedUserDataVariant<T> {
|
||||
Ref(*const T),
|
||||
RefMut(RefCell<*mut T>),
|
||||
Boxed(RefCell<*mut T>),
|
||||
}
|
||||
|
||||
impl<T> Drop for UserDataRef<T> {
|
||||
impl<T> Drop for ScopedUserDataVariant<T> {
|
||||
#[inline]
|
||||
fn drop(&mut self) {
|
||||
unsafe { self.0.raw_lock().unlock_shared() };
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: fmt::Debug> fmt::Debug for UserDataRef<T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
(**self).fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: fmt::Display> fmt::Display for UserDataRef<T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
(**self).fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> TryFrom<UserDataVariant<T>> for UserDataRef<T> {
|
||||
type Error = Error;
|
||||
|
||||
#[inline]
|
||||
fn try_from(variant: UserDataVariant<T>) -> Result<Self> {
|
||||
if !variant.raw_lock().try_lock_shared() {
|
||||
return Err(Error::UserDataBorrowError);
|
||||
}
|
||||
Ok(UserDataRef(variant))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: 'static> FromLua for UserDataRef<T> {
|
||||
fn from_lua(value: Value, _: &Lua) -> Result<Self> {
|
||||
try_value_to_userdata::<T>(value)?.borrow()
|
||||
}
|
||||
|
||||
unsafe fn from_stack(idx: c_int, lua: &RawLua) -> Result<Self> {
|
||||
let type_id = lua.get_userdata_type_id(idx)?;
|
||||
match type_id {
|
||||
Some(type_id) if type_id == TypeId::of::<T>() => {
|
||||
(*get_userdata::<UserDataVariant<T>>(lua.state(), idx)).try_borrow_owned()
|
||||
if let Self::Boxed(value) = self {
|
||||
if let Ok(value) = value.try_borrow_mut() {
|
||||
unsafe { drop(Box::from_raw(*value)) };
|
||||
}
|
||||
_ => Err(Error::UserDataTypeMismatch),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A wrapper type for a mutably borrowed value from a `AnyUserData`.
|
||||
///
|
||||
/// It implements [`FromLua`] and can be used to receive a typed userdata from Lua.
|
||||
pub struct UserDataRefMut<T>(UserDataVariant<T>);
|
||||
|
||||
impl<T> Deref for UserDataRefMut<T> {
|
||||
type Target = T;
|
||||
|
||||
#[inline]
|
||||
fn deref(&self) -> &Self::Target {
|
||||
unsafe { &*self.0.as_ptr() }
|
||||
impl<T: 'static> UserDataStorage<T> {
|
||||
#[inline(always)]
|
||||
pub(crate) fn new(data: T) -> Self {
|
||||
Self::Owned(UserDataVariant::Default(XRc::new(UserDataCell::new(data))))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> DerefMut for UserDataRefMut<T> {
|
||||
#[inline]
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
unsafe { &mut *self.0.as_ptr() }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Drop for UserDataRefMut<T> {
|
||||
#[inline]
|
||||
fn drop(&mut self) {
|
||||
unsafe { self.0.raw_lock().unlock_exclusive() };
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: fmt::Debug> fmt::Debug for UserDataRefMut<T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
(**self).fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: fmt::Display> fmt::Display for UserDataRefMut<T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
(**self).fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> TryFrom<UserDataVariant<T>> for UserDataRefMut<T> {
|
||||
type Error = Error;
|
||||
|
||||
#[inline]
|
||||
fn try_from(variant: UserDataVariant<T>) -> Result<Self> {
|
||||
if !variant.raw_lock().try_lock_exclusive() {
|
||||
return Err(Error::UserDataBorrowMutError);
|
||||
}
|
||||
Ok(UserDataRefMut(variant))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: 'static> FromLua for UserDataRefMut<T> {
|
||||
fn from_lua(value: Value, _: &Lua) -> Result<Self> {
|
||||
try_value_to_userdata::<T>(value)?.borrow_mut()
|
||||
}
|
||||
|
||||
unsafe fn from_stack(idx: c_int, lua: &RawLua) -> Result<Self> {
|
||||
let type_id = lua.get_userdata_type_id(idx)?;
|
||||
match type_id {
|
||||
Some(type_id) if type_id == TypeId::of::<T>() => {
|
||||
(*get_userdata::<UserDataVariant<T>>(lua.state(), idx)).try_borrow_owned_mut()
|
||||
}
|
||||
_ => Err(Error::UserDataTypeMismatch),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A type that provides read access to a userdata value (borrowing the value).
|
||||
pub(crate) struct UserDataBorrowRef<'a, T>(&'a UserDataVariant<T>);
|
||||
|
||||
impl<'a, T> Drop for UserDataBorrowRef<'a, T> {
|
||||
#[inline]
|
||||
fn drop(&mut self) {
|
||||
unsafe { self.0.raw_lock().unlock_shared() };
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> Deref for UserDataBorrowRef<'a, T> {
|
||||
type Target = T;
|
||||
|
||||
#[inline]
|
||||
fn deref(&self) -> &T {
|
||||
// SAFETY: `UserDataBorrowRef` is only created with shared access to the value.
|
||||
unsafe { &*self.0.as_ptr() }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> TryFrom<&'a UserDataVariant<T>> for UserDataBorrowRef<'a, T> {
|
||||
type Error = Error;
|
||||
|
||||
#[inline(always)]
|
||||
fn try_from(variant: &'a UserDataVariant<T>) -> Result<Self> {
|
||||
if !variant.raw_lock().try_lock_shared() {
|
||||
return Err(Error::UserDataBorrowError);
|
||||
}
|
||||
Ok(UserDataBorrowRef(variant))
|
||||
pub(crate) fn new_ref(data: &T) -> Self {
|
||||
Self::Scoped(ScopedUserDataVariant::Ref(data))
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct UserDataBorrowMut<'a, T>(&'a UserDataVariant<T>);
|
||||
|
||||
impl<'a, T> Drop for UserDataBorrowMut<'a, T> {
|
||||
#[inline]
|
||||
fn drop(&mut self) {
|
||||
unsafe { self.0.raw_lock().unlock_exclusive() };
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> Deref for UserDataBorrowMut<'a, T> {
|
||||
type Target = T;
|
||||
|
||||
#[inline]
|
||||
fn deref(&self) -> &T {
|
||||
unsafe { &*self.0.as_ptr() }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> DerefMut for UserDataBorrowMut<'a, T> {
|
||||
#[inline]
|
||||
fn deref_mut(&mut self) -> &mut T {
|
||||
unsafe { &mut *self.0.as_ptr() }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> TryFrom<&'a UserDataVariant<T>> for UserDataBorrowMut<'a, T> {
|
||||
type Error = Error;
|
||||
|
||||
#[inline(always)]
|
||||
fn try_from(variant: &'a UserDataVariant<T>) -> Result<Self> {
|
||||
if !variant.raw_lock().try_lock_exclusive() {
|
||||
return Err(Error::UserDataBorrowMutError);
|
||||
pub(crate) fn new_ref_mut(data: &mut T) -> Self {
|
||||
Self::Scoped(ScopedUserDataVariant::RefMut(RefCell::new(data)))
|
||||
}
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
#[inline(always)]
|
||||
pub(crate) fn new_ser(data: T) -> Self
|
||||
where
|
||||
T: Serialize + crate::types::MaybeSend,
|
||||
{
|
||||
let data = Box::new(data) as Box<DynSerialize>;
|
||||
let is_sync = super::util::is_sync::<T>();
|
||||
let variant = UserDataVariant::Serializable(XRc::new(UserDataCell::new(data)), is_sync);
|
||||
Self::Owned(variant)
|
||||
}
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
#[inline(always)]
|
||||
pub(crate) fn is_serializable(&self) -> bool {
|
||||
matches!(self, Self::Owned(UserDataVariant::Serializable(..)))
|
||||
}
|
||||
|
||||
// Immutably borrows the wrapped value and returns an owned reference.
|
||||
#[inline(always)]
|
||||
pub(crate) fn try_borrow_owned(&self) -> Result<UserDataRef<T>> {
|
||||
match self {
|
||||
Self::Owned(data) => data.try_borrow_owned(),
|
||||
Self::Scoped(_) => Err(Error::UserDataTypeMismatch),
|
||||
}
|
||||
}
|
||||
|
||||
// Mutably borrows the wrapped value and returns an owned reference.
|
||||
#[inline(always)]
|
||||
pub(crate) fn try_borrow_owned_mut(&self) -> Result<UserDataRefMut<T>> {
|
||||
match self {
|
||||
Self::Owned(data) => data.try_borrow_owned_mut(),
|
||||
Self::Scoped(_) => Err(Error::UserDataTypeMismatch),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub(crate) fn into_inner(self) -> Result<T> {
|
||||
match self {
|
||||
Self::Owned(data) => data.into_inner(),
|
||||
Self::Scoped(_) => Err(Error::UserDataTypeMismatch),
|
||||
}
|
||||
Ok(UserDataBorrowMut(variant))
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn try_value_to_userdata<T>(value: Value) -> Result<AnyUserData> {
|
||||
match value {
|
||||
Value::UserData(ud) => Ok(ud),
|
||||
_ => Err(Error::FromLuaConversionError {
|
||||
from: value.type_name(),
|
||||
to: "userdata",
|
||||
message: Some(format!("expected userdata of type {}", type_name::<T>())),
|
||||
}),
|
||||
impl<T> UserDataStorage<T> {
|
||||
#[inline(always)]
|
||||
pub(crate) fn new_scoped(data: T) -> Self {
|
||||
let data = Box::into_raw(Box::new(data));
|
||||
Self::Scoped(ScopedUserDataVariant::Boxed(RefCell::new(data)))
|
||||
}
|
||||
|
||||
/// Returns `true` if it's safe to destroy the container.
|
||||
///
|
||||
/// It's safe to destroy the container if the reference count is greater than 1 or the lock is
|
||||
/// not acquired.
|
||||
#[inline(always)]
|
||||
pub(crate) fn is_safe_to_destroy(&self) -> bool {
|
||||
match self {
|
||||
Self::Owned(variant) => variant.strong_count() > 1 || !variant.raw_lock().is_locked(),
|
||||
Self::Scoped(_) => false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `true` if the container has exclusive access to the value.
|
||||
#[inline(always)]
|
||||
pub(crate) fn has_exclusive_access(&self) -> bool {
|
||||
match self {
|
||||
Self::Owned(variant) => !variant.raw_lock().is_locked(),
|
||||
Self::Scoped(_) => false,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn try_borrow_scoped<R>(&self, f: impl FnOnce(&T) -> R) -> Result<R> {
|
||||
match self {
|
||||
Self::Owned(data) => data.try_borrow_scoped(f),
|
||||
Self::Scoped(ScopedUserDataVariant::Ref(value)) => Ok(f(unsafe { &**value })),
|
||||
Self::Scoped(ScopedUserDataVariant::RefMut(value) | ScopedUserDataVariant::Boxed(value)) => {
|
||||
let t = value.try_borrow().map_err(|_| Error::UserDataBorrowError)?;
|
||||
Ok(f(unsafe { &**t }))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn try_borrow_scoped_mut<R>(&self, f: impl FnOnce(&mut T) -> R) -> Result<R> {
|
||||
match self {
|
||||
Self::Owned(data) => data.try_borrow_scoped_mut(f),
|
||||
Self::Scoped(ScopedUserDataVariant::Ref(_)) => Err(Error::UserDataBorrowMutError),
|
||||
Self::Scoped(ScopedUserDataVariant::RefMut(value) | ScopedUserDataVariant::Boxed(value)) => {
|
||||
let mut t = value
|
||||
.try_borrow_mut()
|
||||
.map_err(|_| Error::UserDataBorrowMutError)?;
|
||||
Ok(f(unsafe { &mut **t }))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod assertions {
|
||||
use super::*;
|
||||
|
||||
#[cfg(feature = "send")]
|
||||
static_assertions::assert_impl_all!(UserDataRef<()>: Send, Sync);
|
||||
#[cfg(feature = "send")]
|
||||
static_assertions::assert_not_impl_all!(UserDataRef<std::rc::Rc<()>>: Send, Sync);
|
||||
#[cfg(feature = "send")]
|
||||
static_assertions::assert_impl_all!(UserDataRefMut<()>: Sync, Send);
|
||||
#[cfg(feature = "send")]
|
||||
static_assertions::assert_not_impl_all!(UserDataRefMut<std::rc::Rc<()>>: Send, Sync);
|
||||
#[cfg(feature = "send")]
|
||||
static_assertions::assert_impl_all!(UserDataBorrowRef<'_, ()>: Send, Sync);
|
||||
#[cfg(feature = "send")]
|
||||
static_assertions::assert_impl_all!(UserDataBorrowMut<'_, ()>: Send, Sync);
|
||||
|
||||
#[cfg(not(feature = "send"))]
|
||||
static_assertions::assert_not_impl_all!(UserDataRef<()>: Send, Sync);
|
||||
#[cfg(not(feature = "send"))]
|
||||
static_assertions::assert_not_impl_all!(UserDataRefMut<()>: Send, Sync);
|
||||
#[cfg(not(feature = "send"))]
|
||||
static_assertions::assert_not_impl_all!(UserDataBorrowRef<'_, ()>: Send, Sync);
|
||||
#[cfg(not(feature = "send"))]
|
||||
static_assertions::assert_not_impl_all!(UserDataBorrowMut<'_, ()>: Send, Sync);
|
||||
}
|
||||
|
||||
+58
-7
@@ -1,16 +1,57 @@
|
||||
pub(crate) trait UserDataLock {
|
||||
const INIT: Self;
|
||||
|
||||
fn is_locked(&self) -> bool;
|
||||
fn try_lock_shared(&self) -> bool;
|
||||
fn try_lock_exclusive(&self) -> bool;
|
||||
|
||||
unsafe fn unlock_shared(&self);
|
||||
unsafe fn unlock_exclusive(&self);
|
||||
|
||||
fn try_lock_shared_guarded(&self) -> Result<LockGuard<'_, Self>, ()> {
|
||||
if self.try_lock_shared() {
|
||||
Ok(LockGuard {
|
||||
lock: self,
|
||||
exclusive: false,
|
||||
})
|
||||
} else {
|
||||
Err(())
|
||||
}
|
||||
}
|
||||
|
||||
fn try_lock_exclusive_guarded(&self) -> Result<LockGuard<'_, Self>, ()> {
|
||||
if self.try_lock_exclusive() {
|
||||
Ok(LockGuard {
|
||||
lock: self,
|
||||
exclusive: true,
|
||||
})
|
||||
} else {
|
||||
Err(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct LockGuard<'a, L: UserDataLock + ?Sized> {
|
||||
lock: &'a L,
|
||||
exclusive: bool,
|
||||
}
|
||||
|
||||
impl<L: UserDataLock + ?Sized> Drop for LockGuard<'_, L> {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
if self.exclusive {
|
||||
self.lock.unlock_exclusive();
|
||||
} else {
|
||||
self.lock.unlock_shared();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) use lock_impl::RawLock;
|
||||
|
||||
#[cfg(not(feature = "send"))]
|
||||
#[cfg(not(tarpaulin_include))]
|
||||
mod lock_impl {
|
||||
use std::cell::Cell;
|
||||
|
||||
@@ -24,6 +65,11 @@ mod lock_impl {
|
||||
#[allow(clippy::declare_interior_mutable_const)]
|
||||
const INIT: Self = Cell::new(UNUSED);
|
||||
|
||||
#[inline(always)]
|
||||
fn is_locked(&self) -> bool {
|
||||
self.get() != UNUSED
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn try_lock_shared(&self) -> bool {
|
||||
let flag = self.get().wrapping_add(1);
|
||||
@@ -62,32 +108,37 @@ mod lock_impl {
|
||||
|
||||
#[cfg(feature = "send")]
|
||||
mod lock_impl {
|
||||
use parking_lot::lock_api::RawMutex;
|
||||
use parking_lot::lock_api::RawRwLock;
|
||||
|
||||
pub(crate) type RawLock = parking_lot::RawMutex;
|
||||
pub(crate) type RawLock = parking_lot::RawRwLock;
|
||||
|
||||
impl super::UserDataLock for RawLock {
|
||||
#[allow(clippy::declare_interior_mutable_const)]
|
||||
const INIT: Self = <Self as parking_lot::lock_api::RawMutex>::INIT;
|
||||
const INIT: Self = <Self as parking_lot::lock_api::RawRwLock>::INIT;
|
||||
|
||||
#[inline(always)]
|
||||
fn is_locked(&self) -> bool {
|
||||
RawRwLock::is_locked(self)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn try_lock_shared(&self) -> bool {
|
||||
RawLock::try_lock(self)
|
||||
RawRwLock::try_lock_shared(self)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn try_lock_exclusive(&self) -> bool {
|
||||
RawLock::try_lock(self)
|
||||
RawRwLock::try_lock_exclusive(self)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
unsafe fn unlock_shared(&self) {
|
||||
RawLock::unlock(self)
|
||||
RawRwLock::unlock_shared(self)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
unsafe fn unlock_exclusive(&self) {
|
||||
RawLock::unlock(self)
|
||||
RawRwLock::unlock_exclusive(self)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+10
-10
@@ -2,13 +2,13 @@ use std::string::String as StdString;
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
use crate::table::Table;
|
||||
use crate::traits::ObjectLike;
|
||||
use crate::traits::{FromLua, FromLuaMulti, IntoLua, IntoLuaMulti, ObjectLike};
|
||||
use crate::userdata::AnyUserData;
|
||||
use crate::value::{FromLua, FromLuaMulti, IntoLua, IntoLuaMulti, Value};
|
||||
use crate::value::Value;
|
||||
use crate::Function;
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
use futures_util::future::{self, Either, Future};
|
||||
use crate::function::AsyncCallFuture;
|
||||
|
||||
impl ObjectLike for AnyUserData {
|
||||
#[inline]
|
||||
@@ -35,7 +35,7 @@ impl ObjectLike for AnyUserData {
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
#[inline]
|
||||
fn call_async<R>(&self, args: impl IntoLuaMulti) -> impl Future<Output = Result<R>>
|
||||
fn call_async<R>(&self, args: impl IntoLuaMulti) -> AsyncCallFuture<R>
|
||||
where
|
||||
R: FromLuaMulti,
|
||||
{
|
||||
@@ -51,7 +51,7 @@ impl ObjectLike for AnyUserData {
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
fn call_async_method<R>(&self, name: &str, args: impl IntoLuaMulti) -> impl Future<Output = Result<R>>
|
||||
fn call_async_method<R>(&self, name: &str, args: impl IntoLuaMulti) -> AsyncCallFuture<R>
|
||||
where
|
||||
R: FromLuaMulti,
|
||||
{
|
||||
@@ -72,22 +72,22 @@ impl ObjectLike for AnyUserData {
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
fn call_async_function<R>(&self, name: &str, args: impl IntoLuaMulti) -> impl Future<Output = Result<R>>
|
||||
fn call_async_function<R>(&self, name: &str, args: impl IntoLuaMulti) -> AsyncCallFuture<R>
|
||||
where
|
||||
R: FromLuaMulti,
|
||||
{
|
||||
match self.get(name) {
|
||||
Ok(Value::Function(func)) => Either::Left(func.call_async(args)),
|
||||
Ok(Value::Function(func)) => func.call_async(args),
|
||||
Ok(val) => {
|
||||
let msg = format!("attempt to call a {} value (function '{name}')", val.type_name());
|
||||
Either::Right(future::ready(Err(Error::RuntimeError(msg))))
|
||||
AsyncCallFuture::error(Error::RuntimeError(msg))
|
||||
}
|
||||
Err(err) => Either::Right(future::ready(Err(err))),
|
||||
Err(err) => AsyncCallFuture::error(err),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn to_string(&self) -> Result<StdString> {
|
||||
Value::UserData(AnyUserData(self.0.copy(), self.1)).to_string()
|
||||
Value::UserData(AnyUserData(self.0.copy())).to_string()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,474 @@
|
||||
use std::any::{type_name, TypeId};
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use std::os::raw::c_int;
|
||||
use std::{fmt, mem};
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
use crate::state::{Lua, RawLua};
|
||||
use crate::traits::FromLua;
|
||||
use crate::userdata::AnyUserData;
|
||||
use crate::util::get_userdata;
|
||||
use crate::value::Value;
|
||||
|
||||
use super::cell::{UserDataStorage, UserDataVariant};
|
||||
use super::lock::{LockGuard, RawLock, UserDataLock};
|
||||
use super::util::is_sync;
|
||||
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
use {
|
||||
parking_lot::{
|
||||
Mutex as MutexPL, MutexGuard as MutexGuardPL, RwLock as RwLockPL,
|
||||
RwLockReadGuard as RwLockReadGuardPL, RwLockWriteGuard as RwLockWriteGuardPL,
|
||||
},
|
||||
std::sync::Arc,
|
||||
};
|
||||
#[cfg(all(feature = "userdata-wrappers", not(feature = "send")))]
|
||||
use {
|
||||
std::cell::{Ref, RefCell, RefMut},
|
||||
std::rc::Rc,
|
||||
};
|
||||
|
||||
/// A wrapper type for a userdata value that provides read access.
|
||||
///
|
||||
/// It implements [`FromLua`] and can be used to receive a typed userdata from Lua.
|
||||
pub struct UserDataRef<T: 'static> {
|
||||
// It's important to drop the guard first, as it refers to the `inner` data.
|
||||
_guard: LockGuard<'static, RawLock>,
|
||||
inner: UserDataRefInner<T>,
|
||||
}
|
||||
|
||||
impl<T> Deref for UserDataRef<T> {
|
||||
type Target = T;
|
||||
|
||||
#[inline]
|
||||
fn deref(&self) -> &T {
|
||||
&self.inner
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: fmt::Debug> fmt::Debug for UserDataRef<T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
(**self).fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: fmt::Display> fmt::Display for UserDataRef<T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
(**self).fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> TryFrom<UserDataVariant<T>> for UserDataRef<T> {
|
||||
type Error = Error;
|
||||
|
||||
#[inline]
|
||||
fn try_from(variant: UserDataVariant<T>) -> Result<Self> {
|
||||
let guard = if cfg!(not(feature = "send")) || is_sync::<T>() {
|
||||
variant.raw_lock().try_lock_shared_guarded()
|
||||
} else {
|
||||
variant.raw_lock().try_lock_exclusive_guarded()
|
||||
};
|
||||
let guard = guard.map_err(|_| Error::UserDataBorrowError)?;
|
||||
let guard = unsafe { mem::transmute::<LockGuard<_>, LockGuard<'static, _>>(guard) };
|
||||
Ok(UserDataRef::from_parts(UserDataRefInner::Default(variant), guard))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: 'static> FromLua for UserDataRef<T> {
|
||||
fn from_lua(value: Value, _: &Lua) -> Result<Self> {
|
||||
try_value_to_userdata::<T>(value)?.borrow()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn from_stack(idx: c_int, lua: &RawLua) -> Result<Self> {
|
||||
Self::borrow_from_stack(lua, lua.state(), idx)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: 'static> UserDataRef<T> {
|
||||
#[inline(always)]
|
||||
fn from_parts(inner: UserDataRefInner<T>, guard: LockGuard<'static, RawLock>) -> Self {
|
||||
Self { _guard: guard, inner }
|
||||
}
|
||||
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
fn remap<U>(
|
||||
self,
|
||||
f: impl FnOnce(UserDataVariant<T>) -> Result<UserDataRefInner<U>>,
|
||||
) -> Result<UserDataRef<U>> {
|
||||
match &self.inner {
|
||||
UserDataRefInner::Default(variant) => {
|
||||
let inner = f(variant.clone())?;
|
||||
Ok(UserDataRef::from_parts(inner, self._guard))
|
||||
}
|
||||
_ => Err(Error::UserDataTypeMismatch),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn borrow_from_stack(
|
||||
lua: &RawLua,
|
||||
state: *mut ffi::lua_State,
|
||||
idx: c_int,
|
||||
) -> Result<Self> {
|
||||
let type_id = lua.get_userdata_type_id::<T>(state, idx)?;
|
||||
match type_id {
|
||||
Some(type_id) if type_id == TypeId::of::<T>() => {
|
||||
let ud = get_userdata::<UserDataStorage<T>>(state, idx);
|
||||
(*ud).try_borrow_owned()
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "userdata-wrappers", not(feature = "send")))]
|
||||
Some(type_id) if type_id == TypeId::of::<Rc<T>>() => {
|
||||
let ud = get_userdata::<UserDataStorage<Rc<T>>>(state, idx);
|
||||
((*ud).try_borrow_owned()).and_then(|ud| ud.transform_rc())
|
||||
}
|
||||
#[cfg(all(feature = "userdata-wrappers", not(feature = "send")))]
|
||||
Some(type_id) if type_id == TypeId::of::<Rc<RefCell<T>>>() => {
|
||||
let ud = get_userdata::<UserDataStorage<Rc<RefCell<T>>>>(state, idx);
|
||||
((*ud).try_borrow_owned()).and_then(|ud| ud.transform_rc_refcell())
|
||||
}
|
||||
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
Some(type_id) if type_id == TypeId::of::<Arc<T>>() => {
|
||||
let ud = get_userdata::<UserDataStorage<Arc<T>>>(state, idx);
|
||||
((*ud).try_borrow_owned()).and_then(|ud| ud.transform_arc())
|
||||
}
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
Some(type_id) if type_id == TypeId::of::<Arc<MutexPL<T>>>() => {
|
||||
let ud = get_userdata::<UserDataStorage<Arc<MutexPL<T>>>>(state, idx);
|
||||
((*ud).try_borrow_owned()).and_then(|ud| ud.transform_arc_mutex_pl())
|
||||
}
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
Some(type_id) if type_id == TypeId::of::<Arc<RwLockPL<T>>>() => {
|
||||
let ud = get_userdata::<UserDataStorage<Arc<RwLockPL<T>>>>(state, idx);
|
||||
((*ud).try_borrow_owned()).and_then(|ud| ud.transform_arc_rwlock_pl())
|
||||
}
|
||||
_ => Err(Error::UserDataTypeMismatch),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "userdata-wrappers", not(feature = "send")))]
|
||||
impl<T> UserDataRef<Rc<T>> {
|
||||
fn transform_rc(self) -> Result<UserDataRef<T>> {
|
||||
self.remap(|variant| Ok(UserDataRefInner::Rc(variant)))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "userdata-wrappers", not(feature = "send")))]
|
||||
impl<T> UserDataRef<Rc<RefCell<T>>> {
|
||||
fn transform_rc_refcell(self) -> Result<UserDataRef<T>> {
|
||||
self.remap(|variant| unsafe {
|
||||
let obj = &*variant.as_ptr();
|
||||
let r#ref = obj.try_borrow().map_err(|_| Error::UserDataBorrowError)?;
|
||||
let borrow = std::mem::transmute::<Ref<T>, Ref<'static, T>>(r#ref);
|
||||
Ok(UserDataRefInner::RcRefCell(borrow, variant))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
impl<T> UserDataRef<Arc<T>> {
|
||||
fn transform_arc(self) -> Result<UserDataRef<T>> {
|
||||
self.remap(|variant| Ok(UserDataRefInner::Arc(variant)))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
impl<T> UserDataRef<Arc<MutexPL<T>>> {
|
||||
fn transform_arc_mutex_pl(self) -> Result<UserDataRef<T>> {
|
||||
self.remap(|variant| unsafe {
|
||||
let obj = &*variant.as_ptr();
|
||||
let guard = obj.try_lock().ok_or(Error::UserDataBorrowError)?;
|
||||
let borrow = std::mem::transmute::<MutexGuardPL<T>, MutexGuardPL<'static, T>>(guard);
|
||||
Ok(UserDataRefInner::ArcMutexPL(borrow, variant))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
impl<T> UserDataRef<Arc<RwLockPL<T>>> {
|
||||
fn transform_arc_rwlock_pl(self) -> Result<UserDataRef<T>> {
|
||||
self.remap(|variant| unsafe {
|
||||
let obj = &*variant.as_ptr();
|
||||
let guard = obj.try_read().ok_or(Error::UserDataBorrowError)?;
|
||||
let borrow = std::mem::transmute::<RwLockReadGuardPL<T>, RwLockReadGuardPL<'static, T>>(guard);
|
||||
Ok(UserDataRefInner::ArcRwLockPL(borrow, variant))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
enum UserDataRefInner<T: 'static> {
|
||||
Default(UserDataVariant<T>),
|
||||
|
||||
#[cfg(all(feature = "userdata-wrappers", not(feature = "send")))]
|
||||
Rc(UserDataVariant<Rc<T>>),
|
||||
#[cfg(all(feature = "userdata-wrappers", not(feature = "send")))]
|
||||
RcRefCell(Ref<'static, T>, UserDataVariant<Rc<RefCell<T>>>),
|
||||
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
Arc(UserDataVariant<Arc<T>>),
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
ArcMutexPL(MutexGuardPL<'static, T>, UserDataVariant<Arc<MutexPL<T>>>),
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
ArcRwLockPL(RwLockReadGuardPL<'static, T>, UserDataVariant<Arc<RwLockPL<T>>>),
|
||||
}
|
||||
|
||||
impl<T> Deref for UserDataRefInner<T> {
|
||||
type Target = T;
|
||||
|
||||
#[inline]
|
||||
fn deref(&self) -> &T {
|
||||
match self {
|
||||
Self::Default(inner) => unsafe { &*inner.as_ptr() },
|
||||
|
||||
#[cfg(all(feature = "userdata-wrappers", not(feature = "send")))]
|
||||
Self::Rc(inner) => unsafe { &*Rc::as_ptr(&*inner.as_ptr()) },
|
||||
#[cfg(all(feature = "userdata-wrappers", not(feature = "send")))]
|
||||
Self::RcRefCell(x, ..) => x,
|
||||
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
Self::Arc(inner) => unsafe { &*Arc::as_ptr(&*inner.as_ptr()) },
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
Self::ArcMutexPL(x, ..) => x,
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
Self::ArcRwLockPL(x, ..) => x,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A wrapper type for a userdata value that provides read and write access.
|
||||
///
|
||||
/// It implements [`FromLua`] and can be used to receive a typed userdata from Lua.
|
||||
pub struct UserDataRefMut<T: 'static> {
|
||||
// It's important to drop the guard first, as it refers to the `inner` data.
|
||||
_guard: LockGuard<'static, RawLock>,
|
||||
inner: UserDataRefMutInner<T>,
|
||||
}
|
||||
|
||||
impl<T> Deref for UserDataRefMut<T> {
|
||||
type Target = T;
|
||||
|
||||
#[inline]
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.inner
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> DerefMut for UserDataRefMut<T> {
|
||||
#[inline]
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.inner
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: fmt::Debug> fmt::Debug for UserDataRefMut<T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
(**self).fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: fmt::Display> fmt::Display for UserDataRefMut<T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
(**self).fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> TryFrom<UserDataVariant<T>> for UserDataRefMut<T> {
|
||||
type Error = Error;
|
||||
|
||||
#[inline]
|
||||
fn try_from(variant: UserDataVariant<T>) -> Result<Self> {
|
||||
let guard = variant.raw_lock().try_lock_exclusive_guarded();
|
||||
let guard = guard.map_err(|_| Error::UserDataBorrowMutError)?;
|
||||
let guard = unsafe { mem::transmute::<LockGuard<_>, LockGuard<'static, _>>(guard) };
|
||||
Ok(UserDataRefMut::from_parts(
|
||||
UserDataRefMutInner::Default(variant),
|
||||
guard,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: 'static> FromLua for UserDataRefMut<T> {
|
||||
fn from_lua(value: Value, _: &Lua) -> Result<Self> {
|
||||
try_value_to_userdata::<T>(value)?.borrow_mut()
|
||||
}
|
||||
|
||||
unsafe fn from_stack(idx: c_int, lua: &RawLua) -> Result<Self> {
|
||||
Self::borrow_from_stack(lua, lua.state(), idx)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: 'static> UserDataRefMut<T> {
|
||||
#[inline(always)]
|
||||
fn from_parts(inner: UserDataRefMutInner<T>, guard: LockGuard<'static, RawLock>) -> Self {
|
||||
Self { _guard: guard, inner }
|
||||
}
|
||||
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
fn remap<U>(
|
||||
self,
|
||||
f: impl FnOnce(UserDataVariant<T>) -> Result<UserDataRefMutInner<U>>,
|
||||
) -> Result<UserDataRefMut<U>> {
|
||||
match &self.inner {
|
||||
UserDataRefMutInner::Default(variant) => {
|
||||
let inner = f(variant.clone())?;
|
||||
Ok(UserDataRefMut::from_parts(inner, self._guard))
|
||||
}
|
||||
_ => Err(Error::UserDataTypeMismatch),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn borrow_from_stack(
|
||||
lua: &RawLua,
|
||||
state: *mut ffi::lua_State,
|
||||
idx: c_int,
|
||||
) -> Result<Self> {
|
||||
let type_id = lua.get_userdata_type_id::<T>(state, idx)?;
|
||||
match type_id {
|
||||
Some(type_id) if type_id == TypeId::of::<T>() => {
|
||||
let ud = get_userdata::<UserDataStorage<T>>(state, idx);
|
||||
(*ud).try_borrow_owned_mut()
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "userdata-wrappers", not(feature = "send")))]
|
||||
Some(type_id) if type_id == TypeId::of::<Rc<T>>() => Err(Error::UserDataBorrowMutError),
|
||||
#[cfg(all(feature = "userdata-wrappers", not(feature = "send")))]
|
||||
Some(type_id) if type_id == TypeId::of::<Rc<RefCell<T>>>() => {
|
||||
let ud = get_userdata::<UserDataStorage<Rc<RefCell<T>>>>(state, idx);
|
||||
((*ud).try_borrow_owned_mut()).and_then(|ud| ud.transform_rc_refcell())
|
||||
}
|
||||
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
Some(type_id) if type_id == TypeId::of::<Arc<T>>() => Err(Error::UserDataBorrowMutError),
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
Some(type_id) if type_id == TypeId::of::<Arc<MutexPL<T>>>() => {
|
||||
let ud = get_userdata::<UserDataStorage<Arc<MutexPL<T>>>>(state, idx);
|
||||
((*ud).try_borrow_owned_mut()).and_then(|ud| ud.transform_arc_mutex_pl())
|
||||
}
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
Some(type_id) if type_id == TypeId::of::<Arc<RwLockPL<T>>>() => {
|
||||
let ud = get_userdata::<UserDataStorage<Arc<RwLockPL<T>>>>(state, idx);
|
||||
((*ud).try_borrow_owned_mut()).and_then(|ud| ud.transform_arc_rwlock_pl())
|
||||
}
|
||||
_ => Err(Error::UserDataTypeMismatch),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "userdata-wrappers", not(feature = "send")))]
|
||||
impl<T> UserDataRefMut<Rc<RefCell<T>>> {
|
||||
fn transform_rc_refcell(self) -> Result<UserDataRefMut<T>> {
|
||||
self.remap(|variant| unsafe {
|
||||
let obj = &*variant.as_ptr();
|
||||
let refmut = obj.try_borrow_mut().map_err(|_| Error::UserDataBorrowMutError)?;
|
||||
let borrow = std::mem::transmute::<RefMut<T>, RefMut<'static, T>>(refmut);
|
||||
Ok(UserDataRefMutInner::RcRefCell(borrow, variant))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
impl<T> UserDataRefMut<Arc<MutexPL<T>>> {
|
||||
fn transform_arc_mutex_pl(self) -> Result<UserDataRefMut<T>> {
|
||||
self.remap(|variant| unsafe {
|
||||
let obj = &*variant.as_ptr();
|
||||
let guard = obj.try_lock().ok_or(Error::UserDataBorrowMutError)?;
|
||||
let borrow = std::mem::transmute::<MutexGuardPL<T>, MutexGuardPL<'static, T>>(guard);
|
||||
Ok(UserDataRefMutInner::ArcMutexPL(borrow, variant))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
impl<T> UserDataRefMut<Arc<RwLockPL<T>>> {
|
||||
fn transform_arc_rwlock_pl(self) -> Result<UserDataRefMut<T>> {
|
||||
self.remap(|variant| unsafe {
|
||||
let obj = &*variant.as_ptr();
|
||||
let guard = obj.try_write().ok_or(Error::UserDataBorrowMutError)?;
|
||||
let borrow = std::mem::transmute::<RwLockWriteGuardPL<T>, RwLockWriteGuardPL<'static, T>>(guard);
|
||||
Ok(UserDataRefMutInner::ArcRwLockPL(borrow, variant))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
enum UserDataRefMutInner<T: 'static> {
|
||||
Default(UserDataVariant<T>),
|
||||
|
||||
#[cfg(all(feature = "userdata-wrappers", not(feature = "send")))]
|
||||
RcRefCell(RefMut<'static, T>, UserDataVariant<Rc<RefCell<T>>>),
|
||||
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
ArcMutexPL(MutexGuardPL<'static, T>, UserDataVariant<Arc<MutexPL<T>>>),
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
ArcRwLockPL(RwLockWriteGuardPL<'static, T>, UserDataVariant<Arc<RwLockPL<T>>>),
|
||||
}
|
||||
|
||||
impl<T> Deref for UserDataRefMutInner<T> {
|
||||
type Target = T;
|
||||
|
||||
#[inline]
|
||||
fn deref(&self) -> &T {
|
||||
match self {
|
||||
Self::Default(inner) => unsafe { &*inner.as_ptr() },
|
||||
|
||||
#[cfg(all(feature = "userdata-wrappers", not(feature = "send")))]
|
||||
Self::RcRefCell(x, ..) => x,
|
||||
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
Self::ArcMutexPL(x, ..) => x,
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
Self::ArcRwLockPL(x, ..) => x,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> DerefMut for UserDataRefMutInner<T> {
|
||||
#[inline]
|
||||
fn deref_mut(&mut self) -> &mut T {
|
||||
match self {
|
||||
Self::Default(inner) => unsafe { &mut *inner.as_ptr() },
|
||||
|
||||
#[cfg(all(feature = "userdata-wrappers", not(feature = "send")))]
|
||||
Self::RcRefCell(x, ..) => x,
|
||||
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
Self::ArcMutexPL(x, ..) => x,
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
Self::ArcRwLockPL(x, ..) => x,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn try_value_to_userdata<T>(value: Value) -> Result<AnyUserData> {
|
||||
match value {
|
||||
Value::UserData(ud) => Ok(ud),
|
||||
_ => Err(Error::FromLuaConversionError {
|
||||
from: value.type_name(),
|
||||
to: "userdata".to_string(),
|
||||
message: Some(format!("expected userdata of type {}", type_name::<T>())),
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod assertions {
|
||||
use super::*;
|
||||
|
||||
#[cfg(feature = "send")]
|
||||
static_assertions::assert_impl_all!(UserDataRef<()>: Send, Sync);
|
||||
#[cfg(feature = "send")]
|
||||
static_assertions::assert_not_impl_all!(UserDataRef<std::rc::Rc<()>>: Send, Sync);
|
||||
#[cfg(feature = "send")]
|
||||
static_assertions::assert_impl_all!(UserDataRefMut<()>: Sync, Send);
|
||||
#[cfg(feature = "send")]
|
||||
static_assertions::assert_not_impl_all!(UserDataRefMut<std::rc::Rc<()>>: Send, Sync);
|
||||
|
||||
#[cfg(not(feature = "send"))]
|
||||
static_assertions::assert_not_impl_all!(UserDataRef<()>: Send, Sync);
|
||||
#[cfg(not(feature = "send"))]
|
||||
static_assertions::assert_not_impl_all!(UserDataRefMut<()>: Send, Sync);
|
||||
}
|
||||
+235
-160
@@ -3,35 +3,47 @@
|
||||
use std::any::TypeId;
|
||||
use std::cell::RefCell;
|
||||
use std::marker::PhantomData;
|
||||
use std::os::raw::c_int;
|
||||
use std::os::raw::c_void;
|
||||
use std::string::String as StdString;
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
use crate::state::{Lua, RawLua};
|
||||
use crate::state::{Lua, LuaGuard};
|
||||
use crate::traits::{FromLua, FromLuaMulti, IntoLua, IntoLuaMulti};
|
||||
use crate::types::{Callback, MaybeSend};
|
||||
use crate::userdata::{
|
||||
AnyUserData, MetaMethod, UserData, UserDataFields, UserDataMethods, UserDataRef, UserDataRefMut,
|
||||
borrow_userdata_scoped, borrow_userdata_scoped_mut, AnyUserData, MetaMethod, TypeIdHints, UserData,
|
||||
UserDataFields, UserDataMethods, UserDataStorage,
|
||||
};
|
||||
use crate::util::{get_userdata, short_type_name};
|
||||
use crate::value::{FromLua, FromLuaMulti, IntoLua, IntoLuaMulti, Value};
|
||||
|
||||
use super::cell::{UserDataBorrowMut, UserDataBorrowRef, UserDataVariant};
|
||||
use crate::util::short_type_name;
|
||||
use crate::value::Value;
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
use {
|
||||
crate::types::AsyncCallback,
|
||||
crate::userdata::{UserDataRef, UserDataRefMut},
|
||||
std::future::{self, Future},
|
||||
};
|
||||
|
||||
type StaticFieldCallback = Box<dyn FnOnce(&RawLua) -> Result<()> + 'static>;
|
||||
#[derive(Clone, Copy)]
|
||||
enum UserDataType {
|
||||
Shared(TypeIdHints),
|
||||
Unique(*mut c_void),
|
||||
}
|
||||
|
||||
/// Handle to registry for userdata methods and metamethods.
|
||||
pub struct UserDataRegistry<T: 'static> {
|
||||
pub struct UserDataRegistry<T> {
|
||||
lua: LuaGuard,
|
||||
raw: RawUserDataRegistry,
|
||||
r#type: UserDataType,
|
||||
_phantom: PhantomData<T>,
|
||||
}
|
||||
|
||||
pub(crate) struct RawUserDataRegistry {
|
||||
// Fields
|
||||
pub(crate) fields: Vec<(String, StaticFieldCallback)>,
|
||||
pub(crate) fields: Vec<(String, Result<Value>)>,
|
||||
pub(crate) field_getters: Vec<(String, Callback)>,
|
||||
pub(crate) field_setters: Vec<(String, Callback)>,
|
||||
pub(crate) meta_fields: Vec<(String, StaticFieldCallback)>,
|
||||
pub(crate) meta_fields: Vec<(String, Result<Value>)>,
|
||||
|
||||
// Methods
|
||||
pub(crate) methods: Vec<(String, Callback)>,
|
||||
@@ -41,12 +53,40 @@ pub struct UserDataRegistry<T: 'static> {
|
||||
#[cfg(feature = "async")]
|
||||
pub(crate) async_meta_methods: Vec<(String, AsyncCallback)>,
|
||||
|
||||
_type: PhantomData<T>,
|
||||
pub(crate) destructor: ffi::lua_CFunction,
|
||||
pub(crate) type_id: Option<TypeId>,
|
||||
pub(crate) type_name: StdString,
|
||||
}
|
||||
|
||||
impl UserDataType {
|
||||
#[inline]
|
||||
pub(crate) fn type_id(&self) -> Option<TypeId> {
|
||||
match self {
|
||||
UserDataType::Shared(hints) => Some(hints.type_id()),
|
||||
UserDataType::Unique(_) => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "send")]
|
||||
unsafe impl Send for UserDataType {}
|
||||
|
||||
impl<T: 'static> UserDataRegistry<T> {
|
||||
pub(crate) const fn new() -> Self {
|
||||
UserDataRegistry {
|
||||
#[inline(always)]
|
||||
pub(crate) fn new(lua: &Lua) -> Self {
|
||||
Self::with_type(lua, UserDataType::Shared(TypeIdHints::new::<T>()))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> UserDataRegistry<T> {
|
||||
#[inline(always)]
|
||||
pub(crate) fn new_unique(lua: &Lua, ud_ptr: *mut c_void) -> Self {
|
||||
Self::with_type(lua, UserDataType::Unique(ud_ptr))
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn with_type(lua: &Lua, r#type: UserDataType) -> Self {
|
||||
let raw = RawUserDataRegistry {
|
||||
fields: Vec::new(),
|
||||
field_getters: Vec::new(),
|
||||
field_setters: Vec::new(),
|
||||
@@ -57,11 +97,20 @@ impl<T: 'static> UserDataRegistry<T> {
|
||||
meta_methods: Vec::new(),
|
||||
#[cfg(feature = "async")]
|
||||
async_meta_methods: Vec::new(),
|
||||
_type: PhantomData,
|
||||
destructor: super::util::destroy_userdata_storage::<T>,
|
||||
type_id: r#type.type_id(),
|
||||
type_name: short_type_name::<T>(),
|
||||
};
|
||||
|
||||
UserDataRegistry {
|
||||
lua: lua.lock_arc(),
|
||||
raw,
|
||||
r#type,
|
||||
_phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
fn box_method<M, A, R>(name: &str, method: M) -> Callback
|
||||
fn box_method<M, A, R>(&self, name: &str, method: M) -> Callback
|
||||
where
|
||||
M: Fn(&Lua, &T, A) -> Result<R> + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
@@ -74,6 +123,7 @@ impl<T: 'static> UserDataRegistry<T> {
|
||||
};
|
||||
}
|
||||
|
||||
let target_type = self.r#type;
|
||||
Box::new(move |rawlua, nargs| unsafe {
|
||||
if nargs == 0 {
|
||||
let err = Error::from_lua_conversion("missing argument", "userdata", None);
|
||||
@@ -85,17 +135,29 @@ impl<T: 'static> UserDataRegistry<T> {
|
||||
// Self was at position 1, so we pass 2 here
|
||||
let args = A::from_stack_args(nargs - 1, 2, Some(&name), rawlua);
|
||||
|
||||
match try_self_arg!(rawlua.get_userdata_type_id(self_index)) {
|
||||
Some(id) if id == TypeId::of::<T>() => {
|
||||
let ud = try_self_arg!(borrow_userdata_ref::<T>(state, self_index));
|
||||
method(rawlua.lua(), &ud, args?)?.push_into_stack_multi(rawlua)
|
||||
match target_type {
|
||||
#[rustfmt::skip]
|
||||
UserDataType::Shared(type_hints) => {
|
||||
let type_id = try_self_arg!(rawlua.get_userdata_type_id::<T>(state, self_index));
|
||||
try_self_arg!(borrow_userdata_scoped(state, self_index, type_id, type_hints, |ud| {
|
||||
method(rawlua.lua(), ud, args?)?.push_into_stack_multi(rawlua)
|
||||
}))
|
||||
}
|
||||
UserDataType::Unique(target_ptr) if ffi::lua_touserdata(state, self_index) == target_ptr => {
|
||||
let ud = target_ptr as *mut UserDataStorage<T>;
|
||||
try_self_arg!((*ud).try_borrow_scoped(|ud| {
|
||||
method(rawlua.lua(), ud, args?)?.push_into_stack_multi(rawlua)
|
||||
}))
|
||||
}
|
||||
UserDataType::Unique(_) => {
|
||||
try_self_arg!(rawlua.get_userdata_type_id::<T>(state, self_index));
|
||||
Err(Error::bad_self_argument(&name, Error::UserDataTypeMismatch))
|
||||
}
|
||||
_ => Err(Error::bad_self_argument(&name, Error::UserDataTypeMismatch)),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn box_method_mut<M, A, R>(name: &str, method: M) -> Callback
|
||||
fn box_method_mut<M, A, R>(&self, name: &str, method: M) -> Callback
|
||||
where
|
||||
M: FnMut(&Lua, &mut T, A) -> Result<R> + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
@@ -109,6 +171,7 @@ impl<T: 'static> UserDataRegistry<T> {
|
||||
}
|
||||
|
||||
let method = RefCell::new(method);
|
||||
let target_type = self.r#type;
|
||||
Box::new(move |rawlua, nargs| unsafe {
|
||||
let mut method = method.try_borrow_mut().map_err(|_| Error::RecursiveMutCallback)?;
|
||||
if nargs == 0 {
|
||||
@@ -121,19 +184,32 @@ impl<T: 'static> UserDataRegistry<T> {
|
||||
// Self was at position 1, so we pass 2 here
|
||||
let args = A::from_stack_args(nargs - 1, 2, Some(&name), rawlua);
|
||||
|
||||
match try_self_arg!(rawlua.get_userdata_type_id(self_index)) {
|
||||
Some(id) if id == TypeId::of::<T>() => {
|
||||
let mut ud = try_self_arg!(borrow_userdata_mut::<T>(state, self_index));
|
||||
method(rawlua.lua(), &mut ud, args?)?.push_into_stack_multi(rawlua)
|
||||
match target_type {
|
||||
#[rustfmt::skip]
|
||||
UserDataType::Shared(type_hints) => {
|
||||
let type_id = try_self_arg!(rawlua.get_userdata_type_id::<T>(state, self_index));
|
||||
try_self_arg!(borrow_userdata_scoped_mut(state, self_index, type_id, type_hints, |ud| {
|
||||
method(rawlua.lua(), ud, args?)?.push_into_stack_multi(rawlua)
|
||||
}))
|
||||
}
|
||||
UserDataType::Unique(target_ptr) if ffi::lua_touserdata(state, self_index) == target_ptr => {
|
||||
let ud = target_ptr as *mut UserDataStorage<T>;
|
||||
try_self_arg!((*ud).try_borrow_scoped_mut(|ud| {
|
||||
method(rawlua.lua(), ud, args?)?.push_into_stack_multi(rawlua)
|
||||
}))
|
||||
}
|
||||
UserDataType::Unique(_) => {
|
||||
try_self_arg!(rawlua.get_userdata_type_id::<T>(state, self_index));
|
||||
Err(Error::bad_self_argument(&name, Error::UserDataTypeMismatch))
|
||||
}
|
||||
_ => Err(Error::bad_self_argument(&name, Error::UserDataTypeMismatch)),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
fn box_async_method<M, A, MR, R>(name: &str, method: M) -> AsyncCallback
|
||||
fn box_async_method<M, A, MR, R>(&self, name: &str, method: M) -> AsyncCallback
|
||||
where
|
||||
T: 'static,
|
||||
M: Fn(Lua, UserDataRef<T>, A) -> MR + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
MR: Future<Output = Result<R>> + MaybeSend + 'static,
|
||||
@@ -171,8 +247,9 @@ impl<T: 'static> UserDataRegistry<T> {
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
fn box_async_method_mut<M, A, MR, R>(name: &str, method: M) -> AsyncCallback
|
||||
fn box_async_method_mut<M, A, MR, R>(&self, name: &str, method: M) -> AsyncCallback
|
||||
where
|
||||
T: 'static,
|
||||
M: Fn(Lua, UserDataRefMut<T>, A) -> MR + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
MR: Future<Output = Result<R>> + MaybeSend + 'static,
|
||||
@@ -209,7 +286,7 @@ impl<T: 'static> UserDataRegistry<T> {
|
||||
})
|
||||
}
|
||||
|
||||
fn box_function<F, A, R>(name: &str, function: F) -> Callback
|
||||
fn box_function<F, A, R>(&self, name: &str, function: F) -> Callback
|
||||
where
|
||||
F: Fn(&Lua, A) -> Result<R> + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
@@ -222,7 +299,7 @@ impl<T: 'static> UserDataRegistry<T> {
|
||||
})
|
||||
}
|
||||
|
||||
fn box_function_mut<F, A, R>(name: &str, function: F) -> Callback
|
||||
fn box_function_mut<F, A, R>(&self, name: &str, function: F) -> Callback
|
||||
where
|
||||
F: FnMut(&Lua, A) -> Result<R> + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
@@ -240,7 +317,7 @@ impl<T: 'static> UserDataRegistry<T> {
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
fn box_async_function<F, A, FR, R>(name: &str, function: F) -> AsyncCallback
|
||||
fn box_async_function<F, A, FR, R>(&self, name: &str, function: F) -> AsyncCallback
|
||||
where
|
||||
F: Fn(Lua, A) -> FR + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
@@ -275,6 +352,11 @@ impl<T: 'static> UserDataRegistry<T> {
|
||||
}
|
||||
value.into_lua(lua)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub(crate) fn into_raw(self) -> RawUserDataRegistry {
|
||||
self.raw
|
||||
}
|
||||
}
|
||||
|
||||
// Returns function name for the type `T`, without the module path
|
||||
@@ -282,295 +364,267 @@ fn get_function_name<T>(name: &str) -> StdString {
|
||||
format!("{}.{name}", short_type_name::<T>())
|
||||
}
|
||||
|
||||
impl<T: 'static> UserDataFields<T> for UserDataRegistry<T> {
|
||||
fn add_field<V>(&mut self, name: impl ToString, value: V)
|
||||
impl<T> UserDataFields<T> for UserDataRegistry<T> {
|
||||
fn add_field<V>(&mut self, name: impl Into<StdString>, value: V)
|
||||
where
|
||||
V: IntoLua + 'static,
|
||||
{
|
||||
let name = name.to_string();
|
||||
self.fields.push((
|
||||
name,
|
||||
Box::new(move |rawlua| unsafe { value.push_into_stack(rawlua) }),
|
||||
));
|
||||
let name = name.into();
|
||||
self.raw.fields.push((name, value.into_lua(self.lua.lua())));
|
||||
}
|
||||
|
||||
fn add_field_method_get<M, R>(&mut self, name: impl ToString, method: M)
|
||||
fn add_field_method_get<M, R>(&mut self, name: impl Into<StdString>, method: M)
|
||||
where
|
||||
M: Fn(&Lua, &T) -> Result<R> + MaybeSend + 'static,
|
||||
R: IntoLua,
|
||||
{
|
||||
let name = name.to_string();
|
||||
let callback = Self::box_method(&name, move |lua, data, ()| method(lua, data));
|
||||
self.field_getters.push((name, callback));
|
||||
let name = name.into();
|
||||
let callback = self.box_method(&name, move |lua, data, ()| method(lua, data));
|
||||
self.raw.field_getters.push((name, callback));
|
||||
}
|
||||
|
||||
fn add_field_method_set<M, A>(&mut self, name: impl ToString, method: M)
|
||||
fn add_field_method_set<M, A>(&mut self, name: impl Into<StdString>, method: M)
|
||||
where
|
||||
M: FnMut(&Lua, &mut T, A) -> Result<()> + MaybeSend + 'static,
|
||||
A: FromLua,
|
||||
{
|
||||
let name = name.to_string();
|
||||
let callback = Self::box_method_mut(&name, method);
|
||||
self.field_setters.push((name, callback));
|
||||
let name = name.into();
|
||||
let callback = self.box_method_mut(&name, method);
|
||||
self.raw.field_setters.push((name, callback));
|
||||
}
|
||||
|
||||
fn add_field_function_get<F, R>(&mut self, name: impl ToString, function: F)
|
||||
fn add_field_function_get<F, R>(&mut self, name: impl Into<StdString>, function: F)
|
||||
where
|
||||
F: Fn(&Lua, AnyUserData) -> Result<R> + MaybeSend + 'static,
|
||||
R: IntoLua,
|
||||
{
|
||||
let name = name.to_string();
|
||||
let callback = Self::box_function(&name, function);
|
||||
self.field_getters.push((name, callback));
|
||||
let name = name.into();
|
||||
let callback = self.box_function(&name, function);
|
||||
self.raw.field_getters.push((name, callback));
|
||||
}
|
||||
|
||||
fn add_field_function_set<F, A>(&mut self, name: impl ToString, mut function: F)
|
||||
fn add_field_function_set<F, A>(&mut self, name: impl Into<StdString>, mut function: F)
|
||||
where
|
||||
F: FnMut(&Lua, AnyUserData, A) -> Result<()> + MaybeSend + 'static,
|
||||
A: FromLua,
|
||||
{
|
||||
let name = name.to_string();
|
||||
let callback = Self::box_function_mut(&name, move |lua, (data, val)| function(lua, data, val));
|
||||
self.field_setters.push((name, callback));
|
||||
let name = name.into();
|
||||
let callback = self.box_function_mut(&name, move |lua, (data, val)| function(lua, data, val));
|
||||
self.raw.field_setters.push((name, callback));
|
||||
}
|
||||
|
||||
fn add_meta_field<V>(&mut self, name: impl ToString, value: V)
|
||||
fn add_meta_field<V>(&mut self, name: impl Into<StdString>, value: V)
|
||||
where
|
||||
V: IntoLua + 'static,
|
||||
{
|
||||
let name = name.to_string();
|
||||
self.meta_fields.push((
|
||||
name.clone(),
|
||||
Box::new(move |rawlua| unsafe {
|
||||
Self::check_meta_field(rawlua.lua(), &name, value)?.push_into_stack(rawlua)
|
||||
}),
|
||||
));
|
||||
let lua = self.lua.lua();
|
||||
let name = name.into();
|
||||
let field = Self::check_meta_field(lua, &name, value).and_then(|v| v.into_lua(lua));
|
||||
self.raw.meta_fields.push((name, field));
|
||||
}
|
||||
|
||||
fn add_meta_field_with<F, R>(&mut self, name: impl ToString, f: F)
|
||||
fn add_meta_field_with<F, R>(&mut self, name: impl Into<StdString>, f: F)
|
||||
where
|
||||
F: FnOnce(&Lua) -> Result<R> + 'static,
|
||||
R: IntoLua,
|
||||
{
|
||||
let name = name.to_string();
|
||||
self.meta_fields.push((
|
||||
name.clone(),
|
||||
Box::new(move |rawlua| unsafe {
|
||||
let lua = rawlua.lua();
|
||||
Self::check_meta_field(lua, &name, f(lua)?)?.push_into_stack(rawlua)
|
||||
}),
|
||||
));
|
||||
let lua = self.lua.lua();
|
||||
let name = name.into();
|
||||
let field = f(lua).and_then(|v| Self::check_meta_field(lua, &name, v).and_then(|v| v.into_lua(lua)));
|
||||
self.raw.meta_fields.push((name, field));
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: 'static> UserDataMethods<T> for UserDataRegistry<T> {
|
||||
fn add_method<M, A, R>(&mut self, name: impl ToString, method: M)
|
||||
impl<T> UserDataMethods<T> for UserDataRegistry<T> {
|
||||
fn add_method<M, A, R>(&mut self, name: impl Into<StdString>, method: M)
|
||||
where
|
||||
M: Fn(&Lua, &T, A) -> Result<R> + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
R: IntoLuaMulti,
|
||||
{
|
||||
let name = name.to_string();
|
||||
let callback = Self::box_method(&name, method);
|
||||
self.methods.push((name, callback));
|
||||
let name = name.into();
|
||||
let callback = self.box_method(&name, method);
|
||||
self.raw.methods.push((name, callback));
|
||||
}
|
||||
|
||||
fn add_method_mut<M, A, R>(&mut self, name: impl ToString, method: M)
|
||||
fn add_method_mut<M, A, R>(&mut self, name: impl Into<StdString>, method: M)
|
||||
where
|
||||
M: FnMut(&Lua, &mut T, A) -> Result<R> + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
R: IntoLuaMulti,
|
||||
{
|
||||
let name = name.to_string();
|
||||
let callback = Self::box_method_mut(&name, method);
|
||||
self.methods.push((name, callback));
|
||||
let name = name.into();
|
||||
let callback = self.box_method_mut(&name, method);
|
||||
self.raw.methods.push((name, callback));
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
fn add_async_method<M, A, MR, R>(&mut self, name: impl ToString, method: M)
|
||||
fn add_async_method<M, A, MR, R>(&mut self, name: impl Into<StdString>, method: M)
|
||||
where
|
||||
T: 'static,
|
||||
M: Fn(Lua, UserDataRef<T>, A) -> MR + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
MR: Future<Output = Result<R>> + MaybeSend + 'static,
|
||||
R: IntoLuaMulti,
|
||||
{
|
||||
let name = name.to_string();
|
||||
let callback = Self::box_async_method(&name, method);
|
||||
self.async_methods.push((name, callback));
|
||||
let name = name.into();
|
||||
let callback = self.box_async_method(&name, method);
|
||||
self.raw.async_methods.push((name, callback));
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
fn add_async_method_mut<M, A, MR, R>(&mut self, name: impl ToString, method: M)
|
||||
fn add_async_method_mut<M, A, MR, R>(&mut self, name: impl Into<StdString>, method: M)
|
||||
where
|
||||
T: 'static,
|
||||
M: Fn(Lua, UserDataRefMut<T>, A) -> MR + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
MR: Future<Output = Result<R>> + MaybeSend + 'static,
|
||||
R: IntoLuaMulti,
|
||||
{
|
||||
let name = name.to_string();
|
||||
let callback = Self::box_async_method_mut(&name, method);
|
||||
self.async_methods.push((name, callback));
|
||||
let name = name.into();
|
||||
let callback = self.box_async_method_mut(&name, method);
|
||||
self.raw.async_methods.push((name, callback));
|
||||
}
|
||||
|
||||
fn add_function<F, A, R>(&mut self, name: impl ToString, function: F)
|
||||
fn add_function<F, A, R>(&mut self, name: impl Into<StdString>, function: F)
|
||||
where
|
||||
F: Fn(&Lua, A) -> Result<R> + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
R: IntoLuaMulti,
|
||||
{
|
||||
let name = name.to_string();
|
||||
let callback = Self::box_function(&name, function);
|
||||
self.methods.push((name, callback));
|
||||
let name = name.into();
|
||||
let callback = self.box_function(&name, function);
|
||||
self.raw.methods.push((name, callback));
|
||||
}
|
||||
|
||||
fn add_function_mut<F, A, R>(&mut self, name: impl ToString, function: F)
|
||||
fn add_function_mut<F, A, R>(&mut self, name: impl Into<StdString>, function: F)
|
||||
where
|
||||
F: FnMut(&Lua, A) -> Result<R> + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
R: IntoLuaMulti,
|
||||
{
|
||||
let name = name.to_string();
|
||||
let callback = Self::box_function_mut(&name, function);
|
||||
self.methods.push((name, callback));
|
||||
let name = name.into();
|
||||
let callback = self.box_function_mut(&name, function);
|
||||
self.raw.methods.push((name, callback));
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
fn add_async_function<F, A, FR, R>(&mut self, name: impl ToString, function: F)
|
||||
fn add_async_function<F, A, FR, R>(&mut self, name: impl Into<StdString>, function: F)
|
||||
where
|
||||
F: Fn(Lua, A) -> FR + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
FR: Future<Output = Result<R>> + MaybeSend + 'static,
|
||||
R: IntoLuaMulti,
|
||||
{
|
||||
let name = name.to_string();
|
||||
let callback = Self::box_async_function(&name, function);
|
||||
self.async_methods.push((name, callback));
|
||||
let name = name.into();
|
||||
let callback = self.box_async_function(&name, function);
|
||||
self.raw.async_methods.push((name, callback));
|
||||
}
|
||||
|
||||
fn add_meta_method<M, A, R>(&mut self, name: impl ToString, method: M)
|
||||
fn add_meta_method<M, A, R>(&mut self, name: impl Into<StdString>, method: M)
|
||||
where
|
||||
M: Fn(&Lua, &T, A) -> Result<R> + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
R: IntoLuaMulti,
|
||||
{
|
||||
let name = name.to_string();
|
||||
let callback = Self::box_method(&name, method);
|
||||
self.meta_methods.push((name, callback));
|
||||
let name = name.into();
|
||||
let callback = self.box_method(&name, method);
|
||||
self.raw.meta_methods.push((name, callback));
|
||||
}
|
||||
|
||||
fn add_meta_method_mut<M, A, R>(&mut self, name: impl ToString, method: M)
|
||||
fn add_meta_method_mut<M, A, R>(&mut self, name: impl Into<StdString>, method: M)
|
||||
where
|
||||
M: FnMut(&Lua, &mut T, A) -> Result<R> + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
R: IntoLuaMulti,
|
||||
{
|
||||
let name = name.to_string();
|
||||
let callback = Self::box_method_mut(&name, method);
|
||||
self.meta_methods.push((name, callback));
|
||||
let name = name.into();
|
||||
let callback = self.box_method_mut(&name, method);
|
||||
self.raw.meta_methods.push((name, callback));
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "async", not(any(feature = "lua51", feature = "luau"))))]
|
||||
fn add_async_meta_method<M, A, MR, R>(&mut self, name: impl ToString, method: M)
|
||||
fn add_async_meta_method<M, A, MR, R>(&mut self, name: impl Into<StdString>, method: M)
|
||||
where
|
||||
T: 'static,
|
||||
M: Fn(Lua, UserDataRef<T>, A) -> MR + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
MR: Future<Output = Result<R>> + MaybeSend + 'static,
|
||||
R: IntoLuaMulti,
|
||||
{
|
||||
let name = name.to_string();
|
||||
let callback = Self::box_async_method(&name, method);
|
||||
self.async_meta_methods.push((name, callback));
|
||||
let name = name.into();
|
||||
let callback = self.box_async_method(&name, method);
|
||||
self.raw.async_meta_methods.push((name, callback));
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "async", not(any(feature = "lua51", feature = "luau"))))]
|
||||
fn add_async_meta_method_mut<M, A, MR, R>(&mut self, name: impl ToString, method: M)
|
||||
fn add_async_meta_method_mut<M, A, MR, R>(&mut self, name: impl Into<StdString>, method: M)
|
||||
where
|
||||
T: 'static,
|
||||
M: Fn(Lua, UserDataRefMut<T>, A) -> MR + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
MR: Future<Output = Result<R>> + MaybeSend + 'static,
|
||||
R: IntoLuaMulti,
|
||||
{
|
||||
let name = name.to_string();
|
||||
let callback = Self::box_async_method_mut(&name, method);
|
||||
self.async_meta_methods.push((name, callback));
|
||||
let name = name.into();
|
||||
let callback = self.box_async_method_mut(&name, method);
|
||||
self.raw.async_meta_methods.push((name, callback));
|
||||
}
|
||||
|
||||
fn add_meta_function<F, A, R>(&mut self, name: impl ToString, function: F)
|
||||
fn add_meta_function<F, A, R>(&mut self, name: impl Into<StdString>, function: F)
|
||||
where
|
||||
F: Fn(&Lua, A) -> Result<R> + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
R: IntoLuaMulti,
|
||||
{
|
||||
let name = name.to_string();
|
||||
let callback = Self::box_function(&name, function);
|
||||
self.meta_methods.push((name, callback));
|
||||
let name = name.into();
|
||||
let callback = self.box_function(&name, function);
|
||||
self.raw.meta_methods.push((name, callback));
|
||||
}
|
||||
|
||||
fn add_meta_function_mut<F, A, R>(&mut self, name: impl ToString, function: F)
|
||||
fn add_meta_function_mut<F, A, R>(&mut self, name: impl Into<StdString>, function: F)
|
||||
where
|
||||
F: FnMut(&Lua, A) -> Result<R> + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
R: IntoLuaMulti,
|
||||
{
|
||||
let name = name.to_string();
|
||||
let callback = Self::box_function_mut(&name, function);
|
||||
self.meta_methods.push((name, callback));
|
||||
let name = name.into();
|
||||
let callback = self.box_function_mut(&name, function);
|
||||
self.raw.meta_methods.push((name, callback));
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "async", not(any(feature = "lua51", feature = "luau"))))]
|
||||
fn add_async_meta_function<F, A, FR, R>(&mut self, name: impl ToString, function: F)
|
||||
fn add_async_meta_function<F, A, FR, R>(&mut self, name: impl Into<StdString>, function: F)
|
||||
where
|
||||
F: Fn(Lua, A) -> FR + MaybeSend + 'static,
|
||||
A: FromLuaMulti,
|
||||
FR: Future<Output = Result<R>> + MaybeSend + 'static,
|
||||
R: IntoLuaMulti,
|
||||
{
|
||||
let name = name.to_string();
|
||||
let callback = Self::box_async_function(&name, function);
|
||||
self.async_meta_methods.push((name, callback));
|
||||
let name = name.into();
|
||||
let callback = self.box_async_function(&name, function);
|
||||
self.raw.async_meta_methods.push((name, callback));
|
||||
}
|
||||
}
|
||||
|
||||
// Borrow the userdata in-place from the Lua stack
|
||||
#[inline(always)]
|
||||
unsafe fn borrow_userdata_ref<'a, T>(
|
||||
state: *mut ffi::lua_State,
|
||||
index: c_int,
|
||||
) -> Result<UserDataBorrowRef<'a, T>> {
|
||||
let ud = get_userdata::<UserDataVariant<T>>(state, index);
|
||||
(*ud).try_borrow()
|
||||
}
|
||||
|
||||
// Borrow the userdata mutably in-place from the Lua stack
|
||||
#[inline(always)]
|
||||
unsafe fn borrow_userdata_mut<'a, T>(
|
||||
state: *mut ffi::lua_State,
|
||||
index: c_int,
|
||||
) -> Result<UserDataBorrowMut<'a, T>> {
|
||||
let ud = get_userdata::<UserDataVariant<T>>(state, index);
|
||||
(*ud).try_borrow_mut()
|
||||
}
|
||||
|
||||
macro_rules! lua_userdata_impl {
|
||||
($type:ty) => {
|
||||
impl<T: UserData + 'static> UserData for $type {
|
||||
fn register(registry: &mut UserDataRegistry<Self>) {
|
||||
let mut orig_registry = UserDataRegistry::new();
|
||||
let mut orig_registry = UserDataRegistry::new(registry.lua.lua());
|
||||
T::register(&mut orig_registry);
|
||||
|
||||
// Copy all fields, methods, etc. from the original registry
|
||||
registry.fields.extend(orig_registry.fields);
|
||||
registry.field_getters.extend(orig_registry.field_getters);
|
||||
registry.field_setters.extend(orig_registry.field_setters);
|
||||
registry.meta_fields.extend(orig_registry.meta_fields);
|
||||
registry.methods.extend(orig_registry.methods);
|
||||
(registry.raw.fields).extend(orig_registry.raw.fields);
|
||||
(registry.raw.field_getters).extend(orig_registry.raw.field_getters);
|
||||
(registry.raw.field_setters).extend(orig_registry.raw.field_setters);
|
||||
(registry.raw.meta_fields).extend(orig_registry.raw.meta_fields);
|
||||
(registry.raw.methods).extend(orig_registry.raw.methods);
|
||||
#[cfg(feature = "async")]
|
||||
registry.async_methods.extend(orig_registry.async_methods);
|
||||
registry.meta_methods.extend(orig_registry.meta_methods);
|
||||
(registry.raw.async_methods).extend(orig_registry.raw.async_methods);
|
||||
(registry.raw.meta_methods).extend(orig_registry.raw.meta_methods);
|
||||
#[cfg(feature = "async")]
|
||||
registry
|
||||
.async_meta_methods
|
||||
.extend(orig_registry.async_meta_methods);
|
||||
(registry.raw.async_meta_methods).extend(orig_registry.raw.async_meta_methods);
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -580,3 +634,24 @@ macro_rules! lua_userdata_impl {
|
||||
pub(crate) struct UserDataProxy<T>(pub(crate) PhantomData<T>);
|
||||
|
||||
lua_userdata_impl!(UserDataProxy<T>);
|
||||
|
||||
#[cfg(all(feature = "userdata-wrappers", not(feature = "send")))]
|
||||
lua_userdata_impl!(std::rc::Rc<T>);
|
||||
#[cfg(all(feature = "userdata-wrappers", not(feature = "send")))]
|
||||
lua_userdata_impl!(std::rc::Rc<std::cell::RefCell<T>>);
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
lua_userdata_impl!(std::sync::Arc<T>);
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
lua_userdata_impl!(std::sync::Arc<std::sync::Mutex<T>>);
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
lua_userdata_impl!(std::sync::Arc<std::sync::RwLock<T>>);
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
lua_userdata_impl!(std::sync::Arc<parking_lot::Mutex<T>>);
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
lua_userdata_impl!(std::sync::Arc<parking_lot::RwLock<T>>);
|
||||
|
||||
#[cfg(test)]
|
||||
mod assertions {
|
||||
#[cfg(feature = "send")]
|
||||
static_assertions::assert_impl_all!(super::RawUserDataRegistry: Send);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,469 @@
|
||||
use std::any::TypeId;
|
||||
use std::cell::Cell;
|
||||
use std::marker::PhantomData;
|
||||
use std::os::raw::c_int;
|
||||
use std::ptr;
|
||||
|
||||
use super::UserDataStorage;
|
||||
use crate::error::{Error, Result};
|
||||
use crate::util::{get_userdata, rawget_field, rawset_field, take_userdata};
|
||||
|
||||
// This is a trick to check if a type is `Sync` or not.
|
||||
// It uses leaked specialization feature from stdlib.
|
||||
struct IsSync<'a, T> {
|
||||
is_sync: &'a Cell<bool>,
|
||||
_marker: PhantomData<T>,
|
||||
}
|
||||
|
||||
impl<T> Clone for IsSync<'_, T> {
|
||||
fn clone(&self) -> Self {
|
||||
self.is_sync.set(false);
|
||||
IsSync {
|
||||
is_sync: self.is_sync,
|
||||
_marker: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Sync> Copy for IsSync<'_, T> {}
|
||||
|
||||
pub(crate) fn is_sync<T>() -> bool {
|
||||
let is_sync = Cell::new(true);
|
||||
let _ = [IsSync::<T> {
|
||||
is_sync: &is_sync,
|
||||
_marker: PhantomData,
|
||||
}]
|
||||
.clone();
|
||||
is_sync.get()
|
||||
}
|
||||
|
||||
// Userdata type hints, used to match types of wrapped userdata
|
||||
#[derive(Clone, Copy)]
|
||||
pub(crate) struct TypeIdHints {
|
||||
t: TypeId,
|
||||
|
||||
#[cfg(all(feature = "userdata-wrappers", not(feature = "send")))]
|
||||
rc: TypeId,
|
||||
#[cfg(all(feature = "userdata-wrappers", not(feature = "send")))]
|
||||
rc_refcell: TypeId,
|
||||
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
arc: TypeId,
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
arc_mutex: TypeId,
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
arc_rwlock: TypeId,
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
arc_pl_mutex: TypeId,
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
arc_pl_rwlock: TypeId,
|
||||
}
|
||||
|
||||
impl TypeIdHints {
|
||||
pub(crate) fn new<T: 'static>() -> Self {
|
||||
Self {
|
||||
t: TypeId::of::<T>(),
|
||||
|
||||
#[cfg(all(feature = "userdata-wrappers", not(feature = "send")))]
|
||||
rc: TypeId::of::<std::rc::Rc<T>>(),
|
||||
#[cfg(all(feature = "userdata-wrappers", not(feature = "send")))]
|
||||
rc_refcell: TypeId::of::<std::rc::Rc<std::cell::RefCell<T>>>(),
|
||||
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
arc: TypeId::of::<std::sync::Arc<T>>(),
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
arc_mutex: TypeId::of::<std::sync::Arc<std::sync::Mutex<T>>>(),
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
arc_rwlock: TypeId::of::<std::sync::Arc<std::sync::RwLock<T>>>(),
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
arc_pl_mutex: TypeId::of::<std::sync::Arc<parking_lot::Mutex<T>>>(),
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
arc_pl_rwlock: TypeId::of::<std::sync::Arc<parking_lot::RwLock<T>>>(),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub(crate) fn type_id(&self) -> TypeId {
|
||||
self.t
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn borrow_userdata_scoped<T, R>(
|
||||
state: *mut ffi::lua_State,
|
||||
idx: c_int,
|
||||
type_id: Option<TypeId>,
|
||||
type_hints: TypeIdHints,
|
||||
f: impl FnOnce(&T) -> R,
|
||||
) -> Result<R> {
|
||||
match type_id {
|
||||
Some(type_id) if type_id == type_hints.t => {
|
||||
let ud = get_userdata::<UserDataStorage<T>>(state, idx);
|
||||
(*ud).try_borrow_scoped(|ud| f(ud))
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "userdata-wrappers", not(feature = "send")))]
|
||||
Some(type_id) if type_id == type_hints.rc => {
|
||||
let ud = get_userdata::<UserDataStorage<std::rc::Rc<T>>>(state, idx);
|
||||
(*ud).try_borrow_scoped(|ud| f(ud))
|
||||
}
|
||||
#[cfg(all(feature = "userdata-wrappers", not(feature = "send")))]
|
||||
Some(type_id) if type_id == type_hints.rc_refcell => {
|
||||
let ud = get_userdata::<UserDataStorage<std::rc::Rc<std::cell::RefCell<T>>>>(state, idx);
|
||||
(*ud).try_borrow_scoped(|ud| {
|
||||
let ud = ud.try_borrow().map_err(|_| Error::UserDataBorrowError)?;
|
||||
Ok(f(&ud))
|
||||
})?
|
||||
}
|
||||
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
Some(type_id) if type_id == type_hints.arc => {
|
||||
let ud = get_userdata::<UserDataStorage<std::sync::Arc<T>>>(state, idx);
|
||||
(*ud).try_borrow_scoped(|ud| f(ud))
|
||||
}
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
Some(type_id) if type_id == type_hints.arc_mutex => {
|
||||
let ud = get_userdata::<UserDataStorage<std::sync::Arc<std::sync::Mutex<T>>>>(state, idx);
|
||||
(*ud).try_borrow_scoped(|ud| {
|
||||
let ud = ud.try_lock().map_err(|_| Error::UserDataBorrowError)?;
|
||||
Ok(f(&ud))
|
||||
})?
|
||||
}
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
Some(type_id) if type_id == type_hints.arc_rwlock => {
|
||||
let ud = get_userdata::<UserDataStorage<std::sync::Arc<std::sync::RwLock<T>>>>(state, idx);
|
||||
(*ud).try_borrow_scoped(|ud| {
|
||||
let ud = ud.try_read().map_err(|_| Error::UserDataBorrowError)?;
|
||||
Ok(f(&ud))
|
||||
})?
|
||||
}
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
Some(type_id) if type_id == type_hints.arc_pl_mutex => {
|
||||
let ud = get_userdata::<UserDataStorage<std::sync::Arc<parking_lot::Mutex<T>>>>(state, idx);
|
||||
(*ud).try_borrow_scoped(|ud| {
|
||||
let ud = ud.try_lock().ok_or(Error::UserDataBorrowError)?;
|
||||
Ok(f(&ud))
|
||||
})?
|
||||
}
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
Some(type_id) if type_id == type_hints.arc_pl_rwlock => {
|
||||
let ud = get_userdata::<UserDataStorage<std::sync::Arc<parking_lot::RwLock<T>>>>(state, idx);
|
||||
(*ud).try_borrow_scoped(|ud| {
|
||||
let ud = ud.try_read().ok_or(Error::UserDataBorrowError)?;
|
||||
Ok(f(&ud))
|
||||
})?
|
||||
}
|
||||
_ => Err(Error::UserDataTypeMismatch),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn borrow_userdata_scoped_mut<T, R>(
|
||||
state: *mut ffi::lua_State,
|
||||
idx: c_int,
|
||||
type_id: Option<TypeId>,
|
||||
type_hints: TypeIdHints,
|
||||
f: impl FnOnce(&mut T) -> R,
|
||||
) -> Result<R> {
|
||||
match type_id {
|
||||
Some(type_id) if type_id == type_hints.t => {
|
||||
let ud = get_userdata::<UserDataStorage<T>>(state, idx);
|
||||
(*ud).try_borrow_scoped_mut(|ud| f(ud))
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "userdata-wrappers", not(feature = "send")))]
|
||||
Some(type_id) if type_id == type_hints.rc => {
|
||||
let ud = get_userdata::<UserDataStorage<std::rc::Rc<T>>>(state, idx);
|
||||
(*ud).try_borrow_scoped_mut(|ud| match std::rc::Rc::get_mut(ud) {
|
||||
Some(ud) => Ok(f(ud)),
|
||||
None => Err(Error::UserDataBorrowMutError),
|
||||
})?
|
||||
}
|
||||
#[cfg(all(feature = "userdata-wrappers", not(feature = "send")))]
|
||||
Some(type_id) if type_id == type_hints.rc_refcell => {
|
||||
let ud = get_userdata::<UserDataStorage<std::rc::Rc<std::cell::RefCell<T>>>>(state, idx);
|
||||
(*ud).try_borrow_scoped(|ud| {
|
||||
let mut ud = ud.try_borrow_mut().map_err(|_| Error::UserDataBorrowMutError)?;
|
||||
Ok(f(&mut ud))
|
||||
})?
|
||||
}
|
||||
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
Some(type_id) if type_id == type_hints.arc => {
|
||||
let ud = get_userdata::<UserDataStorage<std::sync::Arc<T>>>(state, idx);
|
||||
(*ud).try_borrow_scoped_mut(|ud| match std::sync::Arc::get_mut(ud) {
|
||||
Some(ud) => Ok(f(ud)),
|
||||
None => Err(Error::UserDataBorrowMutError),
|
||||
})?
|
||||
}
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
Some(type_id) if type_id == type_hints.arc_mutex => {
|
||||
let ud = get_userdata::<UserDataStorage<std::sync::Arc<std::sync::Mutex<T>>>>(state, idx);
|
||||
(*ud).try_borrow_scoped_mut(|ud| {
|
||||
let mut ud = ud.try_lock().map_err(|_| Error::UserDataBorrowMutError)?;
|
||||
Ok(f(&mut ud))
|
||||
})?
|
||||
}
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
Some(type_id) if type_id == type_hints.arc_rwlock => {
|
||||
let ud = get_userdata::<UserDataStorage<std::sync::Arc<std::sync::RwLock<T>>>>(state, idx);
|
||||
(*ud).try_borrow_scoped_mut(|ud| {
|
||||
let mut ud = ud.try_write().map_err(|_| Error::UserDataBorrowMutError)?;
|
||||
Ok(f(&mut ud))
|
||||
})?
|
||||
}
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
Some(type_id) if type_id == type_hints.arc_pl_mutex => {
|
||||
let ud = get_userdata::<UserDataStorage<std::sync::Arc<parking_lot::Mutex<T>>>>(state, idx);
|
||||
(*ud).try_borrow_scoped_mut(|ud| {
|
||||
let mut ud = ud.try_lock().ok_or(Error::UserDataBorrowMutError)?;
|
||||
Ok(f(&mut ud))
|
||||
})?
|
||||
}
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
Some(type_id) if type_id == type_hints.arc_pl_rwlock => {
|
||||
let ud = get_userdata::<UserDataStorage<std::sync::Arc<parking_lot::RwLock<T>>>>(state, idx);
|
||||
(*ud).try_borrow_scoped_mut(|ud| {
|
||||
let mut ud = ud.try_write().ok_or(Error::UserDataBorrowMutError)?;
|
||||
Ok(f(&mut ud))
|
||||
})?
|
||||
}
|
||||
_ => Err(Error::UserDataTypeMismatch),
|
||||
}
|
||||
}
|
||||
|
||||
// Populates the given table with the appropriate members to be a userdata metatable for the given
|
||||
// type. This function takes the given table at the `metatable` index, and adds an appropriate
|
||||
// `__gc` member to it for the given type and a `__metatable` entry to protect the table from script
|
||||
// access. The function also, if given a `field_getters` or `methods` tables, will create an
|
||||
// `__index` metamethod (capturing previous one) to lookup in `field_getters` first, then `methods`
|
||||
// and falling back to the captured `__index` if no matches found.
|
||||
// The same is also applicable for `__newindex` metamethod and `field_setters` table.
|
||||
// Internally uses 9 stack spaces and does not call checkstack.
|
||||
pub(crate) unsafe fn init_userdata_metatable(
|
||||
state: *mut ffi::lua_State,
|
||||
metatable: c_int,
|
||||
field_getters: Option<c_int>,
|
||||
field_setters: Option<c_int>,
|
||||
methods: Option<c_int>,
|
||||
) -> Result<()> {
|
||||
if field_getters.is_some() || methods.is_some() {
|
||||
// Push `__index` generator function
|
||||
init_userdata_metatable_index(state)?;
|
||||
|
||||
let index_type = rawget_field(state, metatable, "__index")?;
|
||||
match index_type {
|
||||
ffi::LUA_TNIL | ffi::LUA_TTABLE | ffi::LUA_TFUNCTION => {
|
||||
for &idx in &[field_getters, methods] {
|
||||
if let Some(idx) = idx {
|
||||
ffi::lua_pushvalue(state, idx);
|
||||
} else {
|
||||
ffi::lua_pushnil(state);
|
||||
}
|
||||
}
|
||||
|
||||
// Generate `__index`
|
||||
protect_lua!(state, 4, 1, fn(state) ffi::lua_call(state, 3, 1))?;
|
||||
}
|
||||
_ => mlua_panic!("improper `__index` type: {}", index_type),
|
||||
}
|
||||
|
||||
rawset_field(state, metatable, "__index")?;
|
||||
}
|
||||
|
||||
if let Some(field_setters) = field_setters {
|
||||
// Push `__newindex` generator function
|
||||
init_userdata_metatable_newindex(state)?;
|
||||
|
||||
let newindex_type = rawget_field(state, metatable, "__newindex")?;
|
||||
match newindex_type {
|
||||
ffi::LUA_TNIL | ffi::LUA_TTABLE | ffi::LUA_TFUNCTION => {
|
||||
ffi::lua_pushvalue(state, field_setters);
|
||||
// Generate `__newindex`
|
||||
protect_lua!(state, 3, 1, fn(state) ffi::lua_call(state, 2, 1))?;
|
||||
}
|
||||
_ => mlua_panic!("improper `__newindex` type: {}", newindex_type),
|
||||
}
|
||||
|
||||
rawset_field(state, metatable, "__newindex")?;
|
||||
}
|
||||
|
||||
ffi::lua_pushboolean(state, 0);
|
||||
rawset_field(state, metatable, "__metatable")?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
unsafe extern "C-unwind" fn lua_error_impl(state: *mut ffi::lua_State) -> c_int {
|
||||
ffi::lua_error(state);
|
||||
}
|
||||
|
||||
unsafe extern "C-unwind" fn lua_isfunction_impl(state: *mut ffi::lua_State) -> c_int {
|
||||
ffi::lua_pushboolean(state, ffi::lua_isfunction(state, -1));
|
||||
1
|
||||
}
|
||||
|
||||
unsafe extern "C-unwind" fn lua_istable_impl(state: *mut ffi::lua_State) -> c_int {
|
||||
ffi::lua_pushboolean(state, ffi::lua_istable(state, -1));
|
||||
1
|
||||
}
|
||||
|
||||
unsafe fn init_userdata_metatable_index(state: *mut ffi::lua_State) -> Result<()> {
|
||||
let index_key = &USERDATA_METATABLE_INDEX as *const u8 as *const _;
|
||||
if ffi::lua_rawgetp(state, ffi::LUA_REGISTRYINDEX, index_key) == ffi::LUA_TFUNCTION {
|
||||
return Ok(());
|
||||
}
|
||||
ffi::lua_pop(state, 1);
|
||||
|
||||
// Create and cache `__index` generator
|
||||
let code = cr#"
|
||||
local error, isfunction, istable = ...
|
||||
return function (__index, field_getters, methods)
|
||||
-- Common case: has field getters and index is a table
|
||||
if field_getters ~= nil and methods == nil and istable(__index) then
|
||||
return function (self, key)
|
||||
local field_getter = field_getters[key]
|
||||
if field_getter ~= nil then
|
||||
return field_getter(self)
|
||||
end
|
||||
return __index[key]
|
||||
end
|
||||
end
|
||||
|
||||
return function (self, key)
|
||||
if field_getters ~= nil then
|
||||
local field_getter = field_getters[key]
|
||||
if field_getter ~= nil then
|
||||
return field_getter(self)
|
||||
end
|
||||
end
|
||||
|
||||
if methods ~= nil then
|
||||
local method = methods[key]
|
||||
if method ~= nil then
|
||||
return method
|
||||
end
|
||||
end
|
||||
|
||||
if isfunction(__index) then
|
||||
return __index(self, key)
|
||||
elseif __index == nil then
|
||||
error("attempt to get an unknown field '"..key.."'")
|
||||
else
|
||||
return __index[key]
|
||||
end
|
||||
end
|
||||
end
|
||||
"#;
|
||||
protect_lua!(state, 0, 1, |state| {
|
||||
let ret = ffi::luaL_loadbuffer(state, code.as_ptr(), code.count_bytes(), cstr!("=__mlua_index"));
|
||||
if ret != ffi::LUA_OK {
|
||||
ffi::lua_error(state);
|
||||
}
|
||||
ffi::lua_pushcfunction(state, lua_error_impl);
|
||||
ffi::lua_pushcfunction(state, lua_isfunction_impl);
|
||||
ffi::lua_pushcfunction(state, lua_istable_impl);
|
||||
ffi::lua_call(state, 3, 1);
|
||||
|
||||
#[cfg(feature = "luau-jit")]
|
||||
if ffi::luau_codegen_supported() != 0 {
|
||||
ffi::luau_codegen_compile(state, -1);
|
||||
}
|
||||
|
||||
// Store in the registry
|
||||
ffi::lua_pushvalue(state, -1);
|
||||
ffi::lua_rawsetp(state, ffi::LUA_REGISTRYINDEX, index_key);
|
||||
})
|
||||
}
|
||||
|
||||
unsafe fn init_userdata_metatable_newindex(state: *mut ffi::lua_State) -> Result<()> {
|
||||
let newindex_key = &USERDATA_METATABLE_NEWINDEX as *const u8 as *const _;
|
||||
if ffi::lua_rawgetp(state, ffi::LUA_REGISTRYINDEX, newindex_key) == ffi::LUA_TFUNCTION {
|
||||
return Ok(());
|
||||
}
|
||||
ffi::lua_pop(state, 1);
|
||||
|
||||
// Create and cache `__newindex` generator
|
||||
let code = cr#"
|
||||
local error, isfunction = ...
|
||||
return function (__newindex, field_setters)
|
||||
return function (self, key, value)
|
||||
if field_setters ~= nil then
|
||||
local field_setter = field_setters[key]
|
||||
if field_setter ~= nil then
|
||||
field_setter(self, value)
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
if isfunction(__newindex) then
|
||||
__newindex(self, key, value)
|
||||
elseif __newindex == nil then
|
||||
error("attempt to set an unknown field '"..key.."'")
|
||||
else
|
||||
__newindex[key] = value
|
||||
end
|
||||
end
|
||||
end
|
||||
"#;
|
||||
protect_lua!(state, 0, 1, |state| {
|
||||
let code_len = code.count_bytes();
|
||||
let ret = ffi::luaL_loadbuffer(state, code.as_ptr(), code_len, cstr!("=__mlua_newindex"));
|
||||
if ret != ffi::LUA_OK {
|
||||
ffi::lua_error(state);
|
||||
}
|
||||
ffi::lua_pushcfunction(state, lua_error_impl);
|
||||
ffi::lua_pushcfunction(state, lua_isfunction_impl);
|
||||
ffi::lua_call(state, 2, 1);
|
||||
|
||||
#[cfg(feature = "luau-jit")]
|
||||
if ffi::luau_codegen_supported() != 0 {
|
||||
ffi::luau_codegen_compile(state, -1);
|
||||
}
|
||||
|
||||
// Store in the registry
|
||||
ffi::lua_pushvalue(state, -1);
|
||||
ffi::lua_rawsetp(state, ffi::LUA_REGISTRYINDEX, newindex_key);
|
||||
})
|
||||
}
|
||||
|
||||
// This method is called by Lua GC when it's time to collect the userdata.
|
||||
//
|
||||
// This method is usually used to collect internal userdata.
|
||||
#[cfg(not(feature = "luau"))]
|
||||
pub(crate) unsafe extern "C-unwind" fn collect_userdata<T>(state: *mut ffi::lua_State) -> c_int {
|
||||
let ud = get_userdata::<T>(state, -1);
|
||||
ptr::drop_in_place(ud);
|
||||
0
|
||||
}
|
||||
|
||||
// This method is called by Luau GC when it's time to collect the userdata.
|
||||
#[cfg(feature = "luau")]
|
||||
pub(crate) unsafe extern "C" fn collect_userdata<T>(
|
||||
state: *mut ffi::lua_State,
|
||||
ud: *mut std::os::raw::c_void,
|
||||
) {
|
||||
// Almost none Lua operations are allowed when destructor is running,
|
||||
// so we need to set a flag to prevent calling any Lua functions
|
||||
let extra = (*ffi::lua_callbacks(state)).userdata as *mut crate::state::ExtraData;
|
||||
(*extra).running_gc = true;
|
||||
// Luau does not support _any_ panics in destructors (they are declared as "C", NOT as "C-unwind"),
|
||||
// so any panics will trigger `abort()`.
|
||||
ptr::drop_in_place(ud as *mut T);
|
||||
(*extra).running_gc = false;
|
||||
}
|
||||
|
||||
// This method can be called by user or Lua GC to destroy the userdata.
|
||||
// It checks if the userdata is safe to destroy and sets the "destroyed" metatable
|
||||
// to prevent further GC collection.
|
||||
pub(super) unsafe extern "C-unwind" fn destroy_userdata_storage<T>(state: *mut ffi::lua_State) -> c_int {
|
||||
let ud = get_userdata::<UserDataStorage<T>>(state, 1);
|
||||
if (*ud).is_safe_to_destroy() {
|
||||
take_userdata::<UserDataStorage<T>>(state, 1);
|
||||
ffi::lua_pushboolean(state, 1);
|
||||
} else {
|
||||
ffi::lua_pushboolean(state, 0);
|
||||
}
|
||||
1
|
||||
}
|
||||
|
||||
static USERDATA_METATABLE_INDEX: u8 = 0;
|
||||
static USERDATA_METATABLE_NEWINDEX: u8 = 0;
|
||||
+18
-23
@@ -9,9 +9,8 @@ use std::sync::Arc;
|
||||
use crate::error::{Error, Result};
|
||||
use crate::memory::MemoryState;
|
||||
use crate::util::{
|
||||
check_stack, get_internal_metatable, get_internal_userdata, init_internal_metatable,
|
||||
push_internal_userdata, push_string, push_table, rawset_field, to_string, TypeKey,
|
||||
DESTRUCTED_USERDATA_METATABLE,
|
||||
check_stack, get_internal_userdata, init_internal_metatable, push_internal_userdata, push_string,
|
||||
push_table, rawset_field, to_string, TypeKey, DESTRUCTED_USERDATA_METATABLE,
|
||||
};
|
||||
|
||||
static WRAPPED_FAILURE_TYPE_KEY: u8 = 0;
|
||||
@@ -31,12 +30,8 @@ impl TypeKey for WrappedFailure {
|
||||
|
||||
impl WrappedFailure {
|
||||
pub(crate) unsafe fn new_userdata(state: *mut ffi::lua_State) -> *mut Self {
|
||||
#[cfg(feature = "luau")]
|
||||
let ud = ffi::lua_newuserdata_t::<Self>(state);
|
||||
#[cfg(not(feature = "luau"))]
|
||||
let ud = ffi::lua_newuserdata(state, std::mem::size_of::<Self>()) as *mut Self;
|
||||
ptr::write(ud, WrappedFailure::None);
|
||||
ud
|
||||
// Unprotected calls always return `Ok`
|
||||
push_internal_userdata(state, WrappedFailure::None, false).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -90,16 +85,11 @@ where
|
||||
let cause = Arc::new(err);
|
||||
let wrapped_error = WrappedFailure::Error(Error::CallbackError { traceback, cause });
|
||||
ptr::write(ud, wrapped_error);
|
||||
get_internal_metatable::<WrappedFailure>(state);
|
||||
ffi::lua_setmetatable(state, -2);
|
||||
|
||||
ffi::lua_error(state)
|
||||
}
|
||||
Err(p) => {
|
||||
ffi::lua_settop(state, 1);
|
||||
ptr::write(ud, WrappedFailure::Panic(Some(p)));
|
||||
get_internal_metatable::<WrappedFailure>(state);
|
||||
ffi::lua_setmetatable(state, -2);
|
||||
ffi::lua_error(state)
|
||||
}
|
||||
}
|
||||
@@ -204,24 +194,25 @@ pub(crate) unsafe fn protect_lua_closure<F, R>(
|
||||
f: F,
|
||||
) -> Result<R>
|
||||
where
|
||||
F: Fn(*mut ffi::lua_State) -> R,
|
||||
F: FnOnce(*mut ffi::lua_State) -> R,
|
||||
R: Copy,
|
||||
{
|
||||
struct Params<F, R: Copy> {
|
||||
function: F,
|
||||
function: Option<F>,
|
||||
result: MaybeUninit<R>,
|
||||
nresults: c_int,
|
||||
}
|
||||
|
||||
unsafe extern "C-unwind" fn do_call<F, R>(state: *mut ffi::lua_State) -> c_int
|
||||
where
|
||||
F: Fn(*mut ffi::lua_State) -> R,
|
||||
F: FnOnce(*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.write(((*params).function)(state));
|
||||
let f = (*params).function.take().unwrap();
|
||||
(*params).result.write(f(state));
|
||||
|
||||
if (*params).nresults == ffi::LUA_MULTRET {
|
||||
ffi::lua_gettop(state)
|
||||
@@ -241,7 +232,7 @@ where
|
||||
}
|
||||
|
||||
let mut params = Params {
|
||||
function: f,
|
||||
function: Some(f),
|
||||
result: MaybeUninit::uninit(),
|
||||
nresults,
|
||||
};
|
||||
@@ -261,7 +252,7 @@ where
|
||||
|
||||
pub(crate) unsafe extern "C-unwind" fn error_traceback(state: *mut ffi::lua_State) -> c_int {
|
||||
// Luau calls error handler for memory allocation errors, skip it
|
||||
// See https://github.com/Roblox/luau/issues/880
|
||||
// See https://github.com/luau-lang/luau/issues/880
|
||||
#[cfg(feature = "luau")]
|
||||
if MemoryState::limit_reached(state) {
|
||||
return 0;
|
||||
@@ -324,7 +315,7 @@ pub(crate) unsafe fn init_error_registry(state: *mut ffi::lua_State) -> Result<(
|
||||
let _ = write!(&mut (*err_buf), "{error}");
|
||||
Ok(err_buf)
|
||||
}
|
||||
Some(WrappedFailure::Panic(Some(ref panic))) => {
|
||||
Some(WrappedFailure::Panic(Some(panic))) => {
|
||||
let err_buf_key = &ERROR_PRINT_BUFFER_KEY as *const u8 as *const c_void;
|
||||
ffi::lua_rawgetp(state, ffi::LUA_REGISTRYINDEX, err_buf_key);
|
||||
let err_buf = ffi::lua_touserdata(state, -1) as *mut String;
|
||||
@@ -358,14 +349,18 @@ pub(crate) unsafe fn init_error_registry(state: *mut ffi::lua_State) -> Result<(
|
||||
state,
|
||||
Some(|state| {
|
||||
ffi::lua_pushcfunction(state, error_tostring);
|
||||
rawset_field(state, -2, "__tostring")
|
||||
ffi::lua_setfield(state, -2, cstr!("__tostring"));
|
||||
|
||||
// This is mostly for Luau typeof() function
|
||||
ffi::lua_pushstring(state, cstr!("error"));
|
||||
ffi::lua_setfield(state, -2, cstr!("__type"));
|
||||
}),
|
||||
)?;
|
||||
|
||||
// Create destructed userdata metatable
|
||||
|
||||
unsafe extern "C-unwind" fn destructed_error(state: *mut ffi::lua_State) -> c_int {
|
||||
callback_error(state, |_| Err(Error::CallbackDestructed))
|
||||
callback_error(state, |_| Err(Error::UserDataDestructed))
|
||||
}
|
||||
|
||||
push_table(state, 0, 26, true)?;
|
||||
|
||||
+39
-13
@@ -1,6 +1,6 @@
|
||||
use std::borrow::Cow;
|
||||
use std::ffi::CStr;
|
||||
use std::os::raw::{c_char, c_int};
|
||||
use std::os::raw::{c_char, c_int, c_void};
|
||||
use std::{ptr, slice, str};
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
@@ -13,17 +13,12 @@ pub(crate) use short_names::short_type_name;
|
||||
pub(crate) use types::TypeKey;
|
||||
pub(crate) use userdata::{
|
||||
get_destructed_userdata_metatable, get_internal_metatable, get_internal_userdata, get_userdata,
|
||||
init_internal_metatable, init_userdata_metatable, push_internal_userdata, take_userdata,
|
||||
init_internal_metatable, push_internal_userdata, push_userdata, take_userdata,
|
||||
DESTRUCTED_USERDATA_METATABLE,
|
||||
};
|
||||
|
||||
#[cfg(not(feature = "lua54"))]
|
||||
pub(crate) use userdata::push_userdata;
|
||||
#[cfg(feature = "lua54")]
|
||||
pub(crate) use userdata::push_userdata_uv;
|
||||
|
||||
#[cfg(not(feature = "luau"))]
|
||||
pub(crate) use userdata::userdata_destructor;
|
||||
pub(crate) use userdata::push_uninit_userdata;
|
||||
|
||||
// Checks that Lua has enough free stack space for future stack operations. On failure, this will
|
||||
// panic with an internal error message.
|
||||
@@ -67,9 +62,15 @@ impl StackGuard {
|
||||
pub(crate) fn with_top(state: *mut ffi::lua_State, top: c_int) -> StackGuard {
|
||||
StackGuard { state, top }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn keep(&mut self, n: c_int) {
|
||||
self.top += n;
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for StackGuard {
|
||||
#[track_caller]
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
let top = ffi::lua_gettop(self.state);
|
||||
@@ -87,7 +88,7 @@ impl Drop for StackGuard {
|
||||
#[inline(always)]
|
||||
pub(crate) 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) {
|
||||
if protect || s.len() >= const { 1 << 30 } {
|
||||
protect_lua!(state, 0, 1, |state| {
|
||||
ffi::lua_pushlstring(state, s.as_ptr() as *const c_char, s.len());
|
||||
})
|
||||
@@ -121,7 +122,7 @@ pub(crate) unsafe fn push_table(
|
||||
) -> Result<()> {
|
||||
let narr: c_int = narr.try_into().unwrap_or(c_int::MAX);
|
||||
let nrec: c_int = nrec.try_into().unwrap_or(c_int::MAX);
|
||||
if protect {
|
||||
if protect || narr >= const { 1 << 30 } || nrec >= const { 1 << 27 } {
|
||||
protect_lua!(state, 0, 1, |state| ffi::lua_createtable(state, narr, nrec))
|
||||
} else {
|
||||
ffi::lua_createtable(state, narr, nrec);
|
||||
@@ -129,6 +130,15 @@ pub(crate) unsafe fn push_table(
|
||||
}
|
||||
}
|
||||
|
||||
// Uses 4 stack spaces, does not call checkstack.
|
||||
pub(crate) unsafe fn rawget_field(state: *mut ffi::lua_State, table: c_int, field: &str) -> Result<c_int> {
|
||||
ffi::lua_pushvalue(state, table);
|
||||
protect_lua!(state, 1, 1, |state| {
|
||||
ffi::lua_pushlstring(state, field.as_ptr() as *const c_char, field.len());
|
||||
ffi::lua_rawget(state, -2)
|
||||
})
|
||||
}
|
||||
|
||||
// Uses 4 stack spaces, does not call checkstack.
|
||||
pub(crate) unsafe fn rawset_field(state: *mut ffi::lua_State, table: c_int, field: &str) -> Result<()> {
|
||||
ffi::lua_pushvalue(state, table);
|
||||
@@ -275,9 +285,25 @@ pub(crate) unsafe fn to_string(state: *mut ffi::lua_State, index: c_int) -> Stri
|
||||
ffi::LUA_TTHREAD => format!("<thread {:?}>", ffi::lua_topointer(state, index)),
|
||||
#[cfg(feature = "luau")]
|
||||
ffi::LUA_TBUFFER => format!("<buffer {:?}>", ffi::lua_topointer(state, index)),
|
||||
#[cfg(feature = "luajit")]
|
||||
ffi::LUA_TCDATA => format!("<cdata {:?}>", ffi::lua_topointer(state, index)),
|
||||
_ => "<unknown>".to_string(),
|
||||
type_id => {
|
||||
let type_name = CStr::from_ptr(ffi::lua_typename(state, type_id)).to_string_lossy();
|
||||
format!("<{type_name} {:?}>", ffi::lua_topointer(state, index))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub(crate) unsafe fn get_metatable_ptr(state: *mut ffi::lua_State, index: c_int) -> *const c_void {
|
||||
#[cfg(feature = "luau")]
|
||||
return ffi::lua_getmetatablepointer(state, index);
|
||||
|
||||
#[cfg(not(feature = "luau"))]
|
||||
if ffi::lua_getmetatable(state, index) == 0 {
|
||||
ptr::null()
|
||||
} else {
|
||||
let p = ffi::lua_topointer(state, -1);
|
||||
ffi::lua_pop(state, 1);
|
||||
p
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+15
-17
@@ -10,73 +10,71 @@ pub(crate) trait TypeKey: Any {
|
||||
fn type_key() -> *const c_void;
|
||||
}
|
||||
|
||||
static STRING_TYPE_KEY: u8 = 0;
|
||||
|
||||
impl TypeKey for String {
|
||||
#[inline(always)]
|
||||
fn type_key() -> *const c_void {
|
||||
static STRING_TYPE_KEY: u8 = 0;
|
||||
&STRING_TYPE_KEY as *const u8 as *const c_void
|
||||
}
|
||||
}
|
||||
|
||||
static CALLBACK_TYPE_KEY: u8 = 0;
|
||||
|
||||
impl TypeKey for Callback {
|
||||
#[inline(always)]
|
||||
fn type_key() -> *const c_void {
|
||||
static CALLBACK_TYPE_KEY: u8 = 0;
|
||||
&CALLBACK_TYPE_KEY as *const u8 as *const c_void
|
||||
}
|
||||
}
|
||||
|
||||
static CALLBACK_UPVALUE_TYPE_KEY: u8 = 0;
|
||||
|
||||
impl TypeKey for CallbackUpvalue {
|
||||
#[inline(always)]
|
||||
fn type_key() -> *const c_void {
|
||||
static CALLBACK_UPVALUE_TYPE_KEY: u8 = 0;
|
||||
&CALLBACK_UPVALUE_TYPE_KEY as *const u8 as *const c_void
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
static ASYNC_CALLBACK_TYPE_KEY: u8 = 0;
|
||||
#[cfg(not(feature = "luau"))]
|
||||
impl TypeKey for crate::types::HookCallback {
|
||||
#[inline(always)]
|
||||
fn type_key() -> *const c_void {
|
||||
static HOOK_CALLBACK_TYPE_KEY: u8 = 0;
|
||||
&HOOK_CALLBACK_TYPE_KEY as *const u8 as *const c_void
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
impl TypeKey for AsyncCallback {
|
||||
#[inline(always)]
|
||||
fn type_key() -> *const c_void {
|
||||
static ASYNC_CALLBACK_TYPE_KEY: u8 = 0;
|
||||
&ASYNC_CALLBACK_TYPE_KEY as *const u8 as *const c_void
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
static ASYNC_CALLBACK_UPVALUE_TYPE_KEY: u8 = 0;
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
impl TypeKey for AsyncCallbackUpvalue {
|
||||
#[inline(always)]
|
||||
fn type_key() -> *const c_void {
|
||||
static ASYNC_CALLBACK_UPVALUE_TYPE_KEY: u8 = 0;
|
||||
&ASYNC_CALLBACK_UPVALUE_TYPE_KEY as *const u8 as *const c_void
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
static ASYNC_POLL_UPVALUE_TYPE_KEY: u8 = 0;
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
impl TypeKey for AsyncPollUpvalue {
|
||||
#[inline(always)]
|
||||
fn type_key() -> *const c_void {
|
||||
static ASYNC_POLL_UPVALUE_TYPE_KEY: u8 = 0;
|
||||
&ASYNC_POLL_UPVALUE_TYPE_KEY as *const u8 as *const c_void
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
static WAKER_TYPE_KEY: u8 = 0;
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
impl TypeKey for Option<std::task::Waker> {
|
||||
#[inline(always)]
|
||||
fn type_key() -> *const c_void {
|
||||
static WAKER_TYPE_KEY: u8 = 0;
|
||||
&WAKER_TYPE_KEY as *const u8 as *const c_void
|
||||
}
|
||||
}
|
||||
|
||||
+83
-290
@@ -1,9 +1,9 @@
|
||||
use std::ffi::CStr;
|
||||
use std::os::raw::{c_int, c_void};
|
||||
use std::{ptr, str};
|
||||
use std::{mem, ptr};
|
||||
|
||||
use crate::error::Result;
|
||||
use crate::util::{check_stack, push_string, push_table, rawset_field, TypeKey};
|
||||
use crate::userdata::collect_userdata;
|
||||
use crate::util::{check_stack, get_metatable_ptr, push_table, rawset_field, TypeKey};
|
||||
|
||||
// Pushes the userdata and attaches a metatable with __gc method.
|
||||
// Internally uses 3 stack spaces, does not call checkstack.
|
||||
@@ -11,11 +11,30 @@ pub(crate) unsafe fn push_internal_userdata<T: TypeKey>(
|
||||
state: *mut ffi::lua_State,
|
||||
t: T,
|
||||
protect: bool,
|
||||
) -> Result<()> {
|
||||
push_userdata(state, t, protect)?;
|
||||
) -> Result<*mut T> {
|
||||
#[cfg(not(feature = "luau"))]
|
||||
let ud_ptr = if protect {
|
||||
protect_lua!(state, 0, 1, move |state| {
|
||||
let ud_ptr = ffi::lua_newuserdata(state, const { mem::size_of::<T>() }) as *mut T;
|
||||
ptr::write(ud_ptr, t);
|
||||
ud_ptr
|
||||
})?
|
||||
} else {
|
||||
let ud_ptr = ffi::lua_newuserdata(state, const { mem::size_of::<T>() }) as *mut T;
|
||||
ptr::write(ud_ptr, t);
|
||||
ud_ptr
|
||||
};
|
||||
|
||||
#[cfg(feature = "luau")]
|
||||
let ud_ptr = if protect {
|
||||
protect_lua!(state, 0, 1, move |state| ffi::lua_newuserdata_t::<T>(state, t))?
|
||||
} else {
|
||||
ffi::lua_newuserdata_t::<T>(state, t)
|
||||
};
|
||||
|
||||
get_internal_metatable::<T>(state);
|
||||
ffi::lua_setmetatable(state, -2);
|
||||
Ok(())
|
||||
Ok(ud_ptr)
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
@@ -28,7 +47,7 @@ pub(crate) unsafe fn get_internal_metatable<T: TypeKey>(state: *mut ffi::lua_Sta
|
||||
// Uses 6 stack spaces and calls checkstack.
|
||||
pub(crate) unsafe fn init_internal_metatable<T: TypeKey>(
|
||||
state: *mut ffi::lua_State,
|
||||
customize_fn: Option<fn(*mut ffi::lua_State) -> Result<()>>,
|
||||
customize_fn: Option<fn(*mut ffi::lua_State)>,
|
||||
) -> Result<()> {
|
||||
check_stack(state, 6)?;
|
||||
|
||||
@@ -36,117 +55,113 @@ pub(crate) unsafe fn init_internal_metatable<T: TypeKey>(
|
||||
|
||||
#[cfg(not(feature = "luau"))]
|
||||
{
|
||||
ffi::lua_pushcfunction(state, userdata_destructor::<T>);
|
||||
ffi::lua_pushcfunction(state, collect_userdata::<T>);
|
||||
rawset_field(state, -2, "__gc")?;
|
||||
}
|
||||
|
||||
ffi::lua_pushboolean(state, 0);
|
||||
rawset_field(state, -2, "__metatable")?;
|
||||
|
||||
if let Some(f) = customize_fn {
|
||||
f(state)?;
|
||||
}
|
||||
|
||||
protect_lua!(state, 1, 0, |state| {
|
||||
if let Some(f) = customize_fn {
|
||||
f(state);
|
||||
}
|
||||
|
||||
ffi::lua_rawsetp(state, ffi::LUA_REGISTRYINDEX, T::type_key());
|
||||
})?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Uses 2 stack spaces, does not call checkstack
|
||||
// Uses up to 1 stack space, does not call `checkstack`
|
||||
pub(crate) unsafe fn get_internal_userdata<T: TypeKey>(
|
||||
state: *mut ffi::lua_State,
|
||||
index: c_int,
|
||||
type_mt_ptr: *const c_void,
|
||||
mut type_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 {
|
||||
if ud.is_null() {
|
||||
return ptr::null_mut();
|
||||
}
|
||||
if !type_mt_ptr.is_null() {
|
||||
let ud_mt_ptr = ffi::lua_topointer(state, -1);
|
||||
ffi::lua_pop(state, 1);
|
||||
if ud_mt_ptr != type_mt_ptr {
|
||||
return ptr::null_mut();
|
||||
}
|
||||
} else {
|
||||
let mt_ptr = get_metatable_ptr(state, index);
|
||||
if type_mt_ptr.is_null() {
|
||||
get_internal_metatable::<T>(state);
|
||||
let res = ffi::lua_rawequal(state, -1, -2);
|
||||
ffi::lua_pop(state, 2);
|
||||
if res == 0 {
|
||||
return ptr::null_mut();
|
||||
}
|
||||
type_mt_ptr = ffi::lua_topointer(state, -1);
|
||||
ffi::lua_pop(state, 1);
|
||||
}
|
||||
if mt_ptr != type_mt_ptr {
|
||||
return ptr::null_mut();
|
||||
}
|
||||
ud
|
||||
}
|
||||
|
||||
// Internally uses 3 stack spaces, does not call checkstack.
|
||||
#[inline]
|
||||
pub(crate) unsafe fn push_userdata<T>(state: *mut ffi::lua_State, t: T, protect: bool) -> Result<()> {
|
||||
#[cfg(not(feature = "luau"))]
|
||||
let ud = if protect {
|
||||
#[cfg(not(feature = "luau"))]
|
||||
pub(crate) unsafe fn push_uninit_userdata<T>(state: *mut ffi::lua_State, protect: bool) -> Result<*mut T> {
|
||||
if protect {
|
||||
protect_lua!(state, 0, 1, |state| {
|
||||
ffi::lua_newuserdata(state, std::mem::size_of::<T>()) as *mut T
|
||||
})?
|
||||
ffi::lua_newuserdata(state, const { mem::size_of::<T>() }) as *mut T
|
||||
})
|
||||
} else {
|
||||
ffi::lua_newuserdata(state, std::mem::size_of::<T>()) as *mut T
|
||||
};
|
||||
#[cfg(feature = "luau")]
|
||||
let ud = if protect {
|
||||
protect_lua!(state, 0, 1, |state| { ffi::lua_newuserdata_t::<T>(state) })?
|
||||
} else {
|
||||
ffi::lua_newuserdata_t::<T>(state)
|
||||
};
|
||||
ptr::write(ud, t);
|
||||
Ok(())
|
||||
Ok(ffi::lua_newuserdata(state, const { mem::size_of::<T>() }) as *mut T)
|
||||
}
|
||||
}
|
||||
|
||||
// Internally uses 3 stack spaces, does not call checkstack.
|
||||
#[cfg(feature = "lua54")]
|
||||
#[inline]
|
||||
pub(crate) unsafe fn push_userdata_uv<T>(
|
||||
state: *mut ffi::lua_State,
|
||||
t: T,
|
||||
nuvalue: c_int,
|
||||
protect: bool,
|
||||
) -> Result<()> {
|
||||
let ud = if protect {
|
||||
pub(crate) unsafe fn push_userdata<T>(state: *mut ffi::lua_State, t: T, protect: bool) -> Result<*mut T> {
|
||||
let size = const { mem::size_of::<T>() };
|
||||
|
||||
#[cfg(not(feature = "luau"))]
|
||||
let ud_ptr = if protect {
|
||||
protect_lua!(state, 0, 1, move |state| ffi::lua_newuserdata(state, size))?
|
||||
} else {
|
||||
ffi::lua_newuserdata(state, size)
|
||||
} as *mut T;
|
||||
|
||||
#[cfg(feature = "luau")]
|
||||
let ud_ptr = if protect {
|
||||
protect_lua!(state, 0, 1, |state| {
|
||||
ffi::lua_newuserdatauv(state, std::mem::size_of::<T>(), nuvalue) as *mut T
|
||||
ffi::lua_newuserdatadtor(state, size, collect_userdata::<T>)
|
||||
})?
|
||||
} else {
|
||||
ffi::lua_newuserdatauv(state, std::mem::size_of::<T>(), nuvalue) as *mut T
|
||||
};
|
||||
ptr::write(ud, t);
|
||||
Ok(())
|
||||
ffi::lua_newuserdatadtor(state, size, collect_userdata::<T>)
|
||||
} as *mut T;
|
||||
|
||||
ptr::write(ud_ptr, t);
|
||||
Ok(ud_ptr)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[track_caller]
|
||||
pub(crate) unsafe fn get_userdata<T>(state: *mut ffi::lua_State, index: c_int) -> *mut T {
|
||||
let ud = ffi::lua_touserdata(state, index) as *mut T;
|
||||
mlua_debug_assert!(!ud.is_null(), "userdata pointer is null");
|
||||
ud
|
||||
}
|
||||
|
||||
// Pops the userdata off of the top of the stack and returns it to rust, invalidating the lua
|
||||
// userdata and gives it the special "destructed" userdata metatable. Userdata must not have been
|
||||
// previously invalidated, and this method does not check for this.
|
||||
// Uses 1 extra stack space and does not call checkstack.
|
||||
pub(crate) unsafe fn take_userdata<T>(state: *mut ffi::lua_State) -> T {
|
||||
// We set the metatable of userdata on __gc to a special table with no __gc method and with
|
||||
// metamethods that trigger an error on access. We do this so that it will not be double
|
||||
// dropped, and also so that it cannot be used or identified as any particular userdata type
|
||||
// after the first call to __gc.
|
||||
/// Unwraps `T` from the Lua userdata and invalidating it by setting the special "destructed"
|
||||
/// metatable.
|
||||
///
|
||||
/// This method does not check that userdata is of type `T` and was not previously invalidated.
|
||||
///
|
||||
/// Uses 1 extra stack space, does not call checkstack.
|
||||
pub(crate) unsafe fn take_userdata<T>(state: *mut ffi::lua_State, idx: c_int) -> T {
|
||||
#[rustfmt::skip]
|
||||
let idx = if idx < 0 { ffi::lua_absindex(state, idx) } else { idx };
|
||||
|
||||
// Update the metatable of this userdata to a special one with no `__gc` method and with
|
||||
// metamethods that trigger an error on access.
|
||||
// We do this so that it will not be double dropped or used after being dropped.
|
||||
get_destructed_userdata_metatable(state);
|
||||
ffi::lua_setmetatable(state, -2);
|
||||
let ud = get_userdata::<T>(state, -1);
|
||||
ffi::lua_setmetatable(state, idx);
|
||||
let ud = get_userdata::<T>(state, idx);
|
||||
|
||||
// Update userdata tag to disable destructor and mark as destructed
|
||||
#[cfg(feature = "luau")]
|
||||
ffi::lua_setuserdatatag(state, -1, 1);
|
||||
ffi::lua_setuserdatatag(state, idx, 1);
|
||||
|
||||
ffi::lua_pop(state, 1);
|
||||
ptr::read(ud)
|
||||
}
|
||||
|
||||
@@ -155,226 +170,4 @@ pub(crate) unsafe fn get_destructed_userdata_metatable(state: *mut ffi::lua_Stat
|
||||
ffi::lua_rawgetp(state, ffi::LUA_REGISTRYINDEX, key);
|
||||
}
|
||||
|
||||
// Populates the given table with the appropriate members to be a userdata metatable for the given
|
||||
// type. This function takes the given table at the `metatable` index, and adds an appropriate
|
||||
// `__gc` member to it for the given type and a `__metatable` entry to protect the table from script
|
||||
// access. The function also, if given a `field_getters` or `methods` tables, will create an
|
||||
// `__index` metamethod (capturing previous one) to lookup in `field_getters` first, then `methods`
|
||||
// and falling back to the captured `__index` if no matches found.
|
||||
// The same is also applicable for `__newindex` metamethod and `field_setters` table.
|
||||
// Internally uses 9 stack spaces and does not call checkstack.
|
||||
pub(crate) unsafe fn init_userdata_metatable(
|
||||
state: *mut ffi::lua_State,
|
||||
metatable: c_int,
|
||||
field_getters: Option<c_int>,
|
||||
field_setters: Option<c_int>,
|
||||
methods: Option<c_int>,
|
||||
extra_init: Option<fn(*mut ffi::lua_State) -> Result<()>>,
|
||||
) -> Result<()> {
|
||||
ffi::lua_pushvalue(state, metatable);
|
||||
|
||||
if field_getters.is_some() || methods.is_some() {
|
||||
// Push `__index` generator function
|
||||
init_userdata_metatable_index(state)?;
|
||||
|
||||
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 => {
|
||||
for &idx in &[field_getters, methods] {
|
||||
if let Some(idx) = idx {
|
||||
ffi::lua_pushvalue(state, idx);
|
||||
} else {
|
||||
ffi::lua_pushnil(state);
|
||||
}
|
||||
}
|
||||
|
||||
// Generate `__index`
|
||||
protect_lua!(state, 4, 1, fn(state) ffi::lua_call(state, 3, 1))?;
|
||||
}
|
||||
_ => mlua_panic!("improper __index type {}", index_type),
|
||||
}
|
||||
|
||||
rawset_field(state, -2, "__index")?;
|
||||
}
|
||||
|
||||
if let Some(field_setters) = field_setters {
|
||||
// Push `__newindex` generator function
|
||||
init_userdata_metatable_newindex(state)?;
|
||||
|
||||
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 => {
|
||||
ffi::lua_pushvalue(state, field_setters);
|
||||
// Generate `__newindex`
|
||||
protect_lua!(state, 3, 1, fn(state) ffi::lua_call(state, 2, 1))?;
|
||||
}
|
||||
_ => mlua_panic!("improper __newindex type {}", newindex_type),
|
||||
}
|
||||
|
||||
rawset_field(state, -2, "__newindex")?;
|
||||
}
|
||||
|
||||
// Additional initialization
|
||||
if let Some(extra_init) = extra_init {
|
||||
extra_init(state)?;
|
||||
}
|
||||
|
||||
ffi::lua_pushboolean(state, 0);
|
||||
rawset_field(state, -2, "__metatable")?;
|
||||
|
||||
ffi::lua_pop(state, 1);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
unsafe extern "C-unwind" fn lua_error_impl(state: *mut ffi::lua_State) -> c_int {
|
||||
ffi::lua_error(state);
|
||||
}
|
||||
|
||||
unsafe extern "C-unwind" fn lua_isfunction_impl(state: *mut ffi::lua_State) -> c_int {
|
||||
ffi::lua_pushboolean(state, ffi::lua_isfunction(state, -1));
|
||||
1
|
||||
}
|
||||
|
||||
unsafe extern "C-unwind" fn lua_istable_impl(state: *mut ffi::lua_State) -> c_int {
|
||||
ffi::lua_pushboolean(state, ffi::lua_istable(state, -1));
|
||||
1
|
||||
}
|
||||
|
||||
unsafe fn init_userdata_metatable_index(state: *mut ffi::lua_State) -> Result<()> {
|
||||
let index_key = &USERDATA_METATABLE_INDEX as *const u8 as *const _;
|
||||
if ffi::lua_rawgetp(state, ffi::LUA_REGISTRYINDEX, index_key) == ffi::LUA_TFUNCTION {
|
||||
return Ok(());
|
||||
}
|
||||
ffi::lua_pop(state, 1);
|
||||
|
||||
// Create and cache `__index` generator
|
||||
let code = cstr!(
|
||||
r#"
|
||||
local error, isfunction, istable = ...
|
||||
return function (__index, field_getters, methods)
|
||||
-- Common case: has field getters and index is a table
|
||||
if field_getters ~= nil and methods == nil and istable(__index) then
|
||||
return function (self, key)
|
||||
local field_getter = field_getters[key]
|
||||
if field_getter ~= nil then
|
||||
return field_getter(self)
|
||||
end
|
||||
return __index[key]
|
||||
end
|
||||
end
|
||||
|
||||
return function (self, key)
|
||||
if field_getters ~= nil then
|
||||
local field_getter = field_getters[key]
|
||||
if field_getter ~= nil then
|
||||
return field_getter(self)
|
||||
end
|
||||
end
|
||||
|
||||
if methods ~= nil then
|
||||
local method = methods[key]
|
||||
if method ~= nil then
|
||||
return method
|
||||
end
|
||||
end
|
||||
|
||||
if isfunction(__index) then
|
||||
return __index(self, key)
|
||||
elseif __index == nil then
|
||||
error("attempt to get an unknown field '"..key.."'")
|
||||
else
|
||||
return __index[key]
|
||||
end
|
||||
end
|
||||
end
|
||||
"#
|
||||
);
|
||||
let code_len = CStr::from_ptr(code).to_bytes().len();
|
||||
protect_lua!(state, 0, 1, |state| {
|
||||
let ret = ffi::luaL_loadbuffer(state, code, code_len, cstr!("__mlua_index"));
|
||||
if ret != ffi::LUA_OK {
|
||||
ffi::lua_error(state);
|
||||
}
|
||||
ffi::lua_pushcfunction(state, lua_error_impl);
|
||||
ffi::lua_pushcfunction(state, lua_isfunction_impl);
|
||||
ffi::lua_pushcfunction(state, lua_istable_impl);
|
||||
ffi::lua_call(state, 3, 1);
|
||||
|
||||
#[cfg(feature = "luau-jit")]
|
||||
if ffi::luau_codegen_supported() != 0 {
|
||||
ffi::luau_codegen_compile(state, -1);
|
||||
}
|
||||
|
||||
// Store in the registry
|
||||
ffi::lua_pushvalue(state, -1);
|
||||
ffi::lua_rawsetp(state, ffi::LUA_REGISTRYINDEX, index_key);
|
||||
})
|
||||
}
|
||||
|
||||
unsafe fn init_userdata_metatable_newindex(state: *mut ffi::lua_State) -> Result<()> {
|
||||
let newindex_key = &USERDATA_METATABLE_NEWINDEX as *const u8 as *const _;
|
||||
if ffi::lua_rawgetp(state, ffi::LUA_REGISTRYINDEX, newindex_key) == ffi::LUA_TFUNCTION {
|
||||
return Ok(());
|
||||
}
|
||||
ffi::lua_pop(state, 1);
|
||||
|
||||
// Create and cache `__newindex` generator
|
||||
let code = cstr!(
|
||||
r#"
|
||||
local error, isfunction = ...
|
||||
return function (__newindex, field_setters)
|
||||
return function (self, key, value)
|
||||
if field_setters ~= nil then
|
||||
local field_setter = field_setters[key]
|
||||
if field_setter ~= nil then
|
||||
field_setter(self, value)
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
if isfunction(__newindex) then
|
||||
__newindex(self, key, value)
|
||||
elseif __newindex == nil then
|
||||
error("attempt to set an unknown field '"..key.."'")
|
||||
else
|
||||
__newindex[key] = value
|
||||
end
|
||||
end
|
||||
end
|
||||
"#
|
||||
);
|
||||
let code_len = CStr::from_ptr(code).to_bytes().len();
|
||||
protect_lua!(state, 0, 1, |state| {
|
||||
let ret = ffi::luaL_loadbuffer(state, code, code_len, cstr!("__mlua_newindex"));
|
||||
if ret != ffi::LUA_OK {
|
||||
ffi::lua_error(state);
|
||||
}
|
||||
ffi::lua_pushcfunction(state, lua_error_impl);
|
||||
ffi::lua_pushcfunction(state, lua_isfunction_impl);
|
||||
ffi::lua_call(state, 2, 1);
|
||||
|
||||
#[cfg(feature = "luau-jit")]
|
||||
if ffi::luau_codegen_supported() != 0 {
|
||||
ffi::luau_codegen_compile(state, -1);
|
||||
}
|
||||
|
||||
// Store in the registry
|
||||
ffi::lua_pushvalue(state, -1);
|
||||
ffi::lua_rawsetp(state, ffi::LUA_REGISTRYINDEX, newindex_key);
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "luau"))]
|
||||
pub(crate) unsafe extern "C-unwind" fn userdata_destructor<T>(state: *mut ffi::lua_State) -> c_int {
|
||||
// It's probably NOT a good idea to catch Rust panics in finalizer
|
||||
// Lua 5.4 ignores it, other versions generates `LUA_ERRGCMM` without calling message handler
|
||||
take_userdata::<T>(state);
|
||||
0
|
||||
}
|
||||
|
||||
pub(crate) static DESTRUCTED_USERDATA_METATABLE: u8 = 0;
|
||||
static USERDATA_METATABLE_INDEX: u8 = 0;
|
||||
static USERDATA_METATABLE_NEWINDEX: u8 = 0;
|
||||
|
||||
+155
-292
@@ -1,35 +1,33 @@
|
||||
use std::cell::RefCell;
|
||||
use std::cmp::Ordering;
|
||||
use std::collections::{vec_deque, HashSet, VecDeque};
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use std::os::raw::{c_int, c_void};
|
||||
use std::collections::HashSet;
|
||||
use std::os::raw::c_void;
|
||||
use std::string::String as StdString;
|
||||
use std::sync::Arc;
|
||||
use std::{fmt, mem, ptr, str};
|
||||
use std::{fmt, ptr, str};
|
||||
|
||||
use num_traits::FromPrimitive;
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
use crate::function::Function;
|
||||
use crate::state::{Lua, RawLua};
|
||||
use crate::string::{BorrowedStr, String};
|
||||
use crate::table::Table;
|
||||
use crate::thread::Thread;
|
||||
use crate::types::{Integer, LightUserData, Number, SubtypeId};
|
||||
use crate::types::{Integer, LightUserData, Number, ValueRef};
|
||||
use crate::userdata::AnyUserData;
|
||||
use crate::util::{check_stack, StackGuard};
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
#[cfg(feature = "serde")]
|
||||
use {
|
||||
crate::table::SerializableTable,
|
||||
rustc_hash::FxHashSet,
|
||||
serde::ser::{self, Serialize, Serializer},
|
||||
std::{rc::Rc, result::Result as StdResult},
|
||||
std::{cell::RefCell, rc::Rc, result::Result as StdResult},
|
||||
};
|
||||
|
||||
/// A dynamically typed Lua value. The `String`, `Table`, `Function`, `Thread`, and `UserData`
|
||||
/// variants contain handle types into the internal Lua state. It is a logic error to mix handle
|
||||
/// types between separate `Lua` instances, and doing so will result in a panic.
|
||||
/// A dynamically typed Lua value.
|
||||
///
|
||||
/// The non-primitive variants (eg. string/table/function/thread/userdata) contain handle types
|
||||
/// into the internal Lua state. It is a logic error to mix handle types between separate
|
||||
/// `Lua` instances, and doing so will result in a panic.
|
||||
#[derive(Clone)]
|
||||
pub enum Value {
|
||||
/// The Lua value `nil`.
|
||||
@@ -47,7 +45,7 @@ pub enum Value {
|
||||
/// A Luau vector.
|
||||
#[cfg(any(feature = "luau", doc))]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
|
||||
Vector(crate::types::Vector),
|
||||
Vector(crate::Vector),
|
||||
/// An interned string, managed by Lua.
|
||||
///
|
||||
/// Unlike Rust strings, Lua strings may not be valid UTF-8.
|
||||
@@ -59,10 +57,17 @@ pub enum Value {
|
||||
/// Reference to a Lua thread (or coroutine).
|
||||
Thread(Thread),
|
||||
/// Reference to a userdata object that holds a custom type which implements `UserData`.
|
||||
///
|
||||
/// Special builtin userdata types will be represented as other `Value` variants.
|
||||
UserData(AnyUserData),
|
||||
/// A Luau buffer.
|
||||
#[cfg(any(feature = "luau", doc))]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
|
||||
Buffer(crate::Buffer),
|
||||
/// `Error` is a special builtin userdata type. When received from Lua it is implicitly cloned.
|
||||
Error(Box<Error>),
|
||||
/// Any other value not known to mlua (eg. LuaJIT CData).
|
||||
Other(#[doc(hidden)] ValueRef),
|
||||
}
|
||||
|
||||
pub use self::Value::Nil;
|
||||
@@ -74,7 +79,7 @@ impl Value {
|
||||
pub const NULL: Value = Value::LightUserData(LightUserData(ptr::null_mut()));
|
||||
|
||||
/// Returns type name of this value.
|
||||
pub const fn type_name(&self) -> &'static str {
|
||||
pub fn type_name(&self) -> &'static str {
|
||||
match *self {
|
||||
Value::Nil => "nil",
|
||||
Value::Boolean(_) => "boolean",
|
||||
@@ -87,27 +92,26 @@ impl Value {
|
||||
Value::Table(_) => "table",
|
||||
Value::Function(_) => "function",
|
||||
Value::Thread(_) => "thread",
|
||||
Value::UserData(AnyUserData(_, SubtypeId::None)) => "userdata",
|
||||
Value::UserData(_) => "userdata",
|
||||
#[cfg(feature = "luau")]
|
||||
Value::UserData(AnyUserData(_, SubtypeId::Buffer)) => "buffer",
|
||||
#[cfg(feature = "luajit")]
|
||||
Value::UserData(AnyUserData(_, SubtypeId::CData)) => "cdata",
|
||||
Value::Buffer(_) => "buffer",
|
||||
Value::Error(_) => "error",
|
||||
Value::Other(_) => "other",
|
||||
}
|
||||
}
|
||||
|
||||
/// Compares two values for equality.
|
||||
///
|
||||
/// Equality comparisons do not convert strings to numbers or vice versa.
|
||||
/// Tables, Functions, Threads, and Userdata are compared by reference:
|
||||
/// Tables, functions, threads, and userdata are compared by reference:
|
||||
/// two objects are considered equal only if they are the same object.
|
||||
///
|
||||
/// If Tables or Userdata have `__eq` metamethod then mlua will try to invoke it.
|
||||
/// If table or userdata have `__eq` metamethod then mlua will try to invoke it.
|
||||
/// The first value is checked first. If that value does not define a metamethod
|
||||
/// for `__eq`, then mlua will check the second value.
|
||||
/// Then mlua calls the metamethod with the two values as arguments, if found.
|
||||
pub fn equals<T: AsRef<Self>>(&self, other: T) -> Result<bool> {
|
||||
match (self, other.as_ref()) {
|
||||
pub fn equals(&self, other: &Self) -> Result<bool> {
|
||||
match (self, other) {
|
||||
(Value::Table(a), Value::Table(b)) => a.equals(b),
|
||||
(Value::UserData(a), Value::UserData(b)) => a.equals(b),
|
||||
(a, b) => Ok(a == b),
|
||||
@@ -124,21 +128,42 @@ impl Value {
|
||||
#[inline]
|
||||
pub fn to_pointer(&self) -> *const c_void {
|
||||
match self {
|
||||
Value::String(String(vref)) => {
|
||||
// In Lua < 5.4 (excluding Luau), string pointers are NULL
|
||||
// Use alternative approach
|
||||
let lua = vref.lua.lock();
|
||||
unsafe { ffi::lua_tostring(lua.ref_thread(), vref.index) as *const c_void }
|
||||
}
|
||||
Value::LightUserData(ud) => ud.0,
|
||||
Value::String(String(r))
|
||||
| Value::Table(Table(r))
|
||||
| Value::Function(Function(r))
|
||||
| Value::Thread(Thread(r, ..))
|
||||
| Value::UserData(AnyUserData(r, ..)) => r.to_pointer(),
|
||||
Value::Table(Table(vref))
|
||||
| Value::Function(Function(vref))
|
||||
| Value::Thread(Thread(vref, ..))
|
||||
| Value::UserData(AnyUserData(vref))
|
||||
| Value::Other(vref) => vref.to_pointer(),
|
||||
#[cfg(feature = "luau")]
|
||||
Value::Buffer(crate::Buffer(vref)) => vref.to_pointer(),
|
||||
_ => ptr::null(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Converts the value to a string.
|
||||
///
|
||||
/// If the value has a metatable with a `__tostring` method, then it will be called to get the
|
||||
/// result.
|
||||
/// This might invoke the `__tostring` metamethod for non-primitive types (eg. tables,
|
||||
/// functions).
|
||||
pub fn to_string(&self) -> Result<StdString> {
|
||||
unsafe fn invoke_to_string(vref: &ValueRef) -> Result<StdString> {
|
||||
let lua = vref.lua.lock();
|
||||
let state = lua.state();
|
||||
let _guard = StackGuard::new(state);
|
||||
check_stack(state, 3)?;
|
||||
|
||||
lua.push_ref(vref);
|
||||
protect_lua!(state, 1, 1, fn(state) {
|
||||
ffi::luaL_tolstring(state, -1, ptr::null_mut());
|
||||
})?;
|
||||
Ok(String(lua.pop_ref()).to_str()?.to_string())
|
||||
}
|
||||
|
||||
match self {
|
||||
Value::Nil => Ok("nil".to_string()),
|
||||
Value::Boolean(b) => Ok(b.to_string()),
|
||||
@@ -149,21 +174,13 @@ impl Value {
|
||||
#[cfg(feature = "luau")]
|
||||
Value::Vector(v) => Ok(v.to_string()),
|
||||
Value::String(s) => Ok(s.to_str()?.to_string()),
|
||||
Value::Table(Table(r))
|
||||
| Value::Function(Function(r))
|
||||
| Value::Thread(Thread(r, ..))
|
||||
| Value::UserData(AnyUserData(r, ..)) => unsafe {
|
||||
let lua = r.lua.lock();
|
||||
let state = lua.state();
|
||||
let _guard = StackGuard::new(state);
|
||||
check_stack(state, 3)?;
|
||||
|
||||
lua.push_ref(r);
|
||||
protect_lua!(state, 1, 1, fn(state) {
|
||||
ffi::luaL_tolstring(state, -1, ptr::null_mut());
|
||||
})?;
|
||||
Ok(String(lua.pop_ref()).to_str()?.to_string())
|
||||
},
|
||||
Value::Table(Table(vref))
|
||||
| Value::Function(Function(vref))
|
||||
| Value::Thread(Thread(vref, ..))
|
||||
| Value::UserData(AnyUserData(vref))
|
||||
| Value::Other(vref) => unsafe { invoke_to_string(vref) },
|
||||
#[cfg(feature = "luau")]
|
||||
Value::Buffer(crate::Buffer(vref)) => unsafe { invoke_to_string(vref) },
|
||||
Value::Error(err) => Ok(err.to_string()),
|
||||
}
|
||||
}
|
||||
@@ -175,6 +192,8 @@ impl Value {
|
||||
}
|
||||
|
||||
/// Returns `true` if the value is a [`NULL`].
|
||||
///
|
||||
/// [`NULL`]: Value::NULL
|
||||
#[inline]
|
||||
pub fn is_null(&self) -> bool {
|
||||
self == &Self::NULL
|
||||
@@ -253,7 +272,10 @@ impl Value {
|
||||
/// If the value is a Lua [`Integer`], try to convert it to `i64` or return `None` otherwise.
|
||||
#[inline]
|
||||
pub fn as_i64(&self) -> Option<i64> {
|
||||
self.as_integer().map(i64::from)
|
||||
#[cfg(target_pointer_width = "64")]
|
||||
return self.as_integer();
|
||||
#[cfg(not(target_pointer_width = "64"))]
|
||||
return self.as_integer().map(i64::from);
|
||||
}
|
||||
|
||||
/// Cast the value to `u64`.
|
||||
@@ -334,14 +356,22 @@ impl Value {
|
||||
///
|
||||
/// If the value is a Lua [`String`], try to convert it to [`BorrowedStr`] or return `None`
|
||||
/// otherwise.
|
||||
#[deprecated(
|
||||
since = "0.11.0",
|
||||
note = "This method does not follow Rust naming convention. Use `as_string().and_then(|s| s.to_str().ok())` instead."
|
||||
)]
|
||||
#[inline]
|
||||
pub fn as_str(&self) -> Option<BorrowedStr> {
|
||||
pub fn as_str(&self) -> Option<BorrowedStr<'_>> {
|
||||
self.as_string().and_then(|s| s.to_str().ok())
|
||||
}
|
||||
|
||||
/// Cast the value to [`StdString`].
|
||||
///
|
||||
/// If the value is a Lua [`String`], converts it to [`StdString`] or returns `None` otherwise.
|
||||
#[deprecated(
|
||||
since = "0.11.0",
|
||||
note = "This method does not follow Rust naming convention. Use `as_string().map(|s| s.to_string_lossy())` instead."
|
||||
)]
|
||||
#[inline]
|
||||
pub fn as_string_lossy(&self) -> Option<StdString> {
|
||||
self.as_string().map(|s| s.to_string_lossy())
|
||||
@@ -415,41 +445,60 @@ impl Value {
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `true` if the value is a Buffer wrapped in [`AnyUserData`].
|
||||
/// Cast the value to a [`Buffer`].
|
||||
///
|
||||
/// If the value is [`Buffer`], returns it or `None` otherwise.
|
||||
///
|
||||
/// [`Buffer`]: crate::Buffer
|
||||
#[cfg(any(feature = "luau", doc))]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
|
||||
#[doc(hidden)]
|
||||
#[inline]
|
||||
pub fn is_buffer(&self) -> bool {
|
||||
self.as_userdata()
|
||||
.map(|ud| ud.1 == SubtypeId::Buffer)
|
||||
.unwrap_or_default()
|
||||
pub fn as_buffer(&self) -> Option<&crate::Buffer> {
|
||||
match self {
|
||||
Value::Buffer(b) => Some(b),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `true` if the value is a CData wrapped in [`AnyUserData`].
|
||||
#[cfg(any(feature = "luajit", doc))]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "luajit")))]
|
||||
#[doc(hidden)]
|
||||
/// Returns `true` if the value is a [`Buffer`].
|
||||
///
|
||||
/// [`Buffer`]: crate::Buffer
|
||||
#[cfg(any(feature = "luau", doc))]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
|
||||
#[inline]
|
||||
pub fn is_cdata(&self) -> bool {
|
||||
self.as_userdata()
|
||||
.map(|ud| ud.1 == SubtypeId::CData)
|
||||
.unwrap_or_default()
|
||||
pub fn is_buffer(&self) -> bool {
|
||||
self.as_buffer().is_some()
|
||||
}
|
||||
|
||||
/// Returns `true` if the value is an [`Error`].
|
||||
#[inline]
|
||||
pub fn is_error(&self) -> bool {
|
||||
self.as_error().is_some()
|
||||
}
|
||||
|
||||
/// Cast the value to [`Error`].
|
||||
///
|
||||
/// If the value is an [`Error`], returns it or `None` otherwise.
|
||||
pub fn as_error(&self) -> Option<&Error> {
|
||||
match self {
|
||||
Value::Error(e) => Some(e),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Wrap reference to this Value into [`SerializableValue`].
|
||||
///
|
||||
/// This allows customizing serialization behavior using serde.
|
||||
#[cfg(feature = "serialize")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "serialize")))]
|
||||
#[cfg(feature = "serde")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "serde")))]
|
||||
#[doc(hidden)]
|
||||
pub fn to_serializable(&self) -> SerializableValue {
|
||||
pub fn to_serializable(&self) -> SerializableValue<'_> {
|
||||
SerializableValue::new(self, Default::default(), None)
|
||||
}
|
||||
|
||||
// Compares two values.
|
||||
// Used to sort values for Debug printing.
|
||||
pub(crate) fn cmp(&self, other: &Self) -> Ordering {
|
||||
pub(crate) fn sort_cmp(&self, other: &Self) -> Ordering {
|
||||
fn cmp_num(a: Number, b: Number) -> Ordering {
|
||||
match (a, b) {
|
||||
_ if a < b => Ordering::Less,
|
||||
@@ -473,16 +522,19 @@ impl Value {
|
||||
(_, Value::Boolean(_)) => Ordering::Greater,
|
||||
// Integer && Number
|
||||
(Value::Integer(a), Value::Integer(b)) => a.cmp(b),
|
||||
(&Value::Integer(a), &Value::Number(b)) => cmp_num(a as Number, b),
|
||||
(&Value::Number(a), &Value::Integer(b)) => cmp_num(a, b as Number),
|
||||
(&Value::Number(a), &Value::Number(b)) => cmp_num(a, b),
|
||||
(Value::Integer(a), Value::Number(b)) => cmp_num(*a as Number, *b),
|
||||
(Value::Number(a), Value::Integer(b)) => cmp_num(*a, *b as Number),
|
||||
(Value::Number(a), Value::Number(b)) => cmp_num(*a, *b),
|
||||
(Value::Integer(_) | Value::Number(_), _) => Ordering::Less,
|
||||
(_, Value::Integer(_) | Value::Number(_)) => Ordering::Greater,
|
||||
// Vector (Luau)
|
||||
#[cfg(feature = "luau")]
|
||||
(Value::Vector(a), Value::Vector(b)) => a.partial_cmp(b).unwrap_or(Ordering::Equal),
|
||||
// String
|
||||
(Value::String(a), Value::String(b)) => a.as_bytes().cmp(&b.as_bytes()),
|
||||
(Value::String(_), _) => Ordering::Less,
|
||||
(_, Value::String(_)) => Ordering::Greater,
|
||||
// Other variants can be randomly ordered
|
||||
// Other variants can be ordered by their pointer
|
||||
(a, b) => a.to_pointer().cmp(&b.to_pointer()),
|
||||
}
|
||||
}
|
||||
@@ -519,17 +571,27 @@ impl Value {
|
||||
.unwrap_or_else(|| format!("userdata: {:?}", u.to_pointer()));
|
||||
write!(fmt, "{s}")
|
||||
}
|
||||
#[cfg(feature = "luau")]
|
||||
buf @ Value::Buffer(_) => write!(fmt, "buffer: {:?}", buf.to_pointer()),
|
||||
Value::Error(e) if recursive => write!(fmt, "{e:?}"),
|
||||
Value::Error(_) => write!(fmt, "error"),
|
||||
Value::Other(v) => write!(fmt, "other: {:?}", v.to_pointer()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Value {
|
||||
fn default() -> Self {
|
||||
Self::Nil
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Value {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
if fmt.alternate() {
|
||||
return self.fmt_pretty(fmt, true, 0, &mut HashSet::new());
|
||||
}
|
||||
|
||||
match self {
|
||||
Value::Nil => write!(fmt, "Nil"),
|
||||
Value::Boolean(b) => write!(fmt, "Boolean({b})"),
|
||||
@@ -543,7 +605,10 @@ impl fmt::Debug for Value {
|
||||
Value::Function(f) => write!(fmt, "{f:?}"),
|
||||
Value::Thread(t) => write!(fmt, "{t:?}"),
|
||||
Value::UserData(ud) => write!(fmt, "{ud:?}"),
|
||||
#[cfg(feature = "luau")]
|
||||
Value::Buffer(buf) => write!(fmt, "{buf:?}"),
|
||||
Value::Error(e) => write!(fmt, "Error({e:?})"),
|
||||
Value::Other(v) => write!(fmt, "Other({v:?})"),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -565,21 +630,16 @@ impl PartialEq for Value {
|
||||
(Value::Function(a), Value::Function(b)) => a == b,
|
||||
(Value::Thread(a), Value::Thread(b)) => a == b,
|
||||
(Value::UserData(a), Value::UserData(b)) => a == b,
|
||||
#[cfg(feature = "luau")]
|
||||
(Value::Buffer(a), Value::Buffer(b)) => a == b,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<Value> for Value {
|
||||
#[inline]
|
||||
fn as_ref(&self) -> &Self {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
/// A wrapped [`Value`] with customized serialization behavior.
|
||||
#[cfg(feature = "serialize")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "serialize")))]
|
||||
#[cfg(feature = "serde")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "serde")))]
|
||||
pub struct SerializableValue<'a> {
|
||||
value: &'a Value,
|
||||
options: crate::serde::de::Options,
|
||||
@@ -587,7 +647,7 @@ pub struct SerializableValue<'a> {
|
||||
visited: Option<Rc<RefCell<FxHashSet<*const c_void>>>>,
|
||||
}
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
#[cfg(feature = "serde")]
|
||||
impl Serialize for Value {
|
||||
#[inline]
|
||||
fn serialize<S: Serializer>(&self, serializer: S) -> StdResult<S::Ok, S::Error> {
|
||||
@@ -595,7 +655,7 @@ impl Serialize for Value {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
#[cfg(feature = "serde")]
|
||||
impl<'a> SerializableValue<'a> {
|
||||
#[inline]
|
||||
pub(crate) fn new(
|
||||
@@ -648,10 +708,19 @@ impl<'a> SerializableValue<'a> {
|
||||
self.options.sort_keys = enabled;
|
||||
self
|
||||
}
|
||||
|
||||
/// If true, empty Lua tables will be encoded as array, instead of map.
|
||||
///
|
||||
/// Default: **false**
|
||||
#[must_use]
|
||||
pub const fn encode_empty_tables_as_array(mut self, enabled: bool) -> Self {
|
||||
self.options.encode_empty_tables_as_array = enabled;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
impl<'a> Serialize for SerializableValue<'a> {
|
||||
#[cfg(feature = "serde")]
|
||||
impl Serialize for SerializableValue<'_> {
|
||||
fn serialize<S>(&self, serializer: S) -> StdResult<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
@@ -673,11 +742,14 @@ impl<'a> Serialize for SerializableValue<'a> {
|
||||
Value::UserData(ud) if ud.is_serializable() || self.options.deny_unsupported_types => {
|
||||
ud.serialize(serializer)
|
||||
}
|
||||
#[cfg(feature = "luau")]
|
||||
Value::Buffer(buf) => buf.serialize(serializer),
|
||||
Value::Function(_)
|
||||
| Value::Thread(_)
|
||||
| Value::UserData(_)
|
||||
| Value::LightUserData(_)
|
||||
| Value::Error(_) => {
|
||||
| Value::Error(_)
|
||||
| Value::Other(_) => {
|
||||
if self.options.deny_unsupported_types {
|
||||
let msg = format!("cannot serialize <{}>", self.value.type_name());
|
||||
Err(ser::Error::custom(msg))
|
||||
@@ -689,221 +761,12 @@ impl<'a> Serialize for SerializableValue<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Trait for types convertible to `Value`.
|
||||
pub trait IntoLua: Sized {
|
||||
/// Performs the conversion.
|
||||
fn into_lua(self, lua: &Lua) -> Result<Value>;
|
||||
|
||||
/// Pushes the value into the Lua stack.
|
||||
///
|
||||
/// # Safety
|
||||
/// This method does not check Lua stack space.
|
||||
#[doc(hidden)]
|
||||
#[inline]
|
||||
unsafe fn push_into_stack(self, lua: &RawLua) -> Result<()> {
|
||||
lua.push_value(&self.into_lua(lua.lua())?)
|
||||
}
|
||||
}
|
||||
|
||||
/// Trait for types convertible from `Value`.
|
||||
pub trait FromLua: Sized {
|
||||
/// Performs the conversion.
|
||||
fn from_lua(value: Value, lua: &Lua) -> Result<Self>;
|
||||
|
||||
/// Performs the conversion for an argument (eg. function argument).
|
||||
///
|
||||
/// `i` is the argument index (position),
|
||||
/// `to` is a function name that received the argument.
|
||||
#[doc(hidden)]
|
||||
#[inline]
|
||||
fn from_lua_arg(arg: Value, i: usize, to: Option<&str>, lua: &Lua) -> Result<Self> {
|
||||
Self::from_lua(arg, lua).map_err(|err| Error::BadArgument {
|
||||
to: to.map(|s| s.to_string()),
|
||||
pos: i,
|
||||
name: None,
|
||||
cause: Arc::new(err),
|
||||
})
|
||||
}
|
||||
|
||||
/// Performs the conversion for a value in the Lua stack at index `idx`.
|
||||
#[doc(hidden)]
|
||||
#[inline]
|
||||
unsafe fn from_stack(idx: c_int, lua: &RawLua) -> Result<Self> {
|
||||
Self::from_lua(lua.stack_value(idx, None), lua.lua())
|
||||
}
|
||||
|
||||
/// Same as `from_lua_arg` but for a value in the Lua stack at index `idx`.
|
||||
#[doc(hidden)]
|
||||
#[inline]
|
||||
unsafe fn from_stack_arg(idx: c_int, i: usize, to: Option<&str>, lua: &RawLua) -> Result<Self> {
|
||||
Self::from_stack(idx, lua).map_err(|err| Error::BadArgument {
|
||||
to: to.map(|s| s.to_string()),
|
||||
pos: i,
|
||||
name: None,
|
||||
cause: Arc::new(err),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Multiple Lua values used for both argument passing and also for multiple return values.
|
||||
#[derive(Default, Debug, Clone)]
|
||||
pub struct MultiValue(VecDeque<Value>);
|
||||
|
||||
impl Deref for MultiValue {
|
||||
type Target = VecDeque<Value>;
|
||||
|
||||
#[inline]
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl DerefMut for MultiValue {
|
||||
#[inline]
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl MultiValue {
|
||||
/// Creates an empty `MultiValue` containing no values.
|
||||
#[inline]
|
||||
pub const fn new() -> MultiValue {
|
||||
MultiValue(VecDeque::new())
|
||||
}
|
||||
|
||||
/// Creates an empty `MultiValue` container with space for at least `capacity` elements.
|
||||
pub fn with_capacity(capacity: usize) -> MultiValue {
|
||||
MultiValue(VecDeque::with_capacity(capacity))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn from_lua_iter<T: IntoLua>(lua: &Lua, iter: impl IntoIterator<Item = T>) -> Result<Self> {
|
||||
let iter = iter.into_iter();
|
||||
let mut multi_value = MultiValue::with_capacity(iter.size_hint().0);
|
||||
for value in iter {
|
||||
multi_value.push_back(value.into_lua(lua)?);
|
||||
}
|
||||
Ok(multi_value)
|
||||
}
|
||||
}
|
||||
|
||||
impl FromIterator<Value> for MultiValue {
|
||||
#[inline]
|
||||
fn from_iter<I: IntoIterator<Item = Value>>(iter: I) -> Self {
|
||||
let mut multi_value = MultiValue::new();
|
||||
multi_value.extend(iter);
|
||||
multi_value
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoIterator for MultiValue {
|
||||
type Item = Value;
|
||||
type IntoIter = vec_deque::IntoIter<Value>;
|
||||
|
||||
#[inline]
|
||||
fn into_iter(mut self) -> Self::IntoIter {
|
||||
let deque = mem::take(&mut self.0);
|
||||
mem::forget(self);
|
||||
deque.into_iter()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> IntoIterator for &'a MultiValue {
|
||||
type Item = &'a Value;
|
||||
type IntoIter = vec_deque::Iter<'a, Value>;
|
||||
|
||||
#[inline]
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.0.iter()
|
||||
}
|
||||
}
|
||||
|
||||
/// Trait for types convertible to any number of Lua values.
|
||||
///
|
||||
/// This is a generalization of `IntoLua`, allowing any number of resulting Lua values instead of
|
||||
/// just one. Any type that implements `IntoLua` will automatically implement this trait.
|
||||
pub trait IntoLuaMulti: Sized {
|
||||
/// Performs the conversion.
|
||||
fn into_lua_multi(self, lua: &Lua) -> Result<MultiValue>;
|
||||
|
||||
/// Pushes the values into the Lua stack.
|
||||
///
|
||||
/// Returns number of pushed values.
|
||||
#[doc(hidden)]
|
||||
#[inline]
|
||||
unsafe fn push_into_stack_multi(self, lua: &RawLua) -> Result<c_int> {
|
||||
let values = self.into_lua_multi(lua.lua())?;
|
||||
let len: c_int = values.len().try_into().unwrap();
|
||||
unsafe {
|
||||
check_stack(lua.state(), len + 1)?;
|
||||
for val in &values {
|
||||
lua.push_value(val)?;
|
||||
}
|
||||
}
|
||||
Ok(len)
|
||||
}
|
||||
}
|
||||
|
||||
/// Trait for types that can be created from an arbitrary number of Lua values.
|
||||
///
|
||||
/// This is a generalization of `FromLua`, allowing an arbitrary number of Lua values to participate
|
||||
/// in the conversion. Any type that implements `FromLua` will automatically implement this trait.
|
||||
pub trait FromLuaMulti: Sized {
|
||||
/// Performs the conversion.
|
||||
///
|
||||
/// In case `values` contains more values than needed to perform the conversion, the excess
|
||||
/// values should be ignored. This reflects the semantics of Lua when calling a function or
|
||||
/// assigning values. Similarly, if not enough values are given, conversions should assume that
|
||||
/// any missing values are nil.
|
||||
fn from_lua_multi(values: MultiValue, lua: &Lua) -> Result<Self>;
|
||||
|
||||
/// Performs the conversion for a list of arguments.
|
||||
///
|
||||
/// `i` is an index (position) of the first argument,
|
||||
/// `to` is a function name that received the arguments.
|
||||
#[doc(hidden)]
|
||||
#[inline]
|
||||
fn from_lua_args(args: MultiValue, i: usize, to: Option<&str>, lua: &Lua) -> Result<Self> {
|
||||
let _ = (i, to);
|
||||
Self::from_lua_multi(args, lua)
|
||||
}
|
||||
|
||||
/// Performs the conversion for a number of values in the Lua stack.
|
||||
#[doc(hidden)]
|
||||
#[inline]
|
||||
unsafe fn from_stack_multi(nvals: c_int, lua: &RawLua) -> Result<Self> {
|
||||
let mut values = MultiValue::with_capacity(nvals as usize);
|
||||
for idx in 0..nvals {
|
||||
values.push_back(lua.stack_value(-nvals + idx, None));
|
||||
}
|
||||
if nvals > 0 {
|
||||
// It's safe to clear the stack as all references moved to ref thread
|
||||
ffi::lua_pop(lua.state(), nvals);
|
||||
}
|
||||
Self::from_lua_multi(values, lua.lua())
|
||||
}
|
||||
|
||||
/// Same as `from_lua_args` but for a number of values in the Lua stack.
|
||||
#[doc(hidden)]
|
||||
#[inline]
|
||||
unsafe fn from_stack_args(nargs: c_int, i: usize, to: Option<&str>, lua: &RawLua) -> Result<Self> {
|
||||
let _ = (i, to);
|
||||
Self::from_stack_multi(nargs, lua)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod assertions {
|
||||
use super::*;
|
||||
|
||||
#[cfg(not(feature = "send"))]
|
||||
static_assertions::assert_not_impl_any!(Value: Send);
|
||||
#[cfg(not(feature = "send"))]
|
||||
static_assertions::assert_not_impl_any!(MultiValue: Send);
|
||||
|
||||
#[cfg(feature = "send")]
|
||||
static_assertions::assert_impl_all!(Value: Send, Sync);
|
||||
#[cfg(feature = "send")]
|
||||
static_assertions::assert_impl_all!(MultiValue: Send, Sync);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use std::fmt;
|
||||
|
||||
#[cfg(all(any(feature = "luau", doc), feature = "serialize"))]
|
||||
#[cfg(feature = "serde")]
|
||||
use serde::ser::{Serialize, SerializeTupleStruct, Serializer};
|
||||
|
||||
/// A Luau vector type.
|
||||
@@ -8,7 +8,7 @@ use serde::ser::{Serialize, SerializeTupleStruct, Serializer};
|
||||
/// By default vectors are 3-dimensional, but can be 4-dimensional
|
||||
/// if the `luau-vector4` feature is enabled.
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
|
||||
#[derive(Debug, Default, Clone, Copy, PartialEq)]
|
||||
#[derive(Debug, Default, Clone, Copy, PartialEq, PartialOrd)]
|
||||
pub struct Vector(pub(crate) [f32; Self::SIZE]);
|
||||
|
||||
impl fmt::Display for Vector {
|
||||
@@ -21,6 +21,7 @@ impl fmt::Display for Vector {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg_attr(not(feature = "luau"), allow(unused))]
|
||||
impl Vector {
|
||||
pub(crate) const SIZE: usize = if cfg!(feature = "luau-vector4") { 4 } else { 3 };
|
||||
|
||||
@@ -37,7 +38,6 @@ impl Vector {
|
||||
}
|
||||
|
||||
/// Creates a new vector with all components set to `0.0`.
|
||||
#[doc(hidden)]
|
||||
pub const fn zero() -> Self {
|
||||
Self([0.0; Self::SIZE])
|
||||
}
|
||||
@@ -65,7 +65,7 @@ impl Vector {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(any(feature = "luau", doc), feature = "serialize"))]
|
||||
#[cfg(feature = "serde")]
|
||||
impl Serialize for Vector {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error> {
|
||||
let mut ts = serializer.serialize_tuple_struct("Vector", Self::SIZE)?;
|
||||
@@ -84,3 +84,8 @@ impl PartialEq<[f32; Self::SIZE]> for Vector {
|
||||
self.0 == *other
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "luau")]
|
||||
impl crate::types::LuaType for Vector {
|
||||
const TYPE_ID: std::os::raw::c_int = ffi::LUA_TVECTOR;
|
||||
}
|
||||
+21
-6
@@ -1,8 +1,23 @@
|
||||
[lua54_coverage]
|
||||
features = "lua54,vendored,async,serialize,macros"
|
||||
[lua54]
|
||||
features = "lua54,vendored,async,send,serde,macros,anyhow,userdata-wrappers"
|
||||
|
||||
[lua51_coverage]
|
||||
features = "lua51,vendored,async,serialize,macros"
|
||||
[lua54_non_send]
|
||||
features = "lua54,vendored,async,serde,macros,anyhow,userdata-wrappers"
|
||||
|
||||
[luau_coverage]
|
||||
features = "luau,async,serialize,macros"
|
||||
[lua54_with_memory_limit]
|
||||
features = "lua54,vendored,async,send,serde,macros,anyhow,userdata-wrappers"
|
||||
rustflags = "--cfg force_memory_limit"
|
||||
|
||||
[lua51]
|
||||
features = "lua51,vendored,async,send,serde,macros"
|
||||
|
||||
[lua51_with_memory_limit]
|
||||
features = "lua51,vendored,async,send,serde,macros"
|
||||
rustflags = "--cfg force_memory_limit"
|
||||
|
||||
[luau]
|
||||
features = "luau,async,send,serde,macros"
|
||||
|
||||
[luau_with_memory_limit]
|
||||
features = "luau,async,send,serde,macros"
|
||||
rustflags = "--cfg force_memory_limit"
|
||||
|
||||
+154
-7
@@ -1,5 +1,6 @@
|
||||
#![cfg(feature = "async")]
|
||||
|
||||
use std::string::String as StdString;
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
|
||||
@@ -8,7 +9,7 @@ use tokio::sync::Mutex;
|
||||
|
||||
use mlua::{
|
||||
Error, Function, Lua, LuaOptions, MultiValue, ObjectLike, Result, StdLib, Table, UserData,
|
||||
UserDataMethods, Value,
|
||||
UserDataMethods, UserDataRef, Value,
|
||||
};
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
@@ -39,12 +40,51 @@ async fn test_async_function() -> Result<()> {
|
||||
async fn test_async_function_wrap() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let f = Function::wrap_async(|_, s: String| async move { Ok(s) });
|
||||
let f = Function::wrap_async(|s: StdString| async move {
|
||||
tokio::task::yield_now().await;
|
||||
Ok(s)
|
||||
});
|
||||
lua.globals().set("f", f)?;
|
||||
|
||||
let res: String = lua.load(r#"f("hello")"#).eval_async().await?;
|
||||
assert_eq!(res, "hello");
|
||||
|
||||
// Return error
|
||||
let ferr = Function::wrap_async(|| async move { Err::<(), _>(Error::runtime("some async error")) });
|
||||
lua.globals().set("ferr", ferr)?;
|
||||
lua.load(
|
||||
r#"
|
||||
local ok, err = pcall(ferr)
|
||||
assert(not ok and tostring(err):find("some async error"))
|
||||
"#,
|
||||
)
|
||||
.exec_async()
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_async_function_wrap_raw() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let f = Function::wrap_raw_async(|s: StdString| async move {
|
||||
tokio::task::yield_now().await;
|
||||
s
|
||||
});
|
||||
lua.globals().set("f", f)?;
|
||||
let res: String = lua.load(r#"f("hello")"#).eval_async().await?;
|
||||
assert_eq!(res, "hello");
|
||||
|
||||
// Return error
|
||||
let ferr = Function::wrap_raw_async(|| async move {
|
||||
tokio::task::yield_now().await;
|
||||
Err::<(), _>("some error")
|
||||
});
|
||||
lua.globals().set("ferr", ferr)?;
|
||||
let (_, err): (Value, String) = lua.load(r#"ferr()"#).eval_async().await?;
|
||||
assert_eq!(err, "some error");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -233,7 +273,7 @@ async fn test_async_lua54_to_be_closed() -> Result<()> {
|
||||
|
||||
// Don't close by default when awaiting async threads
|
||||
let co = lua.create_thread(f.clone())?;
|
||||
let _ = co.clone().into_async::<()>(()).await;
|
||||
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);
|
||||
@@ -260,7 +300,7 @@ async fn test_async_thread_stream() -> Result<()> {
|
||||
.eval()?,
|
||||
)?;
|
||||
|
||||
let mut stream = thread.into_async::<i64>(1);
|
||||
let mut stream = thread.into_async::<i64>(1)?;
|
||||
let mut sum = 0;
|
||||
while let Some(n) = stream.try_next().await? {
|
||||
sum += n;
|
||||
@@ -285,7 +325,7 @@ async fn test_async_thread() -> Result<()> {
|
||||
}
|
||||
})?;
|
||||
|
||||
let res: String = lua.create_thread(f)?.into_async(()).await?;
|
||||
let res: String = lua.create_thread(f)?.into_async(())?.await?;
|
||||
|
||||
assert_eq!(res, "done");
|
||||
|
||||
@@ -346,9 +386,16 @@ async fn test_async_table_object_like() -> Result<()> {
|
||||
table.get::<i64>("val")
|
||||
})?,
|
||||
)?;
|
||||
table.set_metatable(Some(metatable));
|
||||
table.set_metatable(Some(metatable))?;
|
||||
assert_eq!(table.call_async::<i64>(()).await.unwrap(), 15);
|
||||
|
||||
match table.call_async_method::<()>("non_existent", ()).await {
|
||||
Err(Error::RuntimeError(err)) => {
|
||||
assert!(err.contains("attempt to call a nil value (function 'non_existent')"))
|
||||
}
|
||||
r => panic!("expected RuntimeError, got {r:?}"),
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -500,6 +547,7 @@ async fn test_async_thread_error() -> Result<()> {
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_async_terminate() -> Result<()> {
|
||||
// Future captures `Lua` instance and dropped all together
|
||||
let mutex = Arc::new(Mutex::new(0u32));
|
||||
{
|
||||
let lua = Lua::new();
|
||||
@@ -518,5 +566,104 @@ async fn test_async_terminate() -> Result<()> {
|
||||
}
|
||||
assert!(mutex.try_lock().is_ok());
|
||||
|
||||
// Future is dropped, but `Lua` instance is still alive
|
||||
let lua = Lua::new();
|
||||
let func = lua.create_async_function(move |_, mutex: UserDataRef<Arc<Mutex<u32>>>| async move {
|
||||
let _guard = mutex.lock().await;
|
||||
sleep_ms(100).await;
|
||||
Ok(())
|
||||
})?;
|
||||
let mutex2 = lua.create_any_userdata(mutex.clone())?;
|
||||
let _ = tokio::time::timeout(Duration::from_millis(30), func.call_async::<()>(mutex2)).await;
|
||||
assert!(mutex.try_lock().is_ok());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_async_task() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let delay = lua.create_function(|lua, (secs, f, args): (f32, Function, MultiValue)| {
|
||||
let thread = lua.create_thread(f)?;
|
||||
let thread2 = thread.clone().into_async::<()>(args)?;
|
||||
tokio::task::spawn_local(async move {
|
||||
tokio::time::sleep(Duration::from_secs_f32(secs)).await;
|
||||
_ = thread2.await;
|
||||
});
|
||||
Ok(thread)
|
||||
})?;
|
||||
|
||||
lua.globals().set("delay", delay)?;
|
||||
let local = tokio::task::LocalSet::new();
|
||||
local
|
||||
.run_until(async {
|
||||
_ = lua
|
||||
.load("delay(0.1, function(msg) global_msg = msg end, 'done')")
|
||||
.exec_async()
|
||||
.await;
|
||||
})
|
||||
.await;
|
||||
local.await;
|
||||
assert_eq!(lua.globals().get::<String>("global_msg")?, "done");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_async_task_abort() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let sleep = lua.create_async_function(move |_lua, n: u64| async move {
|
||||
sleep_ms(n).await;
|
||||
Ok(())
|
||||
})?;
|
||||
lua.globals().set("sleep", sleep)?;
|
||||
|
||||
let local = tokio::task::LocalSet::new();
|
||||
local
|
||||
.run_until(async {
|
||||
let lua2 = lua.clone();
|
||||
let jh = tokio::task::spawn_local(async move {
|
||||
lua2.load("sleep(200) result = 'done'")
|
||||
.exec_async()
|
||||
.await
|
||||
.unwrap();
|
||||
});
|
||||
sleep_ms(100).await; // Wait for the task to start
|
||||
jh.abort();
|
||||
})
|
||||
.await;
|
||||
local.await;
|
||||
assert_eq!(lua.globals().get::<Value>("result")?, Value::Nil);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
#[cfg(not(feature = "luau"))]
|
||||
async fn test_async_hook() -> Result<()> {
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
|
||||
let lua = Lua::new();
|
||||
|
||||
static HOOK_CALLED: AtomicBool = AtomicBool::new(false);
|
||||
lua.set_global_hook(mlua::HookTriggers::new().every_line(), move |_, _| {
|
||||
if !HOOK_CALLED.swap(true, Ordering::Relaxed) {
|
||||
#[cfg(any(feature = "lu53", feature = "lua54"))]
|
||||
return Ok(mlua::VmState::Yield);
|
||||
}
|
||||
Ok(mlua::VmState::Continue)
|
||||
})?;
|
||||
|
||||
let sleep = lua.create_async_function(move |_lua, n: u64| async move {
|
||||
sleep_ms(n).await;
|
||||
Ok(())
|
||||
})?;
|
||||
lua.globals().set("sleep", sleep)?;
|
||||
|
||||
lua.load(r"sleep(100)").exec_async().await?;
|
||||
assert!(HOOK_CALLED.load(Ordering::Relaxed));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
#![cfg(feature = "luau")]
|
||||
|
||||
use mlua::{Lua, Result, Value};
|
||||
|
||||
#[test]
|
||||
fn test_buffer() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let buf1 = lua
|
||||
.load(
|
||||
r#"
|
||||
local buf = buffer.fromstring("hello")
|
||||
assert(buffer.len(buf) == 5)
|
||||
return buf
|
||||
"#,
|
||||
)
|
||||
.eval::<Value>()?;
|
||||
assert!(buf1.is_buffer());
|
||||
assert_eq!(buf1.type_name(), "buffer");
|
||||
|
||||
let buf2 = lua.load("buffer.fromstring('hello')").eval::<Value>()?;
|
||||
assert_ne!(buf1, buf2);
|
||||
|
||||
// Check that we can pass buffer type to Lua
|
||||
let buf1 = buf1.as_buffer().unwrap();
|
||||
let func = lua.create_function(|_, buf: Value| return buf.to_string())?;
|
||||
assert!(func.call::<String>(buf1)?.starts_with("buffer:"));
|
||||
|
||||
// Check buffer methods
|
||||
assert_eq!(buf1.len(), 5);
|
||||
assert_eq!(buf1.to_vec(), b"hello");
|
||||
assert_eq!(buf1.read_bytes::<3>(1), [b'e', b'l', b'l']);
|
||||
buf1.write_bytes(1, b"i");
|
||||
assert_eq!(buf1.to_vec(), b"hillo");
|
||||
|
||||
let buf3 = lua.create_buffer(b"")?;
|
||||
assert!(buf3.is_empty());
|
||||
assert!(!Value::Buffer(buf3).to_pointer().is_null());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected = "range end index 14 out of range for slice of length 13")]
|
||||
fn test_buffer_out_of_bounds_read() {
|
||||
let lua = Lua::new();
|
||||
let buf = lua.create_buffer(b"hello, world!").unwrap();
|
||||
_ = buf.read_bytes::<1>(13);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected = "range end index 16 out of range for slice of length 13")]
|
||||
fn test_buffer_out_of_bounds_write() {
|
||||
let lua = Lua::new();
|
||||
let buf = lua.create_buffer(b"hello, world!").unwrap();
|
||||
buf.write_bytes(14, b"!!");
|
||||
}
|
||||
+113
-3
@@ -1,6 +1,24 @@
|
||||
use std::{fs, io};
|
||||
|
||||
use mlua::{Lua, Result};
|
||||
use mlua::{Chunk, ChunkMode, Lua, Result};
|
||||
|
||||
#[test]
|
||||
fn test_chunk_methods() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
#[cfg(unix)]
|
||||
assert!(lua.load("return 123").name().starts_with("@tests/chunk.rs"));
|
||||
let chunk2 = lua.load("return 123").set_name("@new_name");
|
||||
assert_eq!(chunk2.name(), "@new_name");
|
||||
|
||||
let env = lua.create_table_from([("a", 987)])?;
|
||||
let chunk3 = lua.load("return a").set_environment(env.clone());
|
||||
assert_eq!(chunk3.environment().unwrap(), &env);
|
||||
assert_eq!(chunk3.mode(), ChunkMode::Text);
|
||||
assert_eq!(chunk3.call::<i32>(())?, 987);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_chunk_path() -> Result<()> {
|
||||
@@ -19,7 +37,7 @@ fn test_chunk_path() -> Result<()> {
|
||||
return 321
|
||||
"#,
|
||||
)?;
|
||||
let i: i32 = lua.load(&*temp_dir.path().join("module.lua")).eval()?;
|
||||
let i: i32 = lua.load(temp_dir.path().join("module.lua")).eval()?;
|
||||
assert_eq!(i, 321);
|
||||
|
||||
match lua.load(&*temp_dir.path().join("module2.lua")).exec() {
|
||||
@@ -27,6 +45,30 @@ fn test_chunk_path() -> Result<()> {
|
||||
res => panic!("expected io::Error, got {:?}", res),
|
||||
};
|
||||
|
||||
// &Path
|
||||
assert_eq!(
|
||||
(lua.load(&*temp_dir.path().join("module.lua").as_path())).eval::<i32>()?,
|
||||
321
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_chunk_impls() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
// StdString
|
||||
assert_eq!(lua.load(String::from("1")).eval::<i32>()?, 1);
|
||||
assert_eq!(lua.load(&String::from("2")).eval::<i32>()?, 2);
|
||||
|
||||
// &[u8]
|
||||
assert_eq!(lua.load(&b"3"[..]).eval::<i32>()?, 3);
|
||||
|
||||
// Vec<u8>
|
||||
assert_eq!(lua.load(b"4".to_vec()).eval::<i32>()?, 4);
|
||||
assert_eq!(lua.load(&b"5".to_vec()).eval::<i32>()?, 5);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -42,7 +84,7 @@ fn test_chunk_macro() -> Result<()> {
|
||||
data.raw_set("num", 1)?;
|
||||
|
||||
let ud = mlua::AnyUserData::wrap("hello");
|
||||
let f = mlua::Function::wrap(|_lua, ()| Ok(()));
|
||||
let f = mlua::Function::wrap(|| Ok(()));
|
||||
|
||||
lua.globals().set("g", 123)?;
|
||||
|
||||
@@ -68,3 +110,71 @@ fn test_chunk_macro() -> Result<()> {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(feature = "luau")]
|
||||
#[test]
|
||||
fn test_compiler() -> Result<()> {
|
||||
let compiler = mlua::Compiler::new()
|
||||
.set_optimization_level(2)
|
||||
.set_debug_level(2)
|
||||
.set_type_info_level(1)
|
||||
.set_coverage_level(2)
|
||||
.set_vector_ctor("vector.new")
|
||||
.set_vector_type("vector")
|
||||
.set_mutable_globals(["mutable_global"])
|
||||
.set_userdata_types(["MyUserdata"])
|
||||
.set_disabled_builtins(["tostring"]);
|
||||
|
||||
assert!(compiler.compile("return tostring(vector.new(1, 2, 3))").is_ok());
|
||||
|
||||
// Error
|
||||
match compiler.compile("%") {
|
||||
Err(mlua::Error::SyntaxError { ref message, .. }) => {
|
||||
assert!(message.contains("Expected identifier when parsing expression, got '%'"),);
|
||||
}
|
||||
res => panic!("expected result: {res:?}"),
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(feature = "luau")]
|
||||
#[test]
|
||||
fn test_compiler_library_constants() {
|
||||
use mlua::{Compiler, Vector};
|
||||
|
||||
let compiler = Compiler::new()
|
||||
.set_optimization_level(2)
|
||||
.add_library_constant("mylib.const_bool", true)
|
||||
.add_library_constant("mylib.const_num", 123.0)
|
||||
.add_library_constant("mylib.const_vec", Vector::zero())
|
||||
.add_library_constant("mylib.const_str", "value1");
|
||||
|
||||
let lua = Lua::new();
|
||||
lua.set_compiler(compiler);
|
||||
let const_bool = lua.load("return mylib.const_bool").eval::<bool>().unwrap();
|
||||
assert_eq!(const_bool, true);
|
||||
let const_num = lua.load("return mylib.const_num").eval::<f64>().unwrap();
|
||||
assert_eq!(const_num, 123.0);
|
||||
let const_vec = lua.load("return mylib.const_vec").eval::<Vector>().unwrap();
|
||||
assert_eq!(const_vec, Vector::zero());
|
||||
let const_str = lua.load("return mylib.const_str").eval::<String>();
|
||||
assert_eq!(const_str.unwrap(), "value1");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_chunk_wrap() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let f = Chunk::wrap("return 123");
|
||||
lua.globals().set("f", f)?;
|
||||
lua.load("assert(f() == 123)").exec().unwrap();
|
||||
|
||||
lua.globals().set("f2", Chunk::wrap("c()"))?;
|
||||
assert!(
|
||||
(lua.load("f2()").exec().err().unwrap().to_string()).contains(file!()),
|
||||
"wrong chunk location"
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -7,8 +7,6 @@ fn test_compilation() {
|
||||
t.compile_fail("tests/compile/lua_norefunwindsafe.rs");
|
||||
t.compile_fail("tests/compile/ref_nounwindsafe.rs");
|
||||
t.compile_fail("tests/compile/scope_callback_capture.rs");
|
||||
t.compile_fail("tests/compile/scope_callback_inner.rs");
|
||||
t.compile_fail("tests/compile/scope_callback_outer.rs");
|
||||
t.compile_fail("tests/compile/scope_invariance.rs");
|
||||
t.compile_fail("tests/compile/scope_mutable_aliasing.rs");
|
||||
t.compile_fail("tests/compile/scope_userdata_borrow.rs");
|
||||
@@ -17,7 +15,6 @@ fn test_compilation() {
|
||||
{
|
||||
t.compile_fail("tests/compile/async_any_userdata_method.rs");
|
||||
t.compile_fail("tests/compile/async_nonstatic_userdata.rs");
|
||||
t.compile_fail("tests/compile/async_userdata_method.rs");
|
||||
}
|
||||
|
||||
#[cfg(feature = "send")]
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use mlua::{UserDataMethods, Lua};
|
||||
use mlua::{Lua, UserDataMethods};
|
||||
|
||||
fn main() {
|
||||
let lua = Lua::new();
|
||||
@@ -6,9 +6,10 @@ fn main() {
|
||||
lua.register_userdata_type::<String>(|reg| {
|
||||
let s = String::new();
|
||||
let mut s = &s;
|
||||
reg.add_async_method("t", |_, this: &String, ()| async {
|
||||
s = this;
|
||||
reg.add_async_method("t", |_, this, ()| async {
|
||||
s = &*this;
|
||||
Ok(())
|
||||
});
|
||||
}).unwrap();
|
||||
})
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
@@ -1,20 +1,42 @@
|
||||
error[E0596]: cannot borrow `s` as mutable, as it is a captured variable in a `Fn` closure
|
||||
--> tests/compile/async_any_userdata_method.rs:9:58
|
||||
--> tests/compile/async_any_userdata_method.rs:9:49
|
||||
|
|
||||
9 | reg.add_async_method("t", |_, this: &String, ()| async {
|
||||
| ^^^^^ cannot borrow as mutable
|
||||
10 | s = this;
|
||||
9 | reg.add_async_method("t", |_, this, ()| async {
|
||||
| ^^^^^ cannot borrow as mutable
|
||||
10 | s = &*this;
|
||||
| - mutable borrow occurs due to use of `s` in closure
|
||||
|
||||
error: lifetime may not live long enough
|
||||
--> tests/compile/async_any_userdata_method.rs:9:58
|
||||
error[E0373]: async block may outlive the current function, but it borrows `this`, which is owned by the current function
|
||||
--> tests/compile/async_any_userdata_method.rs:9:49
|
||||
|
|
||||
9 | reg.add_async_method("t", |_, this: &String, ()| async {
|
||||
| ___________________________________----------------------_^
|
||||
| | | |
|
||||
| | | return type of closure `{async block@$DIR/tests/compile/async_any_userdata_method.rs:9:58: 9:63}` contains a lifetime `'2`
|
||||
9 | reg.add_async_method("t", |_, this, ()| async {
|
||||
| ^^^^^ may outlive borrowed value `this`
|
||||
10 | s = &*this;
|
||||
| ---- `this` is borrowed here
|
||||
|
|
||||
note: async block is returned here
|
||||
--> tests/compile/async_any_userdata_method.rs:9:49
|
||||
|
|
||||
9 | reg.add_async_method("t", |_, this, ()| async {
|
||||
| _________________________________________________^
|
||||
10 | | s = &*this;
|
||||
11 | | Ok(())
|
||||
12 | | });
|
||||
| |_________^
|
||||
help: to force the async block to take ownership of `this` (and any other referenced variables), use the `move` keyword
|
||||
|
|
||||
9 | reg.add_async_method("t", |_, this, ()| async move {
|
||||
| ++++
|
||||
|
||||
error: lifetime may not live long enough
|
||||
--> tests/compile/async_any_userdata_method.rs:9:49
|
||||
|
|
||||
9 | reg.add_async_method("t", |_, this, ()| async {
|
||||
| ___________________________________-------------_^
|
||||
| | | |
|
||||
| | | return type of closure `{async block@$DIR/tests/compile/async_any_userdata_method.rs:9:49: 9:54}` contains a lifetime `'2`
|
||||
| | lifetime `'1` represents this closure's body
|
||||
10 | | s = this;
|
||||
10 | | s = &*this;
|
||||
11 | | Ok(())
|
||||
12 | | });
|
||||
| |_________^ returning this value requires that `'1` must outlive `'2`
|
||||
@@ -28,53 +50,31 @@ error[E0597]: `s` does not live long enough
|
||||
| - binding `s` declared here
|
||||
8 | let mut s = &s;
|
||||
| ^^ borrowed value does not live long enough
|
||||
9 | / reg.add_async_method("t", |_, this: &String, ()| async {
|
||||
10 | | s = this;
|
||||
9 | / reg.add_async_method("t", |_, this, ()| async {
|
||||
10 | | s = &*this;
|
||||
11 | | Ok(())
|
||||
12 | | });
|
||||
| |__________- argument requires that `s` is borrowed for `'static`
|
||||
13 | }).unwrap();
|
||||
13 | })
|
||||
| - `s` dropped here while still borrowed
|
||||
|
||||
error[E0521]: borrowed data escapes outside of closure
|
||||
--> tests/compile/async_any_userdata_method.rs:9:9
|
||||
|
|
||||
6 | lua.register_userdata_type::<String>(|reg| {
|
||||
| ---
|
||||
| |
|
||||
| `reg` is a reference that is only valid in the closure body
|
||||
| has type `&mut LuaUserDataRegistry<'1, std::string::String>`
|
||||
...
|
||||
9 | / reg.add_async_method("t", |_, this: &String, ()| async {
|
||||
10 | | s = this;
|
||||
11 | | Ok(())
|
||||
12 | | });
|
||||
| | ^
|
||||
| | |
|
||||
| |__________`reg` escapes the closure body here
|
||||
| argument requires that `'1` must outlive `'static`
|
||||
|
|
||||
= note: requirement occurs because of a mutable reference to `LuaUserDataRegistry<'_, std::string::String>`
|
||||
= note: mutable references are invariant over their type parameter
|
||||
= help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance
|
||||
|
||||
error[E0373]: closure may outlive the current function, but it borrows `s`, which is owned by the current function
|
||||
--> tests/compile/async_any_userdata_method.rs:9:35
|
||||
|
|
||||
9 | reg.add_async_method("t", |_, this: &String, ()| async {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^ may outlive borrowed value `s`
|
||||
10 | s = this;
|
||||
9 | reg.add_async_method("t", |_, this, ()| async {
|
||||
| ^^^^^^^^^^^^^ may outlive borrowed value `s`
|
||||
10 | s = &*this;
|
||||
| - `s` is borrowed here
|
||||
|
|
||||
note: function requires argument type to outlive `'static`
|
||||
--> tests/compile/async_any_userdata_method.rs:9:9
|
||||
|
|
||||
9 | / reg.add_async_method("t", |_, this: &String, ()| async {
|
||||
10 | | s = this;
|
||||
9 | / reg.add_async_method("t", |_, this, ()| async {
|
||||
10 | | s = &*this;
|
||||
11 | | Ok(())
|
||||
12 | | });
|
||||
| |__________^
|
||||
help: to force the closure to take ownership of `s` (and any other referenced variables), use the `move` keyword
|
||||
|
|
||||
9 | reg.add_async_method("t", move |_, this: &String, ()| async {
|
||||
9 | reg.add_async_method("t", move |_, this, ()| async {
|
||||
| ++++
|
||||
|
||||
@@ -4,8 +4,8 @@ fn main() {
|
||||
#[derive(Clone)]
|
||||
struct MyUserData<'a>(&'a i64);
|
||||
|
||||
impl<'a> UserData for MyUserData<'a> {
|
||||
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
|
||||
impl UserData for MyUserData<'_> {
|
||||
fn add_methods<M: UserDataMethods<Self>>(methods: &mut M) {
|
||||
methods.add_async_method("print", |_, data, ()| async move {
|
||||
println!("{}", data.0);
|
||||
Ok(())
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
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) {
|
||||
7 | impl UserData for MyUserData<'_> {
|
||||
| -- lifetime `'1` appears in the `impl`'s self type
|
||||
8 | fn add_methods<M: UserDataMethods<Self>>(methods: &mut M) {
|
||||
9 | / methods.add_async_method("print", |_, data, ()| async move {
|
||||
10 | | println!("{}", data.0);
|
||||
11 | | Ok(())
|
||||
12 | | });
|
||||
| |______________^ requires that `'a` must outlive `'static`
|
||||
| |______________^ requires that `'1` must outlive `'static`
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
use mlua::{UserData, UserDataMethods};
|
||||
|
||||
struct MyUserData;
|
||||
|
||||
impl UserData for MyUserData {
|
||||
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
|
||||
methods.add_async_method("method", |_, this: &'static Self, ()| async {
|
||||
Ok(())
|
||||
});
|
||||
// ^ lifetime may not live long enough
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
@@ -1,17 +0,0 @@
|
||||
warning: unused variable: `this`
|
||||
--> tests/compile/async_userdata_method.rs:7:48
|
||||
|
|
||||
7 | methods.add_async_method("method", |_, this: &'static Self, ()| async {
|
||||
| ^^^^ help: if this is intentional, prefix it with an underscore: `_this`
|
||||
|
|
||||
= note: `#[warn(unused_variables)]` on by default
|
||||
|
||||
error: lifetime may not live long enough
|
||||
--> tests/compile/async_userdata_method.rs:7:9
|
||||
|
|
||||
6 | fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
|
||||
| ---- lifetime `'lua` defined here
|
||||
7 | / methods.add_async_method("method", |_, this: &'static Self, ()| async {
|
||||
8 | | Ok(())
|
||||
9 | | });
|
||||
| |__________^ argument requires that `'lua` must outlive `'static`
|
||||
@@ -6,7 +6,5 @@ fn main() {
|
||||
let test = Test(0);
|
||||
|
||||
let lua = Lua::new();
|
||||
let _ = lua.create_function(|_, ()| -> Result<i32> {
|
||||
Ok(test.0)
|
||||
});
|
||||
let _ = lua.create_function(|_, ()| -> Result<i32> { Ok(test.0) });
|
||||
}
|
||||
|
||||
@@ -1,20 +1,17 @@
|
||||
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.0`
|
||||
10 | Ok(test.0)
|
||||
| ------ `test.0` is borrowed here
|
||||
|
|
||||
--> tests/compile/function_borrow.rs:9:33
|
||||
|
|
||||
9 | let _ = lua.create_function(|_, ()| -> Result<i32> { Ok(test.0) });
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^ ------ `test.0` is borrowed here
|
||||
| |
|
||||
| may outlive borrowed value `test.0`
|
||||
|
|
||||
note: function requires argument type to outlive `'static`
|
||||
--> tests/compile/function_borrow.rs:9:13
|
||||
|
|
||||
9 | let _ = lua.create_function(|_, ()| -> Result<i32> {
|
||||
| _____________^
|
||||
10 | | Ok(test.0)
|
||||
11 | | });
|
||||
| |______^
|
||||
--> tests/compile/function_borrow.rs:9:13
|
||||
|
|
||||
9 | let _ = lua.create_function(|_, ()| -> Result<i32> { Ok(test.0) });
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
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> {
|
||||
| ++++
|
||||
|
|
||||
9 | let _ = lua.create_function(move |_, ()| -> Result<i32> { Ok(test.0) });
|
||||
| ++++
|
||||
|
||||
@@ -6,7 +6,7 @@ error[E0277]: the type `UnsafeCell<*mut lua_State>` may contain interior mutabil
|
||||
| |
|
||||
| required by a bound introduced by this call
|
||||
|
|
||||
= help: within `mlua::types::sync::inner::ReentrantMutex<mlua::state::raw::RawLua>`, the trait `RefUnwindSafe` is not implemented for `UnsafeCell<*mut lua_State>`, which is required by `{closure@$DIR/tests/compile/lua_norefunwindsafe.rs:7:18: 7:20}: UnwindSafe`
|
||||
= help: within `mlua::types::sync::inner::ReentrantMutex<mlua::state::raw::RawLua>`, the trait `RefUnwindSafe` is not implemented for `UnsafeCell<*mut lua_State>`
|
||||
note: required because it appears within the type `Cell<*mut lua_State>`
|
||||
--> $RUST/core/src/cell.rs
|
||||
|
|
||||
@@ -26,7 +26,7 @@ note: required because it appears within the type `mlua::types::sync::inner::Ree
|
||||
note: required because it appears within the type `Lua`
|
||||
--> src/state.rs
|
||||
|
|
||||
| pub struct Lua(XRc<ReentrantMutex<RawLua>>);
|
||||
| pub struct Lua {
|
||||
| ^^^
|
||||
= note: required for `&Lua` to implement `UnwindSafe`
|
||||
note: required because it's used within this closure
|
||||
@@ -48,7 +48,7 @@ error[E0277]: the type `UnsafeCell<mlua::state::extra::ExtraData>` may contain i
|
||||
| |
|
||||
| required by a bound introduced by this call
|
||||
|
|
||||
= help: the trait `RefUnwindSafe` is not implemented for `UnsafeCell<mlua::state::extra::ExtraData>`, which is required by `{closure@$DIR/tests/compile/lua_norefunwindsafe.rs:7:18: 7:20}: UnwindSafe`
|
||||
= help: the trait `RefUnwindSafe` is not implemented for `UnsafeCell<mlua::state::extra::ExtraData>`
|
||||
= note: required for `Rc<UnsafeCell<mlua::state::extra::ExtraData>>` to implement `RefUnwindSafe`
|
||||
note: required because it appears within the type `mlua::state::raw::RawLua`
|
||||
--> src/state/raw.rs
|
||||
@@ -64,7 +64,7 @@ note: required because it appears within the type `mlua::types::sync::inner::Ree
|
||||
note: required because it appears within the type `Lua`
|
||||
--> src/state.rs
|
||||
|
|
||||
| pub struct Lua(XRc<ReentrantMutex<RawLua>>);
|
||||
| pub struct Lua {
|
||||
| ^^^
|
||||
= note: required for `&Lua` to implement `UnwindSafe`
|
||||
note: required because it's used within this closure
|
||||
|
||||
@@ -8,10 +8,8 @@ fn main() -> Result<()> {
|
||||
|
||||
let data = Rc::new(Cell::new(0));
|
||||
|
||||
lua.create_function(move |_, ()| {
|
||||
Ok(data.get())
|
||||
})?
|
||||
.call::<i32>(())?;
|
||||
lua.create_function(move |_, ()| Ok(data.get()))?
|
||||
.call::<i32>(())?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user